diff --git a/Example/UnsplashKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/UnsplashKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Example/UnsplashKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/README.md b/README.md index a2cfeab..214ec4c 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Unsplash API client written in Swift. +This Fork updates the Official API section to swift 5.0 + [Unsplash](https://unsplash.com/) offers 2 APIs: - [Source API](https://source.unsplash.com/) (unlimited requests) - [Official API](https://unsplash.com/documentation) diff --git a/UnsplashKit.podspec b/UnsplashKit.podspec index 4bdac20..6fb6b8d 100644 --- a/UnsplashKit.podspec +++ b/UnsplashKit.podspec @@ -10,13 +10,14 @@ Swift client for unsplash.com API s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { 'Caramba.io' => 'hello@caramba.io' } s.source = { :git => 'https://github.com/carambalabs/UnsplashKit.git', :tag => s.version.to_s } + s.swift_version = '5.0' s.default_subspec = 'Source' s.subspec "Foundation" do |ss| ss.source_files = 'UnsplashKit/Classes/Foundation/**/*.swift' - ss.dependency 'Unbox', '~> 2.3' - ss.dependency 'Result', '~> 3.1' + ss.dependency 'Unbox', '~> 4.0' + ss.dependency 'Result', '~> 5.0' ss.dependency 'HTTPStatusCodes', '~> 3.1' ss.frameworks = ["CoreLocation"] end diff --git a/UnsplashKit/Classes/Foundation/Resource.swift b/UnsplashKit/Classes/Foundation/Resource.swift index b15ec45..bc04142 100644 --- a/UnsplashKit/Classes/Foundation/Resource.swift +++ b/UnsplashKit/Classes/Foundation/Resource.swift @@ -48,11 +48,11 @@ public struct Resource { internal init(request: @escaping (URLComponents) -> URLRequest, jsonParse: @escaping (Any, HTTPURLResponse) throws -> A) { self.request = request - self.parse = { input -> A in - guard let json = try JSONSerialization.jsonObject(with: input.0, options: []) as? [String: Any] else { + self.parse = { data,response -> A in + guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { throw ResourceError.invalidData } - return try jsonParse(json, input.1) + return try jsonParse(json, response) } } diff --git a/UnsplashKit/Classes/Foundation/WebLinking.swift b/UnsplashKit/Classes/Foundation/WebLinking.swift index 1cb6bf8..02d835b 100644 --- a/UnsplashKit/Classes/Foundation/WebLinking.swift +++ b/UnsplashKit/Classes/Foundation/WebLinking.swift @@ -14,9 +14,8 @@ public struct Link: Equatable, Hashable { self.parameters = parameters ?? [:] } - /// Returns the hash value - public var hashValue: Int { - return uri.hashValue + public func hash(into hasher: inout Hasher) { + hasher.combine(uri) } /// Relation type of the Link. @@ -170,9 +169,9 @@ func split(_ separator: String) -> (String) -> (String, String) { let range = input.range(of: separator, options: NSString.CompareOptions(rawValue: 0), range: nil, locale: nil) if let range = range { - let lhs = input.substring(to: range.lowerBound) - let rhs = input.substring(from: range.upperBound) - return (lhs, rhs) + let lhs = input[.. (String, ArraySlice) { func trim(_ lhs: Character, _ rhs: Character) -> (String) -> String { return { input in if input.hasPrefix("\(lhs)") && input.hasSuffix("\(rhs)") { - return input[input.characters.index(after: input.startIndex).. Resource<[Collection]> { var queryItems: [URLQueryItem] = [] queryItems.append(URLQueryItem(name: "page", value: "\(page)")) @@ -90,7 +90,7 @@ public extension Collection { /// - page: page to be fetched. /// - perPage: number of items to be fetched. /// - Returns: resource for fetching the featured collections. - public static func featured(page: Int = 1, + static func featured(page: Int = 1, perPage: Int = 10) -> Resource<[Collection]> { var queryItems: [URLQueryItem] = [] queryItems.append(URLQueryItem(name: "page", value: "\(page)")) @@ -111,7 +111,7 @@ public extension Collection { /// - page: page to be fetched. /// - perPage: number of items to be fetched. /// - Returns: resource for fetching curated collections. - public static func curated(page: Int = 1, + static func curated(page: Int = 1, perPage: Int = 10) -> Resource<[Collection]> { var queryItems: [URLQueryItem] = [] queryItems.append(URLQueryItem(name: "page", value: "\(page)")) @@ -130,7 +130,7 @@ public extension Collection { /// /// - Parameter id: collection identifier. /// - Returns: resource for fetching the collection. - public static func get(id: String) -> Resource { + static func get(id: String) -> Resource { let queryItems: [URLQueryItem] = [] return Resource { (components: URLComponents) -> URLRequest in var mutable: URLComponents = components @@ -149,7 +149,7 @@ public extension Collection { /// - page: page to be fetched. /// - perPage: items per page. /// - Returns: resource for fetching the photos. - public static func photos(id: String, + static func photos(id: String, page: Int = 1, perPage: Int = 10) -> Resource<[Photo]> { var queryItems: [URLQueryItem] = [] @@ -172,7 +172,7 @@ public extension Collection { /// - page: page to be fetched. /// - perPage: number of items per page. /// - Returns: resource for fetching the photos. - public static func curatedPhotos(id: String, + static func curatedPhotos(id: String, page: Int = 1, perPage: Int = 10) -> Resource<[Photo]> { var queryItems: [URLQueryItem] = [] @@ -192,7 +192,7 @@ public extension Collection { /// /// - Parameter id: collection identifier whose related ones will be fetched. /// - Returns: resource for fetching the related collections. - public static func related(id: String) -> Resource<[Collection]> { + static func related(id: String) -> Resource<[Collection]> { let queryItems: [URLQueryItem] = [] return Resource { (components: URLComponents) -> URLRequest in var mutable: URLComponents = components @@ -211,7 +211,7 @@ public extension Collection { /// - description: collection title. /// - isPrivate: collection private value. /// - Returns: resource for creating the collection. - public static func create(title: String, + static func create(title: String, description: String? = nil, isPrivate: Bool? = nil) -> Resource { var queryItems: [URLQueryItem] = [] @@ -240,7 +240,7 @@ public extension Collection { /// - description: new description. /// - isPrivate: new private value. /// - Returns: resource for updating theh collection. - public static func update(id: String, + static func update(id: String, title: String? = nil, description: String? = nil, isPrivate: Bool? = nil) -> Resource { @@ -268,7 +268,7 @@ public extension Collection { /// /// - Parameter id: collection identifier. /// - Returns: resource for deleting the collection. - public static func delete(id: String) -> Resource { + static func delete(id: String) -> Resource { let queryItems: [URLQueryItem] = [] return Resource(request: { (components) -> URLRequest in var mutable: URLComponents = components @@ -288,7 +288,7 @@ public extension Collection { /// - id: photo identifier. /// - collection: collection identifier. /// - Returns: resource for adding the photo. - public static func addPhoto(with id: String, + static func addPhoto(with id: String, to collection: String) -> Resource { var queryItems: [URLQueryItem] = [] queryItems.append(URLQueryItem(name: "photo_id", value: "\(id)")) @@ -310,7 +310,7 @@ public extension Collection { /// - id: photo identifier. /// - collection: collection identifier. /// - Returns: resource for deleting the photo. - public static func deletePhoto(with id: String, + static func deletePhoto(with id: String, from collection: String) -> Resource { var queryItems: [URLQueryItem] = [] queryItems.append(URLQueryItem(name: "photo_id", value: "\(id)")) diff --git a/UnsplashKit/Classes/UnsplashAPI/Models/Photo.swift b/UnsplashKit/Classes/UnsplashAPI/Models/Photo.swift index 7d05bc9..9315e08 100644 --- a/UnsplashKit/Classes/UnsplashAPI/Models/Photo.swift +++ b/UnsplashKit/Classes/UnsplashAPI/Models/Photo.swift @@ -41,6 +41,12 @@ public struct Photo: Unboxable { /// Photo current user collections. public let currentUserCollections: [Collection]? + /// Photo description + public let description: String? + + /// Photo alternative description + public let altDescription: String? + // MARK: - Unboxable /// Initialize an instance of this model by unboxing a dictionary using an Unboxer @@ -51,11 +57,13 @@ public struct Photo: Unboxable { self.height = try unboxer.unbox(key: "height") self.color = try unboxer.unbox(key: "color") self.likes = try unboxer.unbox(key: "likes") - self.urls = unboxer.unbox(key: "urls") - self.links = unboxer.unbox(key: "links") - self.categories = unboxer.unbox(key: "categories") - self.user = unboxer.unbox(key: "user") - self.currentUserCollections = unboxer.unbox(key: "current_user_collections") + self.urls = try? unboxer.unbox(key: "urls") + self.links = try? unboxer.unbox(key: "links") + self.categories = try? unboxer.unbox(key: "categories") + self.user = try? unboxer.unbox(key: "user") + self.currentUserCollections = try? unboxer.unbox(key: "current_user_collections") + self.description = try? unboxer.unbox(key: "description") + self.altDescription = try? unboxer.unbox(key: "alt_description") } } @@ -72,7 +80,7 @@ public extension Photo { /// - perPage: number of items per page. /// - orderBy: order by value. /// - Returns: resource for fetching photos. - public static func list(page: Int = 1, + static func list(page: Int = 1, perPage: Int = 10, orderBy: Order = .latest) -> Resource<[Photo]> { var queryItems: [URLQueryItem] = [] @@ -96,7 +104,7 @@ public extension Photo { /// - perPage: number of items per page. /// - orderBy: order by value. /// - Returns: resource for fetching the curated photos. - public static func curated(page: Int = 1, + static func curated(page: Int = 1, perPage: Int = 10, orderBy: Order = .latest) -> Resource<[Photo]> { var queryItems: [URLQueryItem] = [] @@ -120,7 +128,7 @@ public extension Photo { /// - size: photo size. /// - rect: photo rect. /// - Returns: resource for fetching a photo. - public static func get(id: String, + static func get(id: String, size: CGSize? = nil, rect: CGRect? = nil) -> Resource { var queryItems: [URLQueryItem] = [] @@ -152,7 +160,7 @@ public extension Photo { /// - size: size of the photos. /// - orientation: orientation filtering. /// - Returns: resource for fetching the photos. - public static func random(categories: [String] = [], + static func random(categories: [String] = [], collections: [String] = [], featured: Bool? = nil, username: String? = nil, @@ -195,7 +203,7 @@ public extension Photo { /// - size: size of the photos. /// - orientation: orientation used for filtering. /// - Returns: resource for fetching the photos. - public static func random(count: Int, + static func random(count: Int, categories: [String] = [], collections: [String] = [], featured: Bool? = nil, @@ -233,7 +241,7 @@ public extension Photo { /// /// - Parameter id: photo identifier. /// - Returns: resource for fetching the stats. - public static func stats(id: String) -> Resource { + static func stats(id: String) -> Resource { return Resource { (components: URLComponents) -> URLRequest in var mutable: URLComponents = components mutable.path = "/photos/\(id)/stats" @@ -247,7 +255,7 @@ public extension Photo { /// /// - Parameter id: photo identifier. /// - Returns: resource for fetching the photo download link. - public static func downloadLink(id: String) -> Resource { + static func downloadLink(id: String) -> Resource { return Resource(request: { (components) -> URLRequest in var mutable: URLComponents = components mutable.path = "/photos/\(id)/download" @@ -275,7 +283,7 @@ public extension Photo { /// - exifFocalLength: photo new exif focal length. /// - exifIsoSpeedRatings: photo new exif iso speed ratings. /// - Returns: resource for updating the photo. - public static func update(id: String, + static func update(id: String, locationPosition: CLLocation? = nil, locationName: String? = nil, locationCity: String? = nil, @@ -336,7 +344,7 @@ public extension Photo { /// /// - Parameter id: photo to be liked. /// - Returns: resource for liking the photo. - public static func like(id: String) -> Resource { + static func like(id: String) -> Resource { return Resource(request: { (components) -> URLRequest in var mutable: URLComponents = components mutable.path = "/photos/\(id)/like" @@ -352,7 +360,7 @@ public extension Photo { /// /// - Parameter id: photo to be unliked. /// - Returns: resource for unliking the photo. - public static func unlike(id: String) -> Resource { + static func unlike(id: String) -> Resource { return Resource(request: { (components) -> URLRequest in var mutable: URLComponents = components mutable.path = "/photos/\(id)/like" diff --git a/UnsplashKit/Classes/UnsplashAPI/Models/PhotoCategory.swift b/UnsplashKit/Classes/UnsplashAPI/Models/PhotoCategory.swift index 3418f31..9a2b85c 100644 --- a/UnsplashKit/Classes/UnsplashAPI/Models/PhotoCategory.swift +++ b/UnsplashKit/Classes/UnsplashAPI/Models/PhotoCategory.swift @@ -21,7 +21,7 @@ public struct PhotoCategory: Unboxable { public init(unboxer: Unboxer) throws { self.id = try unboxer.unbox(key: "id") self.title = try unboxer.unbox(key: "title") - self.links = unboxer.unbox(key: "links") + self.links = try? unboxer.unbox(key: "links") } } diff --git a/UnsplashKit/Classes/UnsplashAPI/Models/PhotoExif.swift b/UnsplashKit/Classes/UnsplashAPI/Models/PhotoExif.swift index 3ba993d..7435899 100644 --- a/UnsplashKit/Classes/UnsplashAPI/Models/PhotoExif.swift +++ b/UnsplashKit/Classes/UnsplashAPI/Models/PhotoExif.swift @@ -28,11 +28,11 @@ public struct PhotoExif: Unboxable { /// Initialize an instance of this model by unboxing a dictionary using an Unboxer public init(unboxer: Unboxer) throws { - self.make = unboxer.unbox(key: "make") - self.model = unboxer.unbox(key: "model") - self.exposureTime = unboxer.unbox(key: "exposure_time") - self.aperture = unboxer.unbox(key: "aperture") - self.focalLength = unboxer.unbox(key: "focal_length") - self.iso = unboxer.unbox(key: "iso") + self.make = try? unboxer.unbox(key: "make") + self.model = try? unboxer.unbox(key: "model") + self.exposureTime = try? unboxer.unbox(key: "exposure_time") + self.aperture = try? unboxer.unbox(key: "aperture") + self.focalLength = try? unboxer.unbox(key: "focal_length") + self.iso = try? unboxer.unbox(key: "iso") } } diff --git a/UnsplashKit/Classes/UnsplashAPI/Models/PhotoLocation.swift b/UnsplashKit/Classes/UnsplashAPI/Models/PhotoLocation.swift index 51c19e3..9adcc8c 100644 --- a/UnsplashKit/Classes/UnsplashAPI/Models/PhotoLocation.swift +++ b/UnsplashKit/Classes/UnsplashAPI/Models/PhotoLocation.swift @@ -23,11 +23,11 @@ public struct PhotoLocation: Unboxable { /// Initialize an instance of this model by unboxing a dictionary using an Unboxer public init(unboxer: Unboxer) throws { - self.name = unboxer.unbox(key: "name") - self.city = unboxer.unbox(key: "city") - self.country = unboxer.unbox(key: "country") - if let latitude: Double = unboxer.unbox(key: "position.latitude"), - let longitude: Double = unboxer.unbox(key: "position.longitude") { + self.name = try? unboxer.unbox(key: "name") + self.city = try? unboxer.unbox(key: "city") + self.country = try? unboxer.unbox(key: "country") + if let latitude: Double = try? unboxer.unbox(key: "position.latitude"), + let longitude: Double = try? unboxer.unbox(key: "position.longitude") { self.location = CLLocation(latitude: latitude, longitude: longitude) } else { self.location = nil diff --git a/UnsplashKit/Classes/UnsplashAPI/Models/Search.swift b/UnsplashKit/Classes/UnsplashAPI/Models/Search.swift index 3f06590..7169174 100644 --- a/UnsplashKit/Classes/UnsplashAPI/Models/Search.swift +++ b/UnsplashKit/Classes/UnsplashAPI/Models/Search.swift @@ -1,6 +1,10 @@ import Foundation import Unbox +public enum SearchOrder: String { + case latest, relevant +} + /// Resources public struct Search { @@ -13,11 +17,13 @@ public struct Search { /// - Returns: resource for searching photos. public static func photos(query: String, page: Int = 1, - perPage: Int = 10) -> Resource<[Photo]> { + perPage: Int = 10, + orderBy: SearchOrder = .relevant ) -> Resource<[Photo]> { var queryItems: [URLQueryItem] = [] queryItems.append(URLQueryItem(name: "query", value: query)) queryItems.append(URLQueryItem(name: "page", value: "\(page)")) queryItems.append(URLQueryItem(name: "per_page", value: "\(perPage)")) + queryItems.append(URLQueryItem(name: "order_by", value: orderBy.rawValue)) return Resource(request: { (components) -> URLRequest in var mutable: URLComponents = components mutable.path = "/search/photos" diff --git a/UnsplashKit/Classes/UnsplashAPI/Models/User.swift b/UnsplashKit/Classes/UnsplashAPI/Models/User.swift index 90614cd..2bd5ab7 100644 --- a/UnsplashKit/Classes/UnsplashAPI/Models/User.swift +++ b/UnsplashKit/Classes/UnsplashAPI/Models/User.swift @@ -63,22 +63,22 @@ public struct User: Unboxable { /// Initialize an instance of this model by unboxing a dictionary using an Unboxer public init(unboxer: Unboxer) throws { self.id = try unboxer.unbox(key: "id") - self.name = unboxer.unbox(key: "name") + self.name = try? unboxer.unbox(key: "name") self.username = try unboxer.unbox(key: "username") self.firstName = try unboxer.unbox(key: "first_name") - self.lastName = unboxer.unbox(key: "last_name") - self.portfolioUrl = unboxer.unbox(key: "portfolio_url") - self.bio = unboxer.unbox(key: "bio") - self.location = unboxer.unbox(key: "location") + self.lastName = try? unboxer.unbox(key: "last_name") + self.portfolioUrl = try? unboxer.unbox(key: "portfolio_url") + self.bio = try? unboxer.unbox(key: "bio") + self.location = try? unboxer.unbox(key: "location") self.totalLikes = try unboxer.unbox(key: "total_likes") self.totalPhotos = try unboxer.unbox(key: "total_photos") self.totalCollections = try unboxer.unbox(key: "total_collections") - self.followedByUser = unboxer.unbox(key: "followed_by_user") - self.downloads = unboxer.unbox(key: "downloads") - self.uploadsRemaining = unboxer.unbox(key: "uploads_remaining") - self.instagramUsername = unboxer.unbox(key: "instagram_username") - self.email = unboxer.unbox(key: "email") - self.links = unboxer.unbox(key: "links") + self.followedByUser = try? unboxer.unbox(key: "followed_by_user") + self.downloads = try? unboxer.unbox(key: "downloads") + self.uploadsRemaining = try? unboxer.unbox(key: "uploads_remaining") + self.instagramUsername = try? unboxer.unbox(key: "instagram_username") + self.email = try? unboxer.unbox(key: "email") + self.links = try? unboxer.unbox(key: "links") } } @@ -88,7 +88,7 @@ public struct User: Unboxable { public extension User { /// Returns the resource to fetch the authenticated user. - public static var me: Resource = Resource { (components: URLComponents) -> URLRequest in + static var me: Resource = Resource { (components: URLComponents) -> URLRequest in var mutable: URLComponents = components mutable.path = "/me" return URLRequest(url: mutable.url!) @@ -106,7 +106,7 @@ public extension User { /// - bio: user new biography. /// - instagramUsername: user new instagram username. /// - Returns: resource for updating the user. - public static func updateMe(username: String? = nil, + static func updateMe(username: String? = nil, firstName: String? = nil, lastName: String? = nil, email: String? = nil, @@ -139,7 +139,7 @@ public extension User { /// - username: user username. /// - size: profile photo size. /// - Returns: resource for fetching an user. - public static func get(username: String, + static func get(username: String, size: CGSize? = nil) -> Resource { var queryItems: [URLQueryItem] = [] if let size = size { @@ -160,7 +160,7 @@ public extension User { /// /// - Parameter username: user username. /// - Returns: resource for fetching the portfolio url. - public static func portfolio(username: String) -> Resource { + static func portfolio(username: String) -> Resource { return Resource(request: { (components) -> URLRequest in var mutable: URLComponents = components mutable.path = "/users/\(username)/portfolio" @@ -180,7 +180,7 @@ public extension User { /// - perPage: number of items per page. /// - orderBy: order by value. /// - Returns: resource for fetching the user's photos. - public static func photos(username: String, + static func photos(username: String, page: Int = 1, perPage: Int = 10, orderBy: Order = .latest) -> Resource<[Photo]> { @@ -206,7 +206,7 @@ public extension User { /// - perPage: number of items per page. /// - orderBy: order by value. /// - Returns: resource for fetching the liked photos. - public static func likes(username: String, + static func likes(username: String, page: Int = 1, perPage: Int = 10, orderBy: Order = .latest) -> Resource<[Photo]> { @@ -232,7 +232,7 @@ public extension User { /// - perPage: number of items per page. /// - orderBy: order by value. /// - Returns: resource for fetching the collections. - public static func collections(username: String, + static func collections(username: String, page: Int = 1, perPage: Int = 10, orderBy: Order = .latest) -> Resource<[Collection]> { diff --git a/UnsplashKit/Classes/UnsplashAPI/Models/UserLinks.swift b/UnsplashKit/Classes/UnsplashAPI/Models/UserLinks.swift index aafb6a9..e431695 100644 --- a/UnsplashKit/Classes/UnsplashAPI/Models/UserLinks.swift +++ b/UnsplashKit/Classes/UnsplashAPI/Models/UserLinks.swift @@ -36,8 +36,8 @@ public struct UserLinks: Unboxable { self.photos = try unboxer.unbox(key: "photos") self.likes = try unboxer.unbox(key: "likes") self.portfolio = try unboxer.unbox(key: "portfolio") - self.followers = unboxer.unbox(key: "followers") - self.following = unboxer.unbox(key: "following") + self.followers = try? unboxer.unbox(key: "followers") + self.following = try? unboxer.unbox(key: "following") } }