/
Asset.swift
166 lines (131 loc) · 5.86 KB
/
Asset.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//
// Asset.swift
// Contentful
//
// Created by Boris Bügling on 18/08/15.
// Copyright © 2015 Contentful GmbH. All rights reserved.
//
import Foundation
internal extension String {
/**
Will make a `URL` from the current `String` instance if possible.
*/
internal func url() throws -> URL {
guard var urlComponents = URLComponents(string: self) else {
throw SDKError.invalidURL(string: self)
}
// Append https scheme if not present.
if urlComponents.scheme == nil {
urlComponents.scheme = "https"
}
guard let url = urlComponents.url else {
throw SDKError.invalidURL(string: self)
}
return url
}
}
/// A simple protocol to bridge `Contentful.Asset` and other formats for storing asset information.
public protocol AssetProtocol {
/// The identifier of the asset.
var id: String { get }
/// String representation for the URL of the media file associated with this asset.
var urlString: String? { get }
}
/// An asset represents a media file in Contentful.
public class Asset: LocalizableResource, AssetProtocol {
/// The key paths for member fields of an Asset
public enum Fields: String, CodingKey {
/// Title description and file keys.
case title, description, file
}
/// The URL for the underlying media file. Returns nil if the url was omitted from the response (i.e. `select` operation in query)
/// or if the underlying media file is still processing with Contentful.
public var url: URL? {
guard let url = file?.url else { return nil }
return url
}
/// String representation for the URL of the media file associated with this asset. Optional for compatibility with `select` operator queries.
/// Also, If the media file is still being processed, as the final stage of uploading to your space, this property will be nil.
public var urlString: String? {
guard let urlString = url?.absoluteString else { return nil }
return urlString
}
/// The title of the asset. Optional for compatibility with `select` operator queries.
public var title: String? {
return fields["title"] as? String
}
/// Description of the asset. Optional for compatibility with `select` operator queries.
public var description: String? {
return fields["description"] as? String
}
/// Metadata describing the file associated with the asset. Optional for compatibility with `select` operator queries.
public var file: FileMetadata? {
return fields["file"] as? FileMetadata
}
}
extension Asset {
/// Metadata describing underlying media file.
public struct FileMetadata: Decodable {
/// Original filename of the file.
public let fileName: String
/// Content type of the file.
public let contentType: String
/// Details of the file, depending on it's MIME type.
public let details: Details?
/// The remote URL for the binary data for this Asset.
/// If the media file is still being processed, as the final stage of uploading to your space, this property will be nil.
public let url: URL?
/// The size and dimensions of the underlying media file if it is an image.
public struct Details: Decodable {
/// The size of the file in bytes.
public let size: Int
/// Additional information describing the image the asset references.
public let imageInfo: ImageInfo?
/// A lightweight struct to hold the dimensions information for the this file, if it is an image type.
public struct ImageInfo: Decodable {
/// The width of the image.
public let width: Double
/// The height of the image.
public let height: Double
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
width = try container.decode(Double.self, forKey: .width)
height = try container.decode(Double.self, forKey: .height)
}
private enum CodingKeys: String, CodingKey {
case width, height
}
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
size = try container.decode(Int.self, forKey: .size)
imageInfo = try container.decodeIfPresent(ImageInfo.self, forKey: .image)
}
private enum CodingKeys: String, CodingKey {
case size, image
}
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
fileName = try container.decode(String.self, forKey: .fileName)
contentType = try container.decode(String.self, forKey: .contentType)
details = try container.decode(Details.self, forKey: .details)
// Decodable handles URL's automatically but we need to prepend the https protocol.
let urlString = try container.decode(String.self, forKey: .url)
guard let url = URL(string: "https:" + urlString) else {
throw SDKError.invalidURL(string: "Asset had urlString incapable of being made into a Foundation.URL object \(urlString)")
}
self.url = url
}
private enum CodingKeys: String, CodingKey {
case fileName, contentType, url, details
}
}
}
extension Asset: EndpointAccessible {
static let endpoint = Endpoint.assets
}
extension Asset: ResourceQueryable {
/// The QueryType for an Asset is AssetQuery
public typealias QueryType = AssetQuery
}