diff --git a/README.md b/README.md index ebf91f2..16a75eb 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@

-SLazeKit is an easy to use Swift restfull collection of extensions and classes. Don't spend hours writing your code to map your rest api request into models and coredata serialization. Stop wasting your time! +SLazeKit is an easy to use Swift restful collection of extensions and classes. Don't spend hours writing your code to map your rest api request into models and coredata serialization. Stop wasting your time! **SLazeKit allows you:** - map your models by `Codable` protocol @@ -47,10 +47,19 @@ This project demonstrates a working method for using Swift Package Manager (SPM) ## 💊 Usage -For positive experience, you should configure `SLazeKit` at first. This step is optional. You may leave it as it is default. +For positive experience, you should configure `SLazeKit` at first. This step is REQUIRED! ```swift import SLazeKit +/// Required override of this method which will provide Context for bacground execution. +/// +/// - Returns: NSManagedObjectContext +open class func newBackgroundContext() -> NSManagedObjectContext? { return nil } +``` + This step is optional. You may leave it as it is default. +```swift +import SLazeKit + extension SLazeKit { //Provide base path for your API requests. open class var basePath?: String { return "www.yourdomain.com" } @@ -78,6 +87,9 @@ extension SLazeKit { ``` **Model example** +Simple +[Object.swift](Tests/SLazeKitTests/Models/Object.swift) +Advance [Model.swift](Tests/SLazeKitTests/Models/Model.swift) ## ⭐ Contributing diff --git a/SLazeKit.xcodeproj/project.pbxproj b/SLazeKit.xcodeproj/project.pbxproj index f3dbbcc..dc4fd5c 100644 --- a/SLazeKit.xcodeproj/project.pbxproj +++ b/SLazeKit.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 625B23AD1FF3462B001976C3 /* Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625B23AB1FF34625001976C3 /* Object.swift */; }; OBJ_32 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; OBJ_38 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* Model.swift */; }; OBJ_39 /* SLazeKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* SLazeKitTests.swift */; }; @@ -53,6 +54,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 625B23AB1FF34625001976C3 /* Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Object.swift; sourceTree = ""; }; OBJ_10 /* Decodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Decodable.swift; sourceTree = ""; }; OBJ_11 /* NSManagedObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSManagedObject.swift; sourceTree = ""; }; OBJ_12 /* NSManagedObjectContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSManagedObjectContext.swift; sourceTree = ""; }; @@ -119,6 +121,7 @@ isa = PBXGroup; children = ( OBJ_21 /* Model.swift */, + 625B23AB1FF34625001976C3 /* Object.swift */, ); path = Models; sourceTree = ""; @@ -132,7 +135,7 @@ name = Products; sourceTree = BUILT_PRODUCTS_DIR; }; - OBJ_5 /* */ = { + OBJ_5 = { isa = PBXGroup; children = ( OBJ_6 /* Package.swift */, @@ -140,7 +143,6 @@ OBJ_18 /* Tests */, OBJ_24 /* Products */, ); - name = ""; sourceTree = ""; }; OBJ_7 /* Sources */ = { @@ -238,7 +240,7 @@ knownRegions = ( en, ); - mainGroup = OBJ_5 /* */; + mainGroup = OBJ_5; productRefGroup = OBJ_24 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -266,6 +268,7 @@ files = ( OBJ_38 /* Model.swift in Sources */, OBJ_39 /* SLazeKitTests.swift in Sources */, + 625B23AD1FF3462B001976C3 /* Object.swift in Sources */, OBJ_40 /* StringInitializableTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Sources/SLazeKit/Extensions/Decodable.swift b/Sources/SLazeKit/Extensions/Decodable.swift index 99be0e2..8ccdfa0 100644 --- a/Sources/SLazeKit/Extensions/Decodable.swift +++ b/Sources/SLazeKit/Extensions/Decodable.swift @@ -1,63 +1,99 @@ import Foundation extension Decodable { - public static func request(path: String, method: HTTPMethod, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func request(path: String, method: HTTPMethod, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: method, queryItems: queryItems, body: body, handler: handler) } - public static func request(path: String, method: HTTPMethod, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func request(path: String, method: HTTPMethod, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: method, queryItems: queryItems, body: body, handler: handler) } - public static func request(path: String, method: HTTPMethod, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func request(path: String, method: HTTPMethod, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: method, queryItems: queryItems, body: body, handler: handler) + } + + public static func request(path: String, method: HTTPMethod, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: method, queryItems: queryItems, handler: handler) } - public static func get(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func get(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, queryItems: queryItems, body: body, handler: handler) } - public static func get(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func get(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, queryItems: queryItems, body: body, handler: handler) } - public static func get(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func get(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, queryItems: queryItems, body: body, handler: handler) + } + + public static func get(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, queryItems: queryItems, handler: handler) } - public static func post(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func post(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .POST, queryItems: queryItems, body: body, handler: handler) } - public static func post(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func post(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .POST, queryItems: queryItems, body: body, handler: handler) } - public static func post(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func post(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .POST, queryItems: queryItems, body: body, handler: handler) + } + + public static func post(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .POST, queryItems: queryItems, handler: handler) } - public static func put(path: String, queryItems: [URLQueryItem]? = nil, body: T, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func put(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .PUT, queryItems: queryItems, body: body, handler: handler) } - public static func put(path: String, queryItems: [URLQueryItem]? = nil, body: [T], handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func put(path: String, queryItems: [URLQueryItem]? = nil, body: T, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .PUT, queryItems: queryItems, body: body, handler: handler) } - public static func put(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func put(path: String, queryItems: [URLQueryItem]? = nil, body: [T], handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PUT, queryItems: queryItems, body: body, handler: handler) + } + + public static func put(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .PUT, queryItems: queryItems, handler: handler) } - public static func delete(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func patch(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PATCH, queryItems: queryItems, body: body, handler: handler) + } + + public static func patch(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PATCH, queryItems: queryItems, body: body, handler: handler) + } + + public static func patch(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PATCH, queryItems: queryItems, body: body, handler: handler) + } + + public static func patch(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PATCH, queryItems: queryItems, handler: handler) + } + + public static func delete(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .DELETE, queryItems: queryItems, body: body, handler: handler) } - public static func delete(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func delete(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .DELETE, queryItems: queryItems, body: body, handler: handler) } - public static func delete(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public static func delete(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .DELETE, queryItems: queryItems, body: body, handler: handler) + } + + public static func delete(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: Self?, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .DELETE, queryItems: queryItems, handler: handler) } } diff --git a/Sources/SLazeKit/Extensions/NSManagedObject.swift b/Sources/SLazeKit/Extensions/NSManagedObject.swift index 445d5a1..04d2f6e 100644 --- a/Sources/SLazeKit/Extensions/NSManagedObject.swift +++ b/Sources/SLazeKit/Extensions/NSManagedObject.swift @@ -2,10 +2,16 @@ import Foundation import CoreData extension NSManagedObject { + /// String entity name public class var entityName: String? { return NSStringFromClass(self).components(separatedBy: ".").last } - + /// Finds `NSManagedObject` by EntityAttribute parameters. + /// + /// - Parameters: + /// - context: Context on which fetch should be executed + /// - attributes: Entity attribute mapped by key - value + /// - Returns: Returns first object that meet the criteria specified by a given fetch request. public class func find(_ context: NSManagedObjectContext?, by attributes: EntityAttribute...) throws -> NSManagedObject? { guard let name = entityName else { return nil } let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: name) @@ -16,6 +22,12 @@ extension NSManagedObject { return try context?.fetch(fetchRequest).first } + /// Finds `NSManagedObject` by EntityAttribute parameters. + /// + /// - Parameters: + /// - context: Context on which fetch should be executed + /// - attributes: Entity attribute mapped by key - value + /// - Returns: Returns first object that meet the criteria specified by a given fetch request. public class func find(_ context: NSManagedObjectContext?, by attributes: [EntityAttribute]) throws -> NSManagedObject? { guard let name = entityName else { return nil } let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: name) diff --git a/Sources/SLazeKit/Extensions/NSManagedObjectContext.swift b/Sources/SLazeKit/Extensions/NSManagedObjectContext.swift index 7584a92..eda379a 100644 --- a/Sources/SLazeKit/Extensions/NSManagedObjectContext.swift +++ b/Sources/SLazeKit/Extensions/NSManagedObjectContext.swift @@ -2,6 +2,7 @@ import Foundation import CoreData extension NSManagedObjectContext { + /// Ifthe context has uncommitted changes, attempts to commit unsaved changes to registered objects to the context’s parent store. func commit() { if hasChanges { do { diff --git a/Sources/SLazeKit/Extensions/String.swift b/Sources/SLazeKit/Extensions/String.swift index 35143d0..f1d2ccd 100644 --- a/Sources/SLazeKit/Extensions/String.swift +++ b/Sources/SLazeKit/Extensions/String.swift @@ -1,6 +1,14 @@ import Foundation extension String { + /// Replace string patter keys with values. For example: + /// + /// in pattern `"/api/path/:id"` + /// + /// `[":id":"\(123-XYZ-321)"]` `:id` key would be replaced with `"\(123-XYZ-321)"` value. + /// + /// - Parameter arguments: Dictionary with key & value to replace. + /// - Returns: Path with values instead of keys. public func patternToPath(with arguments: [String : String] = [:]) -> String { var path = self for (key, value) in arguments { diff --git a/Sources/SLazeKit/Protocols/EntityMapping.swift b/Sources/SLazeKit/Protocols/EntityMapping.swift index 2291045..a903933 100644 --- a/Sources/SLazeKit/Protocols/EntityMapping.swift +++ b/Sources/SLazeKit/Protocols/EntityMapping.swift @@ -1,18 +1,19 @@ import Foundation import CoreData +/// Entity attribute alias for key, value tuple. public typealias EntityAttribute = (key: String, value: Any) +/// Mapping protocol. Required for request object serialization. public protocol EntityMapping { static var entityType: NSManagedObject.Type { get } var idAttributes: [EntityAttribute]? { get } func fillObject(with model: NSManagedObject) } -@available(iOS 10.0, *) extension EntityMapping { func map() throws -> NSManagedObject? { - guard let context = Self.persistentContainer?.newBackgroundContext() else { return nil } + guard let context = SLazeKit.newBackgroundContext() else { return nil } let model: NSManagedObject if let attribiutes = idAttributes { model = try Self.entityType.find(context, by: attribiutes) ?? Self.entityType.init(context: context) @@ -23,20 +24,27 @@ extension EntityMapping { context.performAndWait { context.commit() } return model } - private func findObject(_ context: NSManagedObjectContext?) throws -> NSManagedObject? { return try Self.entityType.find(context, by: idAttributes ?? []) } -} - -extension EntityMapping { + + /// Serialized managed object from datastore by given attribiutes. To be more specific. If request is returning JSON with `EntityMapping` and given Encodable object conform to this protocol. It will be automaticaly updated in DataStore. This method featch this object. + /// + /// - Parameter context: Context on which fetch should be executed + /// - Returns: Serialized object from EntityMapping model public func serialized(_ context: NSManagedObjectContext?) throws -> T? { return (try Self.entityType.find(context, by: idAttributes ?? [])) as? T } } extension Array where Element: EntityMapping { + /// Serialized managed objects from datastore by given attribiutes. To be more specific. If request is returning JSON with `EntityMapping` and given Encodable object conform to this protocol. It will be automaticaly updated in DataStore. This method featch this objects. + /// + /// - Parameter context: Context on which fetch should be executed + /// - Returns: Array of serialized object from EntityMapping response type. public func serialized(_ context: NSManagedObjectContext?) throws -> [T] { return try flatMap({ try $0.serialized(context) }) } } + + diff --git a/Sources/SLazeKit/Protocols/StringInitializable.swift b/Sources/SLazeKit/Protocols/StringInitializable.swift index 6ea685d..9b5a250 100644 --- a/Sources/SLazeKit/Protocols/StringInitializable.swift +++ b/Sources/SLazeKit/Protocols/StringInitializable.swift @@ -5,6 +5,12 @@ public protocol StringInitializable { } extension KeyedDecodingContainerProtocol { + /// If you API have unstable JSON responses for number types. You can use this helper method to decode string as a number. However, first you need extend your desired number type with `StringInitializable` protocol. + /// + /// - Parameters: + /// - type: Type to decode + /// - key: JSON key under given type should be stored + /// - Returns: Desired type value public func decodeUnstable(_ type: T.Type = T.self, forKey key: Key) throws -> T? { guard contains(key) else { return nil } guard !(try decodeNil(forKey: key)) else { return nil } @@ -19,6 +25,12 @@ extension KeyedDecodingContainerProtocol { } return try decode(T.self, forKey: key) } + /// If you API have unstable JSON responses for number types. You can use this helper method to decode string as a number. However, first you need extend your desired number type with `StringInitializable` protocol. + /// + /// - Parameters: + /// - type: Type to decode + /// - key: JSON key under given type should be stored + /// - Returns: Desired type value public func decodeUnstable(_ type: T.Type = T.self, forKey key: Key) throws -> T { if let string = try? decode(String.self, forKey: key) { guard let value = T.init(rawValue: string) else { diff --git a/Sources/SLazeKit/SLazeKit.swift b/Sources/SLazeKit/SLazeKit.swift index 0ecffdf..a9a6014 100644 --- a/Sources/SLazeKit/SLazeKit.swift +++ b/Sources/SLazeKit/SLazeKit.swift @@ -4,60 +4,79 @@ import CoreData public typealias EntityMappingCodable = EntityMapping & Codable public typealias EntityMappingDecodable = EntityMapping & Decodable +/// NetworkResponse tuple holding response `Data` and `HTTPURLResponse` +public typealias NetworkResponse = (data: Data?, urlResponse: HTTPURLResponse?) + +/// HTTPMethod types public enum HTTPMethod { case GET, POST, PUT, PATCH, DELETE, COPY, HEAD, OPTIONS, LINK, UNLINK, PURGE, LOCK, UNLOCK, PROPFIND, VIEW } -@available(iOS 10.0, *) -extension EntityMapping { - public static var persistentContainer: NSPersistentContainer? { return nil } -} - +/// SLazeKit is an easy to use restful collection of extensions and classes. Maps your rest api request into models and provides coredata serialization. public class SLazeKit { + /// Provide base path for your API requests. open class var basePath: String? { return nil } + /// Additional you can set port for your requests. open class var basePort: Int? { return nil } + /// Optional provider for JSONDecoder instance. open class var decoder: JSONDecoder { return JSONDecoder() } + /// Optional provider for JSONDecoder instance. open class var urlSession: URLSession { return URLSession.shared } + /// Global outgoing `URLRequest` customization. Called everytime request is created before executed. + /// + /// - Parameter request: `URLRequest` object to setup + /// - Returns: already setup and customize URLRequest object open class func setup(_ request: URLRequest) -> URLRequest { return request } open class func handle(_ response: HTTPURLResponse?) {} -} - -//MARK: network tasks implementation -extension SLazeKit { - class func networkTask(request: URLRequest, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + + /// Required override of this method which will provide Context for bacground execution. + /// + /// - Returns: NSManagedObjectContext + open class func newBackgroundContext() -> NSManagedObjectContext? { return nil } + + class func networkTask(request: URLRequest, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { let task = urlSession.dataTask(with: setup(request)) { (data, response, error) in handle(response as? HTTPURLResponse) - handler(response as? HTTPURLResponse, error) + handler((data, response as? HTTPURLResponse), error) } task.resume() return task } - class func networkTask(request: URLRequest, handler: @escaping (_ response: HTTPURLResponse?, _ result: T?, _ error: Error?) -> ()) -> URLSessionDataTask? { + class func networkTask(request: URLRequest, handler: @escaping (_ response: NetworkResponse, _ result: T?, _ error: Error?) -> ()) -> URLSessionDataTask? { let task = urlSession.dataTask(with: setup(request)) { (data, response, error) in handle(response as? HTTPURLResponse) if let data = data, error == nil { do { let object = try decoder.decode(T.self, from: data) - if #available(iOS 10.0, *) { - try synchronize(object) - } - handler(response as? HTTPURLResponse, object, nil) + try synchronize(object) + handler((data, response as? HTTPURLResponse), object, nil) } catch { - handler(response as? HTTPURLResponse, nil, error) + handler((data, response as? HTTPURLResponse), nil, error) } } else { - handler(response as? HTTPURLResponse, nil, error) + handler((data, response as? HTTPURLResponse), nil, error) } } task.resume() return task } - class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: B, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: String, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { guard let url = components(for: path, queryItems: queryItems)?.url else { - handler(nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) + handler((nil,nil), NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) + return nil + } + + var request = urlRequest(url, method: method) + request.httpBody = body.data(using: .utf8) + return networkTask(request: request, handler: handler) + } + + class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: B, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + guard let url = components(for: path, queryItems: queryItems)?.url else { + handler((nil,nil), NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) return nil } @@ -65,16 +84,28 @@ extension SLazeKit { do { request.httpBody = try JSONEncoder().encode(body) } catch { - handler(nil, error) + handler((nil,nil), error) return nil } return networkTask(request: request, handler: handler) } - class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: B, handler: @escaping (_ response: HTTPURLResponse?, _ result: T?, _ error: Error?) -> ()) -> URLSessionDataTask? { + class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: String, handler: @escaping (_ response: NetworkResponse, _ result: T?, _ error: Error?) -> ()) -> URLSessionDataTask? { guard let url = components(for: path, queryItems: queryItems)?.url else { - handler(nil, nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) + handler((nil,nil), nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) + return nil + } + + var request = urlRequest(url, method: method) + request.httpBody = body.data(using: .utf8) + + return networkTask(request: request, handler: handler) + } + + class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: B, handler: @escaping (_ response: NetworkResponse, _ result: T?, _ error: Error?) -> ()) -> URLSessionDataTask? { + guard let url = components(for: path, queryItems: queryItems)?.url else { + handler((nil,nil), nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) return nil } @@ -82,16 +113,16 @@ extension SLazeKit { do { request.httpBody = try JSONEncoder().encode(body) } catch { - handler(nil, nil, error) + handler((nil,nil), nil, error) return nil } return networkTask(request: request, handler: handler) } - class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: [B], handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: [B], handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { guard let url = components(for: path, queryItems: queryItems)?.url else { - handler(nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) + handler((nil,nil), NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) return nil } @@ -99,16 +130,16 @@ extension SLazeKit { do { request.httpBody = try JSONEncoder().encode(body) } catch { - handler(nil, error) + handler((nil,nil), error) return nil } return networkTask(request: request, handler: handler) } - class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: [B], handler: @escaping (_ response: HTTPURLResponse?, _ result: T?, _ error: Error?) -> ()) -> URLSessionDataTask? { + class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, body: [B], handler: @escaping (_ response: NetworkResponse, _ result: T?, _ error: Error?) -> ()) -> URLSessionDataTask? { guard let url = components(for: path, queryItems: queryItems)?.url else { - handler(nil, nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) + handler((nil,nil), nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) return nil } @@ -116,24 +147,24 @@ extension SLazeKit { do { request.httpBody = try JSONEncoder().encode(body) } catch { - handler(nil, nil, error) + handler((nil,nil), nil, error) return nil } return networkTask(request: request, handler: handler) } - class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { guard let url = components(for: path, queryItems: queryItems)?.url else { - handler(nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) + handler((nil,nil), NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) return nil } return networkTask(request: urlRequest(url, method: method), handler: handler) } - class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ result: T?, _ error: Error?) -> ()) -> URLSessionDataTask? { + class func networkTask(path: String, method: HTTPMethod? = nil, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ result: T?, _ error: Error?) -> ()) -> URLSessionDataTask? { guard let url = components(for: path, queryItems: queryItems)?.url else { - handler(nil, nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) + handler((nil,nil), nil, NSError(domain: "Unable to get url from components", code: NSURLErrorBadURL, userInfo: nil)) return nil } return networkTask(request: urlRequest(url, method: method), handler: handler) @@ -153,12 +184,8 @@ extension SLazeKit { urlComponents?.queryItems = queryItems return urlComponents } -} - -//MARK: CoreData mapping support -extension SLazeKit { - @available(iOS 10.0, *) - fileprivate class func synchronize(_ obj: Any) throws { + + private class func synchronize(_ obj: Any) throws { if let array = obj as? [EntityMapping] { array.forEach({_ = try? $0.map()}) } else { @@ -169,63 +196,99 @@ extension SLazeKit { } extension SLazeKit { - public class func request(path: String, method: HTTPMethod, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func request(path: String, method: HTTPMethod, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: method, queryItems: queryItems, body: body, handler: handler) } - public class func request(path: String, method: HTTPMethod, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func request(path: String, method: HTTPMethod, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: method, queryItems: queryItems, body: body, handler: handler) } - public class func request(path: String, method: HTTPMethod, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func request(path: String, method: HTTPMethod, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: method, queryItems: queryItems, body: body, handler: handler) + } + + public class func request(path: String, method: HTTPMethod, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: method, queryItems: queryItems, handler: handler) } - public class func get(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func get(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, queryItems: queryItems, body: body, handler: handler) } - public class func get(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func get(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, queryItems: queryItems, body: body, handler: handler) } - public class func get(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func get(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, queryItems: queryItems, body: body, handler: handler) + } + + public class func get(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, queryItems: queryItems, handler: handler) } - public class func post(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func post(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .POST, queryItems: queryItems, body: body, handler: handler) } - public class func post(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func post(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .POST, queryItems: queryItems, body: body, handler: handler) } - public class func post(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func post(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .POST, queryItems: queryItems, body: body, handler: handler) + } + + public class func post(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .POST, queryItems: queryItems, handler: handler) } - public class func put(path: String, queryItems: [URLQueryItem]? = nil, body: T, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func put(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .PUT, queryItems: queryItems, body: body, handler: handler) } - public class func put(path: String, queryItems: [URLQueryItem]? = nil, body: [T], handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func put(path: String, queryItems: [URLQueryItem]? = nil, body: T, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .PUT, queryItems: queryItems, body: body, handler: handler) } - public class func put(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func put(path: String, queryItems: [URLQueryItem]? = nil, body: [T], handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PUT, queryItems: queryItems, body: body, handler: handler) + } + + public class func put(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .PUT, queryItems: queryItems, handler: handler) } - public class func delete(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func patch(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PATCH, queryItems: queryItems, body: body, handler: handler) + } + + public class func patch(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PATCH, queryItems: queryItems, body: body, handler: handler) + } + + public class func patch(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PATCH, queryItems: queryItems, body: body, handler: handler) + } + + public class func patch(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .PATCH, queryItems: queryItems, handler: handler) + } + + public class func delete(path: String, body: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .DELETE, queryItems: queryItems, body: body, handler: handler) } - public class func delete(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func delete(path: String, body: T, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .DELETE, queryItems: queryItems, body: body, handler: handler) } - public class func delete(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: HTTPURLResponse?, _ error: Error?) -> ()) -> URLSessionDataTask? { + public class func delete(path: String, body: [T], queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { + return SLazeKit.networkTask(path: path, method: .DELETE, queryItems: queryItems, body: body, handler: handler) + } + + public class func delete(path: String, queryItems: [URLQueryItem]? = nil, handler: @escaping (_ response: NetworkResponse, _ error: Error?) -> ()) -> URLSessionDataTask? { return SLazeKit.networkTask(path: path, method: .DELETE, queryItems: queryItems, handler: handler) } } diff --git a/Tests/SLazeKitTests/Models/Model.swift b/Tests/SLazeKitTests/Models/Model.swift index 65a3869..6130077 100644 --- a/Tests/SLazeKitTests/Models/Model.swift +++ b/Tests/SLazeKitTests/Models/Model.swift @@ -2,13 +2,7 @@ import Foundation import SLazeKit import CoreData -//First we gonna create NSManagedObject object on which we will work. - -/// We assume you have Model as an NSManagedObject -class Model: NSManagedObject {} - -// MARK: - Simulate extension for CoreData -extension Model { +class Model: NSManagedObject { @nonobjc public class func fetchRequest() -> NSFetchRequest { return NSFetchRequest(entityName: "Model") } @@ -16,11 +10,7 @@ extension Model { @NSManaged public var id: String @NSManaged public var value: Double @NSManaged public var name: String? -} - - -// MARK: - SLazeKit extension -extension Model { + /// Path pattern for our model API requests public struct PathPattern { static var model: String { return "/api/Models/:modelId" } @@ -53,7 +43,6 @@ extension Model { object.name = name } } - /// We could skip that and add `Encodable` to our ModelResponse. But to highlight it. We will create separate one for request purpose public struct ModelRequest: Encodable { var id: String @@ -66,9 +55,6 @@ extension Model { self.name = model.name } } -} - -extension Model { /// Create request. Called from SLazeKit. Response is not maped or serialized. /// /// - Parameters: @@ -77,7 +63,7 @@ extension Model { /// SLazeKit request are done i background. To handle response on main thread we need to dispatch it. let _ = SLazeKit.post(path: PathPattern.create.patternToPath(with: ["modelId":model.id]), body: ModelRequest(with: model)) { (response, error) in guard error == nil else { - DispatchQueue.main.async { failure?(response?.statusCode ?? -1,error) } + DispatchQueue.main.async { failure?(response.urlResponse?.statusCode ?? -1,error) } return } DispatchQueue.main.async { success() } @@ -87,19 +73,24 @@ extension Model { /// /// - Parameters: /// - modelId: model id used to replace key string in path pattern. + /// + /// SLazeKit request are done i background. To handle response on main thread we need to dispatch it. class func remove(for modelId: String, success: @escaping (() ->()), failure: ((_ statusCode: Int, _ error: Error?) ->())? = nil) { - /// SLazeKit request are done i background. To handle response on main thread we need to dispatch it. let _ = SLazeKit.delete(path: PathPattern.delete.patternToPath(with: ["modelId":modelId])) { (response, error) in - guard error == nil else { - DispatchQueue.main.async { failure?(response?.statusCode ?? -1,error) } - return - } - DispatchQueue.main.async { success() } + guard error == nil else { + DispatchQueue.main.async { failure?(response.urlResponse?.statusCode ?? -1,error) } + return + } + DispatchQueue.main.async { success() } } } /// `[ModelResponse]` Stands as a result type. Decodable provides generic requests. if Response model beside `Decodable` comforms to `EntityMapping` as well it will be serialized. + /// + /// [ModelResponse] Decodable type used to generate result type. + /// + ///Result is type of `[ModelResponse]` to return array of our CoreData models we need to serialize it. + ///`result?.serialized` will return `[Model]` class func getFromServer(success: @escaping (([Model]?) ->()), failure: (() ->())? = nil) { - /// [ModelResponse] Decodable type used to generate result type. let _ = [ModelResponse].get(path: PathPattern.models.patternToPath()) { (response, result, error) in guard error == nil else { failure?() @@ -107,18 +98,19 @@ extension Model { } var models: [Model]? = nil do { - //Result is type of `[ModelResponse]` to return array of our CoreData models we need to serialize it. - //result?.serialized will return `[Model]` - models = try result?.serialized(ModelResponse.persistentContainer?.newBackgroundContext()) + models = try result?.serialized(SLazeKit.newBackgroundContext()) } catch { print(error) } success(models) } } - + /// In this example API request will decode single object of type ModelResponse instead of an array. + /// + ///Result of type `ModelResponse` to return CoreData model we need to serialize it. + /// + ///`result?.serialized` will return `Model` class func getFromServer(for modelId: String, success: @escaping ((Model?) ->()), failure: (() ->())? = nil) { - /// in this example API request will decode single object of type ModelResponse instead of an array. let _ = ModelResponse.get(path: PathPattern.model.patternToPath(with: ["modelId":modelId])) { (response, result, error) in guard error == nil else { failure?() @@ -126,9 +118,7 @@ extension Model { } var models: Model? = nil do { - //Result of type `ModelResponse` to return CoreData model we need to serialize it. - //result?.serialized will return `Model` - models = try result?.serialized(ModelResponse.persistentContainer?.newBackgroundContext()) + models = try result?.serialized(SLazeKit.newBackgroundContext()) } catch { print(error) } diff --git a/Tests/SLazeKitTests/Models/Object.swift b/Tests/SLazeKitTests/Models/Object.swift new file mode 100644 index 0000000..8a0188c --- /dev/null +++ b/Tests/SLazeKitTests/Models/Object.swift @@ -0,0 +1,55 @@ +import Foundation +import SLazeKit +import CoreData + +class Object: NSManagedObject { + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "Object") + } + + @NSManaged public var id: String + @NSManaged public var value: Double + @NSManaged public var name: String? + + /// Path pattern for our model API requests + public struct PathPattern { + static var model: String { return "/api/Objects/:modelId" } + static var models: String { return "/api/Objects" } + static var create: String { return "/api/Objects/:modelId/create" } + static var delete: String { return "/api/Objects/:modelId/delete" } + } + /// We are creating struct which represents Codable object of our API request + /// `EntityMappingCodable` is required + public struct ObjectRestful: EntityMappingCodable { + var id: String + var value: Double + var name: String? + /// We need provide NSManagedObject type for our serialization. + public static var entityType: NSManagedObject.Type { + return Object.self + } + /// By providing id attributes our model are updated/created/serialized + public var idAttributes: [EntityAttribute]? { + return [ + ("id",id) + ] + } + /// init ObjectRestful with our Object clss + /// + /// - Parameter model: model of our Object class + init(with model: Object) { + self.id = model.id + self.value = model.value + self.name = model.name + } + + /// Fill CoreData object with our model response + public func fillObject(with model: NSManagedObject) { + guard let object = model as? Object else { return } + object.id = id + object.value = value + object.name = name + } + } +} +