From 8994cae6eb709af538394f63faacd27fdafdec74 Mon Sep 17 00:00:00 2001 From: Sean Kladek Date: Mon, 9 Jul 2018 07:15:09 -0600 Subject: [PATCH 1/7] Added generic types to delete, get, post, and put to specify the json object type --- .../Networking/MyWebServiceController.swift | 2 +- .../Networking/PostController.swift | 10 ++++---- Source/JSONHandler.swift | 25 +++++++++++++------ Source/RequestController.swift | 8 +++--- Source/WebServiceController.swift | 10 ++++---- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/SampleProject/SampleProject/Networking/MyWebServiceController.swift b/SampleProject/SampleProject/Networking/MyWebServiceController.swift index 9da415a..2a9d895 100644 --- a/SampleProject/SampleProject/Networking/MyWebServiceController.swift +++ b/SampleProject/SampleProject/Networking/MyWebServiceController.swift @@ -3,6 +3,6 @@ import SKWebServiceController class MyWebServiceController: WebServiceController { init() { super.init(baseURL: "https://jsonplaceholder.typicode.com/") - self.useLocalFiles = true + self.useLocalFiles = false } } diff --git a/SampleProject/SampleProject/Networking/PostController.swift b/SampleProject/SampleProject/Networking/PostController.swift index 9143d83..2b45a84 100644 --- a/SampleProject/SampleProject/Networking/PostController.swift +++ b/SampleProject/SampleProject/Networking/PostController.swift @@ -15,14 +15,14 @@ class PostController: NSObject { let endpoint = String(format: Endpoints.postsWithId, postId) - webServiceController.delete(endpoint) { (_, _, error) in + webServiceController.delete(endpoint) { (_: Any?, _, error) in completion(error) } } func getPosts(completion: @escaping ([Post]?, Error?) -> Void) { - webServiceController.get(Endpoints.posts) { (objects, _, error) in - guard let objects = objects as? [[String: Any]] else { + webServiceController.get(Endpoints.posts) { (objects: [[String: Any]]?, _, error) in + guard let objects = objects else { completion(nil, error) return } @@ -39,13 +39,13 @@ class PostController: NSObject { let endpoint = String(format: Endpoints.postsWithId, postId) - webServiceController.put(endpoint, json: post.toJSON(), requestConfiguration: nil) { (_, _, error) in + webServiceController.put(endpoint, json: post.toJSON(), requestConfiguration: nil) { (_: Any?, _, error) in completion(error) } } func uploadNew(_ post: Post, completion: @escaping (Error?) -> Void) { - webServiceController.post(Endpoints.posts, json: post.toJSON()) { (_, _, error) in + webServiceController.post(Endpoints.posts, json: post.toJSON()) { (_: Any?, _, error) in completion(error) } } diff --git a/Source/JSONHandler.swift b/Source/JSONHandler.swift index 6cb406c..e5c2217 100644 --- a/Source/JSONHandler.swift +++ b/Source/JSONHandler.swift @@ -1,10 +1,10 @@ import Foundation -typealias ConvertedJSON = (object: Any?, error: Error?) +typealias ConvertedJSON = (object: T?, error: Error?) protocol JSONHandling { - func dataToJSON(_ data: Data?) -> ConvertedJSON - func jsonToData(_ jsonObject: Any?) -> ConvertedJSON + func dataToJSON(_ data: Data?) -> ConvertedJSON + func jsonToData(_ jsonObject: Any?) -> ConvertedJSON } class JSONHandler: JSONHandling { @@ -21,7 +21,7 @@ class JSONHandler: JSONHandling { // MARK: Instance Methods - func dataToJSON(_ data: Data?) -> ConvertedJSON { + func dataToJSON(_ data: Data?) -> ConvertedJSON { guard let data = data else { let error = WebServiceError(code: .noData, message: "The server returned without error and without data.") return (nil, error) @@ -36,10 +36,21 @@ class JSONHandler: JSONHandling { serializationError = error } - return (jsonObject, serializationError) + guard let typedJSONObject = jsonObject as? T else { + var receivedTypeString = "nil" + + if let jsonObject = jsonObject { + receivedTypeString = String(describing: type(of: jsonObject)) + } + + let typeError = WebServiceError(code: .invalidData, message: "The JSON object was not the expected type. Received \(receivedTypeString), expected \(T.self)") + return (nil, typeError) + } + + return (typedJSONObject, serializationError) } - func jsonToData(_ jsonObject: Any?) -> ConvertedJSON { + func jsonToData(_ jsonObject: Any?) -> ConvertedJSON { guard let jsonObject = jsonObject else { let error = WebServiceError(code: .noData, message: "The JSON object to be converted was nil.") return (nil, error) @@ -50,7 +61,7 @@ class JSONHandler: JSONHandling { return (nil, error) } - var jsonData: Any? = nil + var jsonData: Data? = nil var serializationError: Error? = nil do { diff --git a/Source/RequestController.swift b/Source/RequestController.swift index 7d4a296..99fa4ea 100644 --- a/Source/RequestController.swift +++ b/Source/RequestController.swift @@ -10,7 +10,7 @@ protocol Requesting { func dataCompletion(data: Data?, response: URLResponse?, error: Error?, completion: @escaping WebServiceController.DataCompletion) func imageCompletion(data: Data?, response: URLResponse?, error: Error?, completion: @escaping WebServiceController.ImageCompletion) - func jsonCompletion(data: Data?, response: URLResponse?, error: Error?, completion: @escaping WebServiceController.JSONCompletion) + func jsonCompletion(data: Data?, response: URLResponse?, error: Error?, completion: @escaping WebServiceController.JSONCompletion) func performRequest(_ request: URLRequest, headers: [AnyHashable: Any]?, httpMethod: WebServiceController.HTTPMethod, json: Any?, completion: @escaping RequestCompletion) -> URLSessionDataTask? func performRequest(endpoint: String?, httpMethod: WebServiceController.HTTPMethod, json: Any?, requestConfiguration: RequestConfiguration?, completion: @escaping RequestCompletion) -> URLSessionDataTask? } @@ -73,14 +73,14 @@ class RequestController: Requesting { } } - func jsonCompletion(data: Data?, response: URLResponse?, error: Error?, completion: @escaping WebServiceController.JSONCompletion) { + func jsonCompletion(data: Data?, response: URLResponse?, error: Error?, completion: @escaping WebServiceController.JSONCompletion) { DispatchQueue.main.async { if let error = error { completion(nil, response, error) return } - let result = self.jsonHandler.dataToJSON(data) + let result: ConvertedJSON = self.jsonHandler.dataToJSON(data) completion(result.object, response, result.error) } } @@ -110,7 +110,7 @@ class RequestController: Requesting { return nil } - data = convertedJSON.object as? Data + data = convertedJSON.object } switch httpMethod { diff --git a/Source/WebServiceController.swift b/Source/WebServiceController.swift index 25dbf53..4434770 100644 --- a/Source/WebServiceController.swift +++ b/Source/WebServiceController.swift @@ -12,7 +12,7 @@ open class WebServiceController: NSObject { public typealias ImageCompletion = (UIImage?, URLResponse?, Error?) -> Void /// The completion that is returned with JSON requests. - public typealias JSONCompletion = (Any?, URLResponse?, Error?) -> Void + public typealias JSONCompletion = (T?, URLResponse?, Error?) -> Void // MARK: Static Variables @@ -88,7 +88,7 @@ open class WebServiceController: NSObject { /// - completion: The closure called when the request completes. /// - Returns: The data task to be performed. @discardableResult - open func delete(_ endpoint: String? = nil, requestConfiguration: RequestConfiguration? = nil, completion: @escaping JSONCompletion) -> URLSessionDataTask? { + open func delete(_ endpoint: String? = nil, requestConfiguration: RequestConfiguration? = nil, completion: @escaping JSONCompletion) -> URLSessionDataTask? { return requester.performRequest(endpoint: endpoint, httpMethod: .delete, json: nil, requestConfiguration: requestConfiguration, completion: { (data, response, error) in self.requester.jsonCompletion(data: data, response: response, error: error, completion: completion) }) @@ -102,7 +102,7 @@ open class WebServiceController: NSObject { /// - completion: The closure called when the request completes. /// - Returns: The data task to be performed. @discardableResult - open func get(_ endpoint: String? = nil, requestConfiguration: RequestConfiguration? = nil, completion: @escaping JSONCompletion) -> URLSessionDataTask? { + open func get(_ endpoint: String? = nil, requestConfiguration: RequestConfiguration? = nil, completion: @escaping JSONCompletion) -> URLSessionDataTask? { return requester.performRequest(endpoint: endpoint, httpMethod: .get, json: nil, requestConfiguration: requestConfiguration, completion: { (data, response, error) in self.requester.jsonCompletion(data: data, response: response, error: error, completion: completion) }) @@ -151,7 +151,7 @@ open class WebServiceController: NSObject { /// - completion: The closure called when the request completes. /// - Returns: The upload task to be performed. @discardableResult - open func post(_ endpoint: String? = nil, json: Any?, requestConfiguration: RequestConfiguration? = nil, completion: @escaping JSONCompletion) -> URLSessionDataTask? { + open func post(_ endpoint: String? = nil, json: Any?, requestConfiguration: RequestConfiguration? = nil, completion: @escaping JSONCompletion) -> URLSessionDataTask? { return requester.performRequest(endpoint: endpoint, httpMethod: .post, json: json, requestConfiguration: requestConfiguration, completion: { (data, response, error) in self.requester.jsonCompletion(data: data, response: response, error: error, completion: completion) }) @@ -166,7 +166,7 @@ open class WebServiceController: NSObject { /// - completion: The closure called when the request completes. /// - Returns: The upload task to be performed. @discardableResult - open func put(_ endpoint: String? = nil, json: Any?, requestConfiguration: RequestConfiguration? = nil, completion: @escaping JSONCompletion) -> URLSessionDataTask? { + open func put(_ endpoint: String? = nil, json: Any?, requestConfiguration: RequestConfiguration? = nil, completion: @escaping JSONCompletion) -> URLSessionDataTask? { return requester.performRequest(endpoint: endpoint, httpMethod: .put, json: json, requestConfiguration: requestConfiguration, completion: { (data, response, error) in self.requester.jsonCompletion(data: data, response: response, error: error, completion: completion) }) From d2bf825cc39d71c5faf0b405d80b405fbc7a9ef0 Mon Sep 17 00:00:00 2001 From: Sean Kladek Date: Mon, 9 Jul 2018 07:19:03 -0600 Subject: [PATCH 2/7] Fixed failing tests --- Tests/Mocks/MockJSONHandler.swift | 4 +-- Tests/Mocks/MockRequester.swift | 2 +- .../JSONHandlerSpec.swift | 8 ++--- .../RequestControllerSpec.swift | 6 ++-- .../WebServiceControllerSpec.swift | 32 +++++++++---------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Tests/Mocks/MockJSONHandler.swift b/Tests/Mocks/MockJSONHandler.swift index 4ed9258..8619a0d 100644 --- a/Tests/Mocks/MockJSONHandler.swift +++ b/Tests/Mocks/MockJSONHandler.swift @@ -7,14 +7,14 @@ class MockJSONHandler: JSONHandling { var jsonToDataCalled = false var mockError = false - func dataToJSON(_ data: Data?) -> ConvertedJSON { + func dataToJSON(_ data: Data?) -> ConvertedJSON { dataToJSONCalled = true let error = mockError ? NSError(domain: "test.domain", code: 999, userInfo: nil) : nil return (nil, error) } - func jsonToData(_ jsonObject: Any?) -> ConvertedJSON { + func jsonToData(_ jsonObject: Any?) -> ConvertedJSON { jsonToDataCalled = true let error = mockError ? NSError(domain: "test.domain", code: 999, userInfo: nil) : nil diff --git a/Tests/Mocks/MockRequester.swift b/Tests/Mocks/MockRequester.swift index 77ebfc7..90c05e0 100644 --- a/Tests/Mocks/MockRequester.swift +++ b/Tests/Mocks/MockRequester.swift @@ -31,7 +31,7 @@ class MockRequester: Requesting { completion(nil, nil, nil) } - func jsonCompletion(data: Data?, response: URLResponse?, error: Error?, completion: @escaping WebServiceController.JSONCompletion) { + func jsonCompletion(data: Data?, response: URLResponse?, error: Error?, completion: @escaping WebServiceController.JSONCompletion) { jsonCompletionCalled = true completion(nil, nil, nil) diff --git a/Tests/WebServiceController/JSONHandlerSpec.swift b/Tests/WebServiceController/JSONHandlerSpec.swift index 1bae908..edb5c42 100644 --- a/Tests/WebServiceController/JSONHandlerSpec.swift +++ b/Tests/WebServiceController/JSONHandlerSpec.swift @@ -16,7 +16,7 @@ class JSONHandlerSpec: QuickSpec { context("dataToJSON(_:") { it("Should return an error if data is nil") { - let result = unitUnderTest.dataToJSON(nil) + let result: ConvertedJSON = unitUnderTest.dataToJSON(nil) expect((result.error as NSError?)?.code).to(equal(WebServiceError.Code.noData.rawValue)) } @@ -30,12 +30,12 @@ class JSONHandlerSpec: QuickSpec { print(error) } - let result = unitUnderTest.dataToJSON(jsonData) - expect(result.object as? [String : String]).to(equal(dictionary)) + let result: ConvertedJSON<[String: String]> = unitUnderTest.dataToJSON(jsonData) + expect(result.object).to(equal(dictionary)) } it("Should return an error if the data cannot be deserialized") { - let result = unitUnderTest.dataToJSON(Data()) + let result: ConvertedJSON = unitUnderTest.dataToJSON(Data()) expect(result.error).toNot(beNil()) } } diff --git a/Tests/WebServiceController/RequestControllerSpec.swift b/Tests/WebServiceController/RequestControllerSpec.swift index 93c373e..4d746d9 100644 --- a/Tests/WebServiceController/RequestControllerSpec.swift +++ b/Tests/WebServiceController/RequestControllerSpec.swift @@ -119,7 +119,7 @@ class RequestControllerSpec: QuickSpec { it("Should execute the completion on the main thread") { waitUntil { done in DispatchQueue.global(qos: .background).async { - unitUnderTest.jsonCompletion(data: nil, response: nil, error: nil, completion: { (_, _, _) in + unitUnderTest.jsonCompletion(data: nil, response: nil, error: nil, completion: { (_: Any?, _, _) in expect(Thread.isMainThread).to(beTrue()) done() }) @@ -130,7 +130,7 @@ class RequestControllerSpec: QuickSpec { it("Should return an error in the completion if an error is passed in") { let inputError = NSError(domain: "com.test.domain", code: 999, userInfo: nil) waitUntil { done in - unitUnderTest.jsonCompletion(data: nil, response: nil, error: inputError, completion: { (_, _, outputError) in + unitUnderTest.jsonCompletion(data: nil, response: nil, error: inputError, completion: { (_: Any?, _, outputError) in expect(outputError).to(be(inputError)) done() }) @@ -139,7 +139,7 @@ class RequestControllerSpec: QuickSpec { it("Should call data to json on the json handler if no error is passed in") { waitUntil { done in - unitUnderTest.jsonCompletion(data: nil, response: nil, error: nil, completion: { (_, _, _) in + unitUnderTest.jsonCompletion(data: nil, response: nil, error: nil, completion: { (_: Any?, _, _) in expect(jsonHandler.dataToJSONCalled).to(beTrue()) done() }) diff --git a/Tests/WebServiceController/WebServiceControllerSpec.swift b/Tests/WebServiceController/WebServiceControllerSpec.swift index ec4f1a1..c9c6f00 100644 --- a/Tests/WebServiceController/WebServiceControllerSpec.swift +++ b/Tests/WebServiceController/WebServiceControllerSpec.swift @@ -33,13 +33,13 @@ class WebServiceControllerSpec: QuickSpec { context("delete(endpoint:completion:)") { it("Should call performRequestWithEndpoint on the requester") { - unitUnderTest.delete(completion: { (_, _, _) in + unitUnderTest.delete(completion: { (_: Any?, _, _) in expect(requester.performRequestWithEndpointCalled).to(beTrue()) }) } it("Should call the JSON handler in the completion") { - unitUnderTest.delete(completion: { (_, _, _) in + unitUnderTest.delete(completion: { (_: Any?, _, _) in expect(requester.jsonCompletionCalled).to(beTrue()) }) } @@ -47,25 +47,25 @@ class WebServiceControllerSpec: QuickSpec { it("Should return the data task returned by the requester") { let inputTask = URLSessionDataTask() requester.dataTask = inputTask - let returnedTask = unitUnderTest.delete(completion: { (_, _, _) in }) + let returnedTask = unitUnderTest.delete(completion: { (_: Any?, _, _) in }) expect(returnedTask).to(be(inputTask)) } it("Should return nil if no task is returned by the requester") { - let returnedTask = unitUnderTest.delete(completion: { (_, _, _) in }) + let returnedTask = unitUnderTest.delete(completion: { (_: Any?, _, _) in }) expect(returnedTask).to(beNil()) } } context("get(endpoint:parameters:completion:") { it("Should call performRequestWithEndpoint on the requester") { - unitUnderTest.get(completion: { (_, _, _) in + unitUnderTest.get(completion: { (_: Any?, _, _) in expect(requester.performRequestWithEndpointCalled).to(beTrue()) }) } it("Should call the JSON handler in the completion") { - unitUnderTest.get(completion: { (_, _, _) in + unitUnderTest.get(completion: { (_: Any?, _, _) in expect(requester.jsonCompletionCalled).to(beTrue()) }) } @@ -73,12 +73,12 @@ class WebServiceControllerSpec: QuickSpec { it("Should return the data task returned by the requester") { let inputTask = URLSessionDataTask() requester.dataTask = inputTask - let returnedTask = unitUnderTest.get(completion: { (_, _, _) in }) + let returnedTask = unitUnderTest.get(completion: { (_: Any?, _, _) in }) expect(returnedTask).to(be(inputTask)) } it("Should return nil if no task is returned by the requester") { - let returnedTask = unitUnderTest.get(completion: { (_, _, _) in }) + let returnedTask = unitUnderTest.get(completion: { (_: Any?, _, _) in }) expect(returnedTask).to(beNil()) } } @@ -149,13 +149,13 @@ class WebServiceControllerSpec: QuickSpec { context("post(endpoint:parameters:json:completion:)") { it("Should call performRequestWithEndpoint on the requester") { - unitUnderTest.post(json: nil, completion: { (_, _, _) in + unitUnderTest.post(json: nil, completion: { (_: Any?, _, _) in expect(requester.performRequestWithEndpointCalled).to(beTrue()) }) } it("Should call the JSON handler in the completion") { - unitUnderTest.post(json: nil, completion: { (_, _, _) in + unitUnderTest.post(json: nil, completion: { (_: Any?, _, _) in expect(requester.jsonCompletionCalled).to(beTrue()) }) } @@ -163,25 +163,25 @@ class WebServiceControllerSpec: QuickSpec { it("Should return the data task returned by the requester") { let inputTask = URLSessionDataTask() requester.dataTask = inputTask - let returnedTask = unitUnderTest.post(json: nil, completion: { (_, _, _) in }) + let returnedTask = unitUnderTest.post(json: nil, completion: { (_: Any?, _, _) in }) expect(returnedTask).to(be(inputTask)) } it("Should return nil if no task is returned by the requester") { - let returnedTask = unitUnderTest.post(json: nil, completion: { (_, _, _) in }) + let returnedTask = unitUnderTest.post(json: nil, completion: { (_: Any?, _, _) in }) expect(returnedTask).to(beNil()) } } context("put(endpoint:parameters:json:completion:)") { it("Should call performRequestWithEndpoint on the requester") { - unitUnderTest.put(json: nil, completion: { (_, _, _) in + unitUnderTest.put(json: nil, completion: { (_: Any?, _, _) in expect(requester.performRequestWithEndpointCalled).to(beTrue()) }) } it("Should call the JSON handler in the completion") { - unitUnderTest.put(json: nil, completion: { (_, _, _) in + unitUnderTest.put(json: nil, completion: { (_: Any?, _, _) in expect(requester.jsonCompletionCalled).to(beTrue()) }) } @@ -189,12 +189,12 @@ class WebServiceControllerSpec: QuickSpec { it("Should return the data task returned by the requester") { let inputTask = URLSessionDataTask() requester.dataTask = inputTask - let returnedTask = unitUnderTest.put(json: nil, completion: { (_, _, _) in }) + let returnedTask = unitUnderTest.put(json: nil, completion: { (_: Any?, _, _) in }) expect(returnedTask).to(be(inputTask)) } it("Should return nil if no task is returned by the requester") { - let returnedTask = unitUnderTest.put(json: nil, completion: { (_, _, _) in }) + let returnedTask = unitUnderTest.put(json: nil, completion: { (_: Any?, _, _) in }) expect(returnedTask).to(beNil()) } } From 142f11df795f15440f98221713a5927a993e73c4 Mon Sep 17 00:00:00 2001 From: Sean Kladek Date: Mon, 9 Jul 2018 07:19:51 -0600 Subject: [PATCH 3/7] Updated to recommended pods settings --- Pods/Pods.xcodeproj/project.pbxproj | 6 +++++- SKWebServiceController.xcodeproj/project.pbxproj | 6 +++++- .../xcschemes/SKWebServiceController.xcscheme | 8 +++----- SampleProject/SampleProject.xcodeproj/project.pbxproj | 6 +++++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj index 21b7aac..8918408 100644 --- a/Pods/Pods.xcodeproj/project.pbxproj +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -843,7 +843,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0940; TargetAttributes = { 1F570E0E95FE043DC249E86F25FA54C4 = { LastSwiftMigration = 0900; @@ -1158,6 +1158,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -1165,6 +1166,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -1472,6 +1474,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -1479,6 +1482,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/SKWebServiceController.xcodeproj/project.pbxproj b/SKWebServiceController.xcodeproj/project.pbxproj index ecbafe6..4e677aa 100644 --- a/SKWebServiceController.xcodeproj/project.pbxproj +++ b/SKWebServiceController.xcodeproj/project.pbxproj @@ -284,7 +284,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = "Sean Kladek"; TargetAttributes = { 147FBB2A1EE6482000D2441D = { @@ -494,6 +494,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -501,6 +502,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -554,6 +556,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -561,6 +564,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/SKWebServiceController.xcodeproj/xcshareddata/xcschemes/SKWebServiceController.xcscheme b/SKWebServiceController.xcodeproj/xcshareddata/xcschemes/SKWebServiceController.xcscheme index 5acd2e9..b516247 100644 --- a/SKWebServiceController.xcodeproj/xcshareddata/xcschemes/SKWebServiceController.xcscheme +++ b/SKWebServiceController.xcodeproj/xcshareddata/xcschemes/SKWebServiceController.xcscheme @@ -1,6 +1,6 @@ + codeCoverageEnabled = "YES" + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -57,7 +56,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/SampleProject/SampleProject.xcodeproj/project.pbxproj b/SampleProject/SampleProject.xcodeproj/project.pbxproj index 92028a9..8e7b6cc 100644 --- a/SampleProject/SampleProject.xcodeproj/project.pbxproj +++ b/SampleProject/SampleProject.xcodeproj/project.pbxproj @@ -262,7 +262,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = "Sean Kladek"; TargetAttributes = { 14053C841ED394A80094E780 = { @@ -443,6 +443,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -450,6 +451,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -499,6 +501,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -506,6 +509,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; From 4e74da2b88e28671ec326b12a95ed040edb426ea Mon Sep 17 00:00:00 2001 From: Sean Kladek Date: Mon, 9 Jul 2018 07:28:04 -0600 Subject: [PATCH 4/7] Added tests around the JSONHandler --- .../JSONHandlerSpec.swift | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/Tests/WebServiceController/JSONHandlerSpec.swift b/Tests/WebServiceController/JSONHandlerSpec.swift index edb5c42..b970d96 100644 --- a/Tests/WebServiceController/JSONHandlerSpec.swift +++ b/Tests/WebServiceController/JSONHandlerSpec.swift @@ -20,24 +20,37 @@ class JSONHandlerSpec: QuickSpec { expect((result.error as NSError?)?.code).to(equal(WebServiceError.Code.noData.rawValue)) } - it("Should deserialize valid data and return the object through the completion closure") { + it("Should deserialize valid dictionary data and return the object through the completion closure") { let dictionary = ["key1" : "value1", "key2" : "value2"] var jsonData: Data? - - do { - jsonData = try JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted) - } catch { - print(error) - } + jsonData = try! JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted) let result: ConvertedJSON<[String: String]> = unitUnderTest.dataToJSON(jsonData) expect(result.object).to(equal(dictionary)) } + it("Should deserialize valid array data and return the object through the completion closure") { + let dictionary = ["value1", "value2", "value3"] + var jsonData: Data? + jsonData = try! JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted) + + let result: ConvertedJSON<[String]> = unitUnderTest.dataToJSON(jsonData) + expect(result.object).to(equal(dictionary)) + } + it("Should return an error if the data cannot be deserialized") { let result: ConvertedJSON = unitUnderTest.dataToJSON(Data()) expect(result.error).toNot(beNil()) } + + it("Should return an error if the deserialized data does not match the generic type") { + let dictionary = ["key1" : "value1", "key2" : "value2"] + var jsonData: Data? + jsonData = try! JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted) + + let result: ConvertedJSON = unitUnderTest.dataToJSON(jsonData) + expect(result.error).toNot(beNil()) + } } context("jsonToData(_:)") { From cfaf6b789bc1efa41bf585b093f9796f8d52d67c Mon Sep 17 00:00:00 2001 From: Sean Kladek Date: Mon, 9 Jul 2018 07:38:54 -0600 Subject: [PATCH 5/7] Updated the README --- README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7015603..beb255b 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,19 @@ The WebServiceController subclass will be used to perform requests. There are me ### JSON Methods -These methods are used to interact with endpoints that send and receive JSON. All requests have a `JSONCompletion` object that is executed when the request is complete. +These methods are used to interact with endpoints that send and receive JSON. All requests have a `JSONCompletion` object that is executed when the request is complete. The methods require a generic type to be specified for the expected type of the JSON. For instance, a GET call that returns an array would call the following: + + get { (json: [Any]?, response, error) in + + } + +If the return data is expected to be a dictionary, that GET call would be written as such: + + get { (json: [String: Any]?, response, error) in + + } + +This will support any type that can be output by `JSONSerialization.jsonObject(with:options:)`. If the call should return `Data` or a `UIImage`, there are separate methods to retrieve that. See Data Methods and Image Methods below. #### Delete @@ -72,6 +84,14 @@ Performs a post request on the provided endpoint. This method has an optional js Performs a put request on the provided endpoint. This method has an optional json parameter. This object must be a valid JSON object. This will be converted to data and sent with the request. +### Data Methods + +There is currently a single method for getting data from a URL. This method has a `DataCompletion` object that is executed when the request is complete. This does not attempt to parse or format the data in any way, it simply returns whatever data is sent by the web service. + +#### Get Data + +This method takes a full URL and will return any data returned by the web service without attempting to format or manipulate it in any way. + ### Image Methods There is currently a single method for getting an image from a URL. This method has an `ImageCompletion` object that is executed when the request is complete. From ba44b481c4a085aa1c7630917b84631a0847f01b Mon Sep 17 00:00:00 2001 From: Sean Kladek Date: Mon, 9 Jul 2018 07:41:37 -0600 Subject: [PATCH 6/7] README updates --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index beb255b..01379d6 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ If the return data is expected to be a dictionary, that GET call would be writte } -This will support any type that can be output by `JSONSerialization.jsonObject(with:options:)`. If the call should return `Data` or a `UIImage`, there are separate methods to retrieve that. See Data Methods and Image Methods below. +This will support any type that can be output by `JSONSerialization.jsonObject(with:options:)`. If the call should return `Data` or a `UIImage`, there are separate methods to retrieve that. See Data Methods and Image Methods below. If the type is unknown or the version 1.x behavior is preferred, `Any?` can be specified as the type. #### Delete From 50e34affb934ed5572e0f5d007ec074b374ed070 Mon Sep 17 00:00:00 2001 From: Sean Kladek Date: Mon, 9 Jul 2018 08:06:21 -0600 Subject: [PATCH 7/7] Version bump to 2.0.0 --- CHANGELOG.md | 11 +++++++++++ SKWebServiceController.podspec | 2 +- Source/Info.plist | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e07b6..e496830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,22 @@ # Change Log All notable changes to this project will be documented in this file. +#### 2.x Releases +- `2.0.x` Releases - [2.0.0](#200) + #### 1.x Releases - `1.2.x` Releases - [1.2.0](#120), [1.2.1](#121) - `1.1.x` Releases - [1.1.0](#110) - `1.0.x` Releases - [1.0.0](#100) +## [2.0.0](https://github.com/skladek/SKWebServiceController/releases/tag/2.0.0) + +#### Added +- Generic types to the delete, get, post, and put methods to allow specifying the expected JSON object type. If the type is unknown or the old behavior is preferred, specify `Any?` + +#### Removed +- The untyped delete, get, post, and put methods. + ## [1.2.1](https://github.com/skladek/SKWebServiceController/releases/tag/1.2.1) #### Updated diff --git a/SKWebServiceController.podspec b/SKWebServiceController.podspec index 5e7580f..cd6a391 100644 --- a/SKWebServiceController.podspec +++ b/SKWebServiceController.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'SKWebServiceController' - spec.version = '1.2.1' + spec.version = '2.0.0' spec.license = 'MIT' spec.summary = 'A barebones network controller.' spec.homepage = 'https://github.com/skladek/SKWebServiceController' diff --git a/Source/Info.plist b/Source/Info.plist index 1e79e1d..d87c8b3 100644 --- a/Source/Info.plist +++ b/Source/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.2.1 + 2.0.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass