diff --git a/.gitmodules b/.gitmodules index fc535505..30480090 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/antitypical/Result.git [submodule "Carthage/Checkouts/OHHTTPStubs"] path = Carthage/Checkouts/OHHTTPStubs - url = https://github.com/ikesyo/OHHTTPStubs.git + url = https://github.com/AliSoftware/OHHTTPStubs.git diff --git a/.travis.yml b/.travis.yml index 12df949e..07f23bd2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,6 @@ env: matrix: include: - - os: osx - language: objective-c - osx_image: xcode7.3 - env: JOB=Xcode7.3 - os: osx language: objective-c osx_image: xcode8 @@ -21,9 +17,9 @@ install: script: - if [[ "$JOB" == "Xcode7.3" ]]; then pod lib lint; fi - set -o pipefail - - xcodebuild test -workspace APIKit.xcworkspace -scheme APIKit | xcpretty -c - - xcodebuild test -workspace APIKit.xcworkspace -scheme APIKit -sdk iphonesimulator -destination "name=iPhone 6s" | xcpretty -c - - xcodebuild test -workspace APIKit.xcworkspace -scheme APIKit -sdk appletvsimulator -destination "name=Apple TV 1080p" | xcpretty -c + - xcodebuild build-for-testing test-without-building -workspace APIKit.xcworkspace -scheme APIKit | xcpretty -c + - xcodebuild build-for-testing test-without-building -workspace APIKit.xcworkspace -scheme APIKit -sdk iphonesimulator -destination "name=iPhone 6s" | xcpretty -c + - xcodebuild build-for-testing test-without-building -workspace APIKit.xcworkspace -scheme APIKit -sdk appletvsimulator -destination "name=Apple TV 1080p" | xcpretty -c - carthage build --no-skip-current after_success: diff --git a/APIKit.xcodeproj/project.pbxproj b/APIKit.xcodeproj/project.pbxproj index fb3cae4f..266111fd 100644 --- a/APIKit.xcodeproj/project.pbxproj +++ b/APIKit.xcodeproj/project.pbxproj @@ -12,28 +12,28 @@ 141F12321C1C9AC70026D415 /* OHHTTPStubs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD51152D1B1FFCC700514240 /* OHHTTPStubs.framework */; }; 141F12361C1C9AC70026D415 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CD5115241B1FFBA900514240 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 141F12371C1C9AC70026D415 /* OHHTTPStubs.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CD51152D1B1FFCC700514240 /* OHHTTPStubs.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 7F09BF931C8AE8DB00F4A59A /* RequestTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F09BF8D1C8AE8DB00F4A59A /* RequestTypeTests.swift */; }; + 7F09BF931C8AE8DB00F4A59A /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F09BF8D1C8AE8DB00F4A59A /* RequestTests.swift */; }; 7F10D8EE1CD5CF8D00722F66 /* CallbackQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F10D8ED1CD5CF8D00722F66 /* CallbackQueue.swift */; }; 7F10D8F01CD5D10100722F66 /* SessionCallbackQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F10D8EF1CD5D10100722F66 /* SessionCallbackQueueTests.swift */; }; - 7F18BD0F1C972C38003A31DF /* BodyParametersType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F18BD0E1C972C38003A31DF /* BodyParametersType.swift */; }; + 7F18BD0F1C972C38003A31DF /* BodyParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F18BD0E1C972C38003A31DF /* BodyParameters.swift */; }; 7F18BD111C972C69003A31DF /* JSONBodyParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F18BD101C972C69003A31DF /* JSONBodyParameters.swift */; }; 7F18BD131C972E5A003A31DF /* FormURLEncodedBodyParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F18BD121C972E5A003A31DF /* FormURLEncodedBodyParameters.swift */; }; 7F18BD1A1C9730ED003A31DF /* URLEncodedSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F18BD181C9730ED003A31DF /* URLEncodedSerialization.swift */; }; - 7F2001D71CF49F3000C5D0EE /* NSURLSessionAdapterSubclassTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2001D61CF49F3000C5D0EE /* NSURLSessionAdapterSubclassTests.swift */; }; - 7F2A15BD1CEB5F67009A12A2 /* NSData+NSInputStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2A15BC1CEB5F67009A12A2 /* NSData+NSInputStream.swift */; }; + 7F2001D71CF49F3000C5D0EE /* URLSessionAdapterSubclassTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2001D61CF49F3000C5D0EE /* URLSessionAdapterSubclassTests.swift */; }; + 7F2A15BD1CEB5F67009A12A2 /* Data+InputStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2A15BC1CEB5F67009A12A2 /* Data+InputStream.swift */; }; 7F2A537A1CF52165000353F0 /* test.json in Resources */ = {isa = PBXBuildFile; fileRef = 7F2A53791CF52165000353F0 /* test.json */; }; 7F2A537C1CF5D55D000353F0 /* URLEncodedSerializationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F2A537B1CF5D55D000353F0 /* URLEncodedSerializationTests.swift */; }; 7F7E8F151C8AD4B1008A13A9 /* APIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F7E8F0B1C8AD4B1008A13A9 /* APIKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7F7E8F161C8AD4B1008A13A9 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F7E8F0C1C8AD4B1008A13A9 /* HTTPMethod.swift */; }; - 7F7E8F1A1C8AD4B1008A13A9 /* RequestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F7E8F101C8AD4B1008A13A9 /* RequestType.swift */; }; + 7F7E8F1A1C8AD4B1008A13A9 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F7E8F101C8AD4B1008A13A9 /* Request.swift */; }; 7F7E8F1C1C8AD4B1008A13A9 /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F7E8F121C8AD4B1008A13A9 /* Session.swift */; }; 7F85FB801C9CF12600CEE132 /* JSONDataParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB7F1C9CF12600CEE132 /* JSONDataParser.swift */; }; 7F85FB831C9CF25D00CEE132 /* JSONDataParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB821C9CF25D00CEE132 /* JSONDataParserTests.swift */; }; 7F85FB871C9CF47300CEE132 /* FormURLEncodedDataParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB861C9CF47300CEE132 /* FormURLEncodedDataParser.swift */; }; 7F85FB891C9CF7B000CEE132 /* FormURLEncodedDataParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB881C9CF7B000CEE132 /* FormURLEncodedDataParserTests.swift */; }; - 7F85FB8E1C9D317300CEE132 /* NSURLSessionAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB8C1C9D317300CEE132 /* NSURLSessionAdapter.swift */; }; - 7F85FB8F1C9D317300CEE132 /* SessionAdapterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB8D1C9D317300CEE132 /* SessionAdapterType.swift */; }; - 7F85FB921C9D336D00CEE132 /* NSURLSessionAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB911C9D336D00CEE132 /* NSURLSessionAdapterTests.swift */; }; + 7F85FB8E1C9D317300CEE132 /* URLSessionAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB8C1C9D317300CEE132 /* URLSessionAdapter.swift */; }; + 7F85FB8F1C9D317300CEE132 /* SessionAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB8D1C9D317300CEE132 /* SessionAdapter.swift */; }; + 7F85FB921C9D336D00CEE132 /* URLSessionAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB911C9D336D00CEE132 /* URLSessionAdapterTests.swift */; }; 7F85FB9A1C9D3DA700CEE132 /* TestRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB981C9D3DA700CEE132 /* TestRequest.swift */; }; 7F85FB9B1C9D3DA700CEE132 /* TestSessionAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB991C9D3DA700CEE132 /* TestSessionAdapter.swift */; }; 7F85FB9F1C9D3F0B00CEE132 /* SessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F85FB9E1C9D3F0B00CEE132 /* SessionTests.swift */; }; @@ -45,8 +45,7 @@ 7FA19A411C9CBF2A005D25AE /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA19A3E1C9CBF2A005D25AE /* RequestError.swift */; }; 7FA19A421C9CBF2A005D25AE /* ResponseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA19A3F1C9CBF2A005D25AE /* ResponseError.swift */; }; 7FA19A431C9CBF2A005D25AE /* SessionTaskError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA19A401C9CBF2A005D25AE /* SessionTaskError.swift */; }; - 7FA19A461C9CC9D0005D25AE /* DataParserType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA19A451C9CC9D0005D25AE /* DataParserType.swift */; }; - 7FAC40341C8F2C900098C4B2 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FAC40331C8F2C900098C4B2 /* Box.swift */; }; + 7FA19A461C9CC9D0005D25AE /* DataParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA19A451C9CC9D0005D25AE /* DataParser.swift */; }; 7FAC64AE1CDC7ADE00F1BB45 /* AbstractInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FAC64AC1CDC7ADE00F1BB45 /* AbstractInputStream.m */; }; 7FB650DF1CEA0E6B00366992 /* StringDataParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB650DE1CEA0E6B00366992 /* StringDataParser.swift */; }; 7FB650E11CEA0F3D00366992 /* StringDataParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB650E01CEA0F3D00366992 /* StringDataParserTests.swift */; }; @@ -86,29 +85,29 @@ 141F123F1C1C9EA30026D415 /* APIKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = APIKit.xcconfig; path = Configurations/APIKit.xcconfig; sourceTree = ""; }; 141F12401C1C9EA30026D415 /* Tests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Tests.xcconfig; path = Configurations/Tests.xcconfig; sourceTree = ""; }; 7F09BF8B1C8AE8DB00F4A59A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 7F09BF8D1C8AE8DB00F4A59A /* RequestTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestTypeTests.swift; sourceTree = ""; }; + 7F09BF8D1C8AE8DB00F4A59A /* RequestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = ""; }; 7F10D8ED1CD5CF8D00722F66 /* CallbackQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallbackQueue.swift; sourceTree = ""; }; 7F10D8EF1CD5D10100722F66 /* SessionCallbackQueueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionCallbackQueueTests.swift; sourceTree = ""; }; - 7F18BD0E1C972C38003A31DF /* BodyParametersType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BodyParametersType.swift; sourceTree = ""; }; + 7F18BD0E1C972C38003A31DF /* BodyParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BodyParameters.swift; sourceTree = ""; }; 7F18BD101C972C69003A31DF /* JSONBodyParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONBodyParameters.swift; sourceTree = ""; }; 7F18BD121C972E5A003A31DF /* FormURLEncodedBodyParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormURLEncodedBodyParameters.swift; sourceTree = ""; }; 7F18BD181C9730ED003A31DF /* URLEncodedSerialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLEncodedSerialization.swift; sourceTree = ""; }; - 7F2001D61CF49F3000C5D0EE /* NSURLSessionAdapterSubclassTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSURLSessionAdapterSubclassTests.swift; sourceTree = ""; }; - 7F2A15BC1CEB5F67009A12A2 /* NSData+NSInputStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSData+NSInputStream.swift"; sourceTree = ""; }; + 7F2001D61CF49F3000C5D0EE /* URLSessionAdapterSubclassTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionAdapterSubclassTests.swift; sourceTree = ""; }; + 7F2A15BC1CEB5F67009A12A2 /* Data+InputStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+InputStream.swift"; sourceTree = ""; }; 7F2A53791CF52165000353F0 /* test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = test.json; sourceTree = ""; }; 7F2A537B1CF5D55D000353F0 /* URLEncodedSerializationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLEncodedSerializationTests.swift; sourceTree = ""; }; 7F7E8F0B1C8AD4B1008A13A9 /* APIKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIKit.h; sourceTree = ""; }; 7F7E8F0C1C8AD4B1008A13A9 /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; 7F7E8F0D1C8AD4B1008A13A9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 7F7E8F101C8AD4B1008A13A9 /* RequestType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestType.swift; sourceTree = ""; }; + 7F7E8F101C8AD4B1008A13A9 /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; 7F7E8F121C8AD4B1008A13A9 /* Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = ""; }; 7F85FB7F1C9CF12600CEE132 /* JSONDataParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONDataParser.swift; sourceTree = ""; }; 7F85FB821C9CF25D00CEE132 /* JSONDataParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONDataParserTests.swift; sourceTree = ""; }; 7F85FB861C9CF47300CEE132 /* FormURLEncodedDataParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormURLEncodedDataParser.swift; sourceTree = ""; }; 7F85FB881C9CF7B000CEE132 /* FormURLEncodedDataParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormURLEncodedDataParserTests.swift; sourceTree = ""; }; - 7F85FB8C1C9D317300CEE132 /* NSURLSessionAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSURLSessionAdapter.swift; sourceTree = ""; }; - 7F85FB8D1C9D317300CEE132 /* SessionAdapterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionAdapterType.swift; sourceTree = ""; }; - 7F85FB911C9D336D00CEE132 /* NSURLSessionAdapterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSURLSessionAdapterTests.swift; sourceTree = ""; }; + 7F85FB8C1C9D317300CEE132 /* URLSessionAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionAdapter.swift; sourceTree = ""; }; + 7F85FB8D1C9D317300CEE132 /* SessionAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionAdapter.swift; sourceTree = ""; }; + 7F85FB911C9D336D00CEE132 /* URLSessionAdapterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionAdapterTests.swift; sourceTree = ""; }; 7F85FB981C9D3DA700CEE132 /* TestRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestRequest.swift; sourceTree = ""; }; 7F85FB991C9D3DA700CEE132 /* TestSessionAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestSessionAdapter.swift; sourceTree = ""; }; 7F85FB9E1C9D3F0B00CEE132 /* SessionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionTests.swift; sourceTree = ""; }; @@ -121,8 +120,7 @@ 7FA19A3E1C9CBF2A005D25AE /* RequestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestError.swift; sourceTree = ""; }; 7FA19A3F1C9CBF2A005D25AE /* ResponseError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseError.swift; sourceTree = ""; }; 7FA19A401C9CBF2A005D25AE /* SessionTaskError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionTaskError.swift; sourceTree = ""; }; - 7FA19A451C9CC9D0005D25AE /* DataParserType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataParserType.swift; sourceTree = ""; }; - 7FAC40331C8F2C900098C4B2 /* Box.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; + 7FA19A451C9CC9D0005D25AE /* DataParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataParser.swift; sourceTree = ""; }; 7FAC64AC1CDC7ADE00F1BB45 /* AbstractInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AbstractInputStream.m; sourceTree = ""; }; 7FB650DE1CEA0E6B00366992 /* StringDataParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringDataParser.swift; sourceTree = ""; }; 7FB650E01CEA0F3D00366992 /* StringDataParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringDataParserTests.swift; sourceTree = ""; }; @@ -176,7 +174,7 @@ children = ( 7F85FB9E1C9D3F0B00CEE132 /* SessionTests.swift */, 7F10D8EF1CD5D10100722F66 /* SessionCallbackQueueTests.swift */, - 7F09BF8D1C8AE8DB00F4A59A /* RequestTypeTests.swift */, + 7F09BF8D1C8AE8DB00F4A59A /* RequestTests.swift */, 7F85FB901C9D335F00CEE132 /* SessionAdapterType */, 7F85FB811C9CF24900CEE132 /* DataParserType */, 7FA19A361C98642F005D25AE /* BodyParametersType */, @@ -196,17 +194,17 @@ name = "Supporting Files"; sourceTree = ""; }; - 7F18BD0D1C972C38003A31DF /* BodyParametersType */ = { + 7F18BD0D1C972C38003A31DF /* BodyParameters */ = { isa = PBXGroup; children = ( - 7F18BD0E1C972C38003A31DF /* BodyParametersType.swift */, + 7F18BD0E1C972C38003A31DF /* BodyParameters.swift */, 7F18BD101C972C69003A31DF /* JSONBodyParameters.swift */, 7F18BD121C972E5A003A31DF /* FormURLEncodedBodyParameters.swift */, 7FA19A311C98542E005D25AE /* MultipartFormDataBodyParameters.swift */, - 7F2A15BC1CEB5F67009A12A2 /* NSData+NSInputStream.swift */, + 7F2A15BC1CEB5F67009A12A2 /* Data+InputStream.swift */, 7FAC64AC1CDC7ADE00F1BB45 /* AbstractInputStream.m */, ); - path = BodyParametersType; + path = BodyParameters; sourceTree = ""; }; 7F18BD161C9730ED003A31DF /* Serializations */ = { @@ -241,13 +239,12 @@ isa = PBXGroup; children = ( 7F7E8F121C8AD4B1008A13A9 /* Session.swift */, - 7F7E8F101C8AD4B1008A13A9 /* RequestType.swift */, + 7F7E8F101C8AD4B1008A13A9 /* Request.swift */, 7F7E8F0C1C8AD4B1008A13A9 /* HTTPMethod.swift */, 7F10D8ED1CD5CF8D00722F66 /* CallbackQueue.swift */, - 7FAC40331C8F2C900098C4B2 /* Box.swift */, - 7F85FB8B1C9D317300CEE132 /* SessionAdapterType */, - 7F18BD0D1C972C38003A31DF /* BodyParametersType */, - 7FA19A441C9CC9A2005D25AE /* DataParserType */, + 7F85FB8B1C9D317300CEE132 /* SessionAdapter */, + 7F18BD0D1C972C38003A31DF /* BodyParameters */, + 7FA19A441C9CC9A2005D25AE /* DataParser */, 7F18BD161C9730ED003A31DF /* Serializations */, 7FA19A3D1C9CBF2A005D25AE /* Error */, 7F7E8F1E1C8AD4E6008A13A9 /* Supporting Files */, @@ -275,20 +272,20 @@ path = DataParserType; sourceTree = ""; }; - 7F85FB8B1C9D317300CEE132 /* SessionAdapterType */ = { + 7F85FB8B1C9D317300CEE132 /* SessionAdapter */ = { isa = PBXGroup; children = ( - 7F85FB8D1C9D317300CEE132 /* SessionAdapterType.swift */, - 7F85FB8C1C9D317300CEE132 /* NSURLSessionAdapter.swift */, + 7F85FB8D1C9D317300CEE132 /* SessionAdapter.swift */, + 7F85FB8C1C9D317300CEE132 /* URLSessionAdapter.swift */, ); - path = SessionAdapterType; + path = SessionAdapter; sourceTree = ""; }; 7F85FB901C9D335F00CEE132 /* SessionAdapterType */ = { isa = PBXGroup; children = ( - 7F85FB911C9D336D00CEE132 /* NSURLSessionAdapterTests.swift */, - 7F2001D61CF49F3000C5D0EE /* NSURLSessionAdapterSubclassTests.swift */, + 7F85FB911C9D336D00CEE132 /* URLSessionAdapterTests.swift */, + 7F2001D61CF49F3000C5D0EE /* URLSessionAdapterSubclassTests.swift */, ); path = SessionAdapterType; sourceTree = ""; @@ -324,15 +321,15 @@ path = Error; sourceTree = ""; }; - 7FA19A441C9CC9A2005D25AE /* DataParserType */ = { + 7FA19A441C9CC9A2005D25AE /* DataParser */ = { isa = PBXGroup; children = ( - 7FA19A451C9CC9D0005D25AE /* DataParserType.swift */, + 7FA19A451C9CC9D0005D25AE /* DataParser.swift */, 7F85FB7F1C9CF12600CEE132 /* JSONDataParser.swift */, 7F85FB861C9CF47300CEE132 /* FormURLEncodedDataParser.swift */, 7FB650DE1CEA0E6B00366992 /* StringDataParser.swift */, ); - path = DataParserType; + path = DataParser; sourceTree = ""; }; /* End PBXGroup section */ @@ -446,25 +443,24 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7F7E8F1A1C8AD4B1008A13A9 /* RequestType.swift in Sources */, + 7F7E8F1A1C8AD4B1008A13A9 /* Request.swift in Sources */, 7FA19A331C985CEC005D25AE /* MultipartFormDataBodyParameters.swift in Sources */, 7F85FB871C9CF47300CEE132 /* FormURLEncodedDataParser.swift in Sources */, 7FA19A431C9CBF2A005D25AE /* SessionTaskError.swift in Sources */, 7F7E8F1C1C8AD4B1008A13A9 /* Session.swift in Sources */, - 7F18BD0F1C972C38003A31DF /* BodyParametersType.swift in Sources */, - 7F2A15BD1CEB5F67009A12A2 /* NSData+NSInputStream.swift in Sources */, - 7FAC40341C8F2C900098C4B2 /* Box.swift in Sources */, + 7F18BD0F1C972C38003A31DF /* BodyParameters.swift in Sources */, + 7F2A15BD1CEB5F67009A12A2 /* Data+InputStream.swift in Sources */, 7FB650DF1CEA0E6B00366992 /* StringDataParser.swift in Sources */, 7F18BD131C972E5A003A31DF /* FormURLEncodedBodyParameters.swift in Sources */, - 7FA19A461C9CC9D0005D25AE /* DataParserType.swift in Sources */, + 7FA19A461C9CC9D0005D25AE /* DataParser.swift in Sources */, 7F18BD111C972C69003A31DF /* JSONBodyParameters.swift in Sources */, 7F10D8EE1CD5CF8D00722F66 /* CallbackQueue.swift in Sources */, 7FA19A411C9CBF2A005D25AE /* RequestError.swift in Sources */, - 7F85FB8F1C9D317300CEE132 /* SessionAdapterType.swift in Sources */, + 7F85FB8F1C9D317300CEE132 /* SessionAdapter.swift in Sources */, 7F85FB801C9CF12600CEE132 /* JSONDataParser.swift in Sources */, 7F18BD1A1C9730ED003A31DF /* URLEncodedSerialization.swift in Sources */, 7FAC64AE1CDC7ADE00F1BB45 /* AbstractInputStream.m in Sources */, - 7F85FB8E1C9D317300CEE132 /* NSURLSessionAdapter.swift in Sources */, + 7F85FB8E1C9D317300CEE132 /* URLSessionAdapter.swift in Sources */, 7F7E8F161C8AD4B1008A13A9 /* HTTPMethod.swift in Sources */, 7FA19A421C9CBF2A005D25AE /* ResponseError.swift in Sources */, ); @@ -482,11 +478,11 @@ 7FA19A3A1C98642F005D25AE /* FormURLEncodedBodyParametersTests.swift in Sources */, 7F85FB831C9CF25D00CEE132 /* JSONDataParserTests.swift in Sources */, 7F85FB9F1C9D3F0B00CEE132 /* SessionTests.swift in Sources */, - 7F85FB921C9D336D00CEE132 /* NSURLSessionAdapterTests.swift in Sources */, + 7F85FB921C9D336D00CEE132 /* URLSessionAdapterTests.swift in Sources */, 7F2A537C1CF5D55D000353F0 /* URLEncodedSerializationTests.swift in Sources */, - 7F09BF931C8AE8DB00F4A59A /* RequestTypeTests.swift in Sources */, + 7F09BF931C8AE8DB00F4A59A /* RequestTests.swift in Sources */, 7FA19A3B1C98642F005D25AE /* JSONBodyParametersTests.swift in Sources */, - 7F2001D71CF49F3000C5D0EE /* NSURLSessionAdapterSubclassTests.swift in Sources */, + 7F2001D71CF49F3000C5D0EE /* URLSessionAdapterSubclassTests.swift in Sources */, 7F85FB9A1C9D3DA700CEE132 /* TestRequest.swift in Sources */, 7F85FB9B1C9D3DA700CEE132 /* TestSessionAdapter.swift in Sources */, ); @@ -549,7 +545,7 @@ ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; ONLY_ACTIVE_ARCH = YES; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -558,7 +554,7 @@ baseConfigurationReference = 141F12111C1C96820026D415 /* Release.xcconfig */; buildSettings = { GCC_NO_COMMON_BLOCKS = YES; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/Cartfile b/Cartfile index e1971052..32a661fc 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "antitypical/Result" ~> 2.1.2 +github "antitypical/Result" ~> 3.0 diff --git a/Cartfile.private b/Cartfile.private index db0e286d..c77bf5c5 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1 +1 @@ -github "ikesyo/OHHTTPStubs" "swift2.3" +github "AliSoftware/OHHTTPStubs" "5.2.1-swift3" diff --git a/Cartfile.resolved b/Cartfile.resolved index 3e7580e6..06a1ddec 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ -github "ikesyo/OHHTTPStubs" "b9abbd044ebeecbedf8db10721ed24e4bfe07f29" -github "antitypical/Result" "2.1.2" +github "AliSoftware/OHHTTPStubs" "5.2.1-swift3" +github "antitypical/Result" "3.0.0" diff --git a/Carthage/Checkouts/OHHTTPStubs b/Carthage/Checkouts/OHHTTPStubs index b9abbd04..624d5161 160000 --- a/Carthage/Checkouts/OHHTTPStubs +++ b/Carthage/Checkouts/OHHTTPStubs @@ -1 +1 @@ -Subproject commit b9abbd044ebeecbedf8db10721ed24e4bfe07f29 +Subproject commit 624d51619bc88144015c2b275e761b359ae5ecb3 diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index efa4c04a..df233a8d 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit efa4c04a65ba0af37ca5320993875f73ecb1cfa0 +Subproject commit df233a8d23a545153d5b5de13b1ac8db2503f088 diff --git a/Demo.playground/Contents.swift b/Demo.playground/Contents.swift index 1f856dd9..6bc8ac9b 100644 --- a/Demo.playground/Contents.swift +++ b/Demo.playground/Contents.swift @@ -1,56 +1,56 @@ -import XCPlayground +import PlaygroundSupport import UIKit import APIKit -XCPlaygroundPage.currentPage.needsIndefiniteExecution = true +PlaygroundPage.current.needsIndefiniteExecution = true //: Step 1: Define request protocol -protocol GitHubRequestType: RequestType { +protocol GitHubRequest: Request { } -extension GitHubRequestType { - var baseURL: NSURL { - return NSURL(string: "https://api.github.com")! +extension GitHubRequest { + var baseURL: URL { + return URL(string: "https://api.github.com")! } } //: Step 2: Create model object struct RateLimit { let count: Int - let resetDate: NSDate + let resetDate: Date init?(dictionary: [String: AnyObject]) { guard let count = dictionary["rate"]?["limit"] as? Int else { return nil } - guard let resetDateString = dictionary["rate"]?["reset"] as? NSTimeInterval else { + guard let resetDateString = dictionary["rate"]?["reset"] as? TimeInterval else { return nil } self.count = count - self.resetDate = NSDate(timeIntervalSince1970: resetDateString) + self.resetDate = Date(timeIntervalSince1970: resetDateString) } } //: Step 3: Define request type conforming to created request protocol // https://developer.github.com/v3/rate_limit/ -struct GetRateLimitRequest: GitHubRequestType { +struct GetRateLimitRequest: GitHubRequest { typealias Response = RateLimit var method: HTTPMethod { - return .GET + return .get } var path: String { return "/rate_limit" } - func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response { + func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response { guard let dictionary = object as? [String: AnyObject], let rateLimit = RateLimit(dictionary: dictionary) else { - throw ResponseError.UnexpectedObject(object) + throw ResponseError.unexpectedObject(object) } return rateLimit @@ -60,13 +60,13 @@ struct GetRateLimitRequest: GitHubRequestType { //: Step 4: Send request let request = GetRateLimitRequest() -Session.sendRequest(request) { result in +Session.send(request) { result in switch result { - case .Success(let rateLimit): + case .success(let rateLimit): print("count: \(rateLimit.count)") print("reset: \(rateLimit.resetDate)") - case .Failure(let error): + case .failure(let error): print("error: \(error)") } } diff --git a/Sources/BodyParametersType/AbstractInputStream.m b/Sources/BodyParameters/AbstractInputStream.m similarity index 100% rename from Sources/BodyParametersType/AbstractInputStream.m rename to Sources/BodyParameters/AbstractInputStream.m diff --git a/Sources/BodyParameters/BodyParameters.swift b/Sources/BodyParameters/BodyParameters.swift new file mode 100644 index 00000000..6670ca56 --- /dev/null +++ b/Sources/BodyParameters/BodyParameters.swift @@ -0,0 +1,20 @@ +import Foundation + +/// `RequestBodyEntity` represents entity of HTTP body. +public enum RequestBodyEntity { + /// Expresses entity as `Data`. The associated value will be set to `URLRequest.httpBody`. + case data(Data) + + /// Expresses entity as `InputStream`. The associated value will be set to `URLRequest.httpBodyStream`. + case inputStream(InputStream) +} + +/// `BodyParameters` provides interface to parse HTTP response body and to state `Content-Type` to accept. +public protocol BodyParameters { + /// `Content-Type` to send. The value for this property will be set to `Accept` HTTP header field. + var contentType: String { get } + + /// Builds `RequestBodyEntity`. + /// Throws: `ErrorType` + func buildEntity() throws -> RequestBodyEntity +} diff --git a/Sources/BodyParameters/Data+InputStream.swift b/Sources/BodyParameters/Data+InputStream.swift new file mode 100644 index 00000000..7ef698d8 --- /dev/null +++ b/Sources/BodyParameters/Data+InputStream.swift @@ -0,0 +1,36 @@ +import Foundation + +enum InputStreamError: Error { + case invalidDataCapacity(Int) + case unreadableStream(InputStream) +} + +extension Data { + init(inputStream: InputStream, capacity: Int = Int(UInt16.max)) throws { + var data = Data(capacity: capacity) + + let bufferSize = Swift.min(Int(UInt16.max), capacity) + let buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) + + var readSize: Int + + repeat { + readSize = inputStream.read(buffer, maxLength: bufferSize) + + switch readSize { + case let x where x > 0: + data.append(buffer, count: readSize) + + case let x where x < 0: + throw InputStreamError.unreadableStream(inputStream) + + default: + break + } + } while readSize > 0 + + buffer.deallocate(capacity: bufferSize) + + self.init(data) + } +} diff --git a/Sources/BodyParametersType/FormURLEncodedBodyParameters.swift b/Sources/BodyParameters/FormURLEncodedBodyParameters.swift similarity index 63% rename from Sources/BodyParametersType/FormURLEncodedBodyParameters.swift rename to Sources/BodyParameters/FormURLEncodedBodyParameters.swift index 03334b42..46c3ce5f 100644 --- a/Sources/BodyParametersType/FormURLEncodedBodyParameters.swift +++ b/Sources/BodyParameters/FormURLEncodedBodyParameters.swift @@ -1,29 +1,29 @@ import Foundation /// `FormURLEncodedBodyParameters` serializes form object for HTTP body and states its content type is form. -public struct FormURLEncodedBodyParameters: BodyParametersType { +public struct FormURLEncodedBodyParameters: BodyParameters { /// The form object to be serialized. - public let form: [String: AnyObject] + public let form: [String: Any] /// The string encoding of the serialized form. - public let encoding: NSStringEncoding + public let encoding: String.Encoding /// Returns `FormURLEncodedBodyParameters` that is initialized with form object and encoding. - public init(formObject: [String: AnyObject], encoding: NSStringEncoding = NSUTF8StringEncoding) { + public init(formObject: [String: Any], encoding: String.Encoding = .utf8) { self.form = formObject self.encoding = encoding } - // MARK: - BodyParametersType + // MARK: - BodyParameters /// `Content-Type` to send. The value for this property will be set to `Accept` HTTP header field. public var contentType: String { return "application/x-www-form-urlencoded" } - /// Builds `RequestBodyEntity.Data` that represents `form`. + /// Builds `RequestBodyEntity.data` that represents `form`. /// - Throws: `URLEncodedSerialization.Error` if `URLEncodedSerialization` fails to serialize form object. public func buildEntity() throws -> RequestBodyEntity { - return .Data(try URLEncodedSerialization.dataFromObject(form, encoding: encoding)) + return .data(try URLEncodedSerialization.data(from: form, encoding: encoding)) } } diff --git a/Sources/BodyParametersType/JSONBodyParameters.swift b/Sources/BodyParameters/JSONBodyParameters.swift similarity index 61% rename from Sources/BodyParametersType/JSONBodyParameters.swift rename to Sources/BodyParameters/JSONBodyParameters.swift index 55c33788..3f541917 100644 --- a/Sources/BodyParametersType/JSONBodyParameters.swift +++ b/Sources/BodyParameters/JSONBodyParameters.swift @@ -1,34 +1,34 @@ import Foundation /// `JSONBodyParameters` serializes JSON object for HTTP body and states its content type is JSON. -public struct JSONBodyParameters: BodyParametersType { +public struct JSONBodyParameters: BodyParameters { /// The JSON object to be serialized. - public let JSONObject: AnyObject + public let JSONObject: Any /// The writing options for serialization. - public let writingOptions: NSJSONWritingOptions + public let writingOptions: JSONSerialization.WritingOptions /// Returns `JSONBodyParameters` that is initialized with JSON object and writing options. - public init(JSONObject: AnyObject, writingOptions: NSJSONWritingOptions = []) { + public init(JSONObject: Any, writingOptions: JSONSerialization.WritingOptions = []) { self.JSONObject = JSONObject self.writingOptions = writingOptions } - // MARK: - BodyParametersType + // MARK: - BodyParameters /// `Content-Type` to send. The value for this property will be set to `Accept` HTTP header field. public var contentType: String { return "application/json" } - /// Builds `RequestBodyEntity.Data` that represents `JSONObject`. - /// - Throws: `NSError` if `NSJSONSerialization` fails to serialize `JSONObject`. + /// Builds `RequestBodyEntity.data` that represents `JSONObject`. + /// - Throws: `NSError` if `JSONSerialization` fails to serialize `JSONObject`. public func buildEntity() throws -> RequestBodyEntity { // If isValidJSONObject(_:) is false, dataWithJSONObject(_:options:) throws NSException. - guard NSJSONSerialization.isValidJSONObject(JSONObject) else { + guard JSONSerialization.isValidJSONObject(JSONObject) else { throw NSError(domain: NSCocoaErrorDomain, code: 3840, userInfo: nil) } - return .Data(try NSJSONSerialization.dataWithJSONObject(JSONObject, options: writingOptions)) + return .data(try JSONSerialization.data(withJSONObject: JSONObject, options: writingOptions)) } } diff --git a/Sources/BodyParametersType/MultipartFormDataBodyParameters.swift b/Sources/BodyParameters/MultipartFormDataBodyParameters.swift similarity index 62% rename from Sources/BodyParametersType/MultipartFormDataBodyParameters.swift rename to Sources/BodyParameters/MultipartFormDataBodyParameters.swift index 7e46f9cb..fe671246 100644 --- a/Sources/BodyParametersType/MultipartFormDataBodyParameters.swift +++ b/Sources/BodyParameters/MultipartFormDataBodyParameters.swift @@ -7,43 +7,43 @@ import Foundation #endif /// `FormURLEncodedBodyParameters` serializes array of `Part` for HTTP body and states its content type is multipart/form-data. -public struct MultipartFormDataBodyParameters: BodyParametersType { - /// `EntityType` represents wheather the entity is expressed as `NSData` or `NSInputStream`. +public struct MultipartFormDataBodyParameters: BodyParameters { + /// `EntityType` represents wheather the entity is expressed as `Data` or `InputStream`. public enum EntityType { - /// Expresses the entity as `NSData`, which has faster upload speed and lager memory usage. - case Data + /// Expresses the entity as `Data`, which has faster upload speed and lager memory usage. + case data - /// Expresses the entity as `NSInputStream`, which has smaller memory usage and slower upload speed. - case InputStream + /// Expresses the entity as `InputStream`, which has smaller memory usage and slower upload speed. + case inputStream } public let parts: [Part] public let boundary: String public let entityType: EntityType - public init(parts: [Part], boundary: String = String(format: "%08x%08x", arc4random(), arc4random()), entityType: EntityType = .Data) { + public init(parts: [Part], boundary: String = String(format: "%08x%08x", arc4random(), arc4random()), entityType: EntityType = .data) { self.parts = parts self.boundary = boundary self.entityType = entityType } - // MARK: BodyParametersType + // MARK: BodyParameters /// `Content-Type` to send. The value for this property will be set to `Accept` HTTP header field. public var contentType: String { return "multipart/form-data; boundary=\(boundary)" } - /// Builds `RequestBodyEntity.Data` that represents `form`. + /// Builds `RequestBodyEntity.data` that represents `form`. public func buildEntity() throws -> RequestBodyEntity { let inputStream = MultipartInputStream(parts: parts, boundary: boundary) switch entityType { - case .InputStream: - return .InputStream(inputStream) + case .inputStream: + return .inputStream(inputStream) - case .Data: - return .Data(try NSData(inputStream: inputStream)) + case .data: + return .data(try Data(inputStream: inputStream)) } } } @@ -51,61 +51,60 @@ public struct MultipartFormDataBodyParameters: BodyParametersType { public extension MultipartFormDataBodyParameters { /// Part represents single part of multipart/form-data. public struct Part { - public enum Error: ErrorType { - case IllegalValue(Any) - case IllegalFileURL(NSURL) - case CannotGetFileSize(NSURL) + public enum Error: Swift.Error { + case illegalValue(Any) + case illegalFileURL(URL) + case cannotGetFileSize(URL) } - public let inputStream: NSInputStream + public let inputStream: InputStream public let name: String public let mimeType: String? public let fileName: String? - public let length: Int + public let count: Int /// Returns Part instance that has data presentation of passed value. /// `value` will be converted via `String(_:)` and serialized via `String.dataUsingEncoding(_:)`. /// If `mimeType` or `fileName` are `nil`, the fields will be omitted. - public init(value: Any, name: String, mimeType: String? = nil, fileName: String? = nil, encoding: NSStringEncoding = NSUTF8StringEncoding) throws { - guard let data = String(value).dataUsingEncoding(encoding) else { - throw Error.IllegalValue(value) + public init(value: Any, name: String, mimeType: String? = nil, fileName: String? = nil, encoding: String.Encoding = .utf8) throws { + guard let data = String(describing: value).data(using: encoding) else { + throw Error.illegalValue(value) } - self.inputStream = NSInputStream(data: data) + self.inputStream = InputStream(data: data) self.name = name self.mimeType = mimeType self.fileName = fileName - self.length = data.length + self.count = data.count } /// Returns Part instance that has input stream of specifed data. /// If `mimeType` or `fileName` are `nil`, the fields will be omitted. - public init(data: NSData, name: String, mimeType: String? = nil, fileName: String? = nil) { - self.inputStream = NSInputStream(data: data) + public init(data: Data, name: String, mimeType: String? = nil, fileName: String? = nil) { + self.inputStream = InputStream(data: data) self.name = name self.mimeType = mimeType self.fileName = fileName - self.length = data.length + self.count = data.count } /// Returns Part instance that has input stream of specifed file URL. /// If `mimeType` or `fileName` are `nil`, values for the fields will be detected from URL. - public init(fileURL: NSURL, name: String, mimeType: String? = nil, fileName: String? = nil) throws { - guard let inputStream = NSInputStream(URL: fileURL) else { - throw Error.IllegalFileURL(fileURL) + public init(fileURL: URL, name: String, mimeType: String? = nil, fileName: String? = nil) throws { + guard let inputStream = InputStream(url: fileURL) else { + throw Error.illegalFileURL(fileURL) } - let fileSize = fileURL.path - .flatMap { try? NSFileManager.defaultManager().attributesOfItemAtPath($0) } - .flatMap { $0[NSFileSize] as? NSNumber } - .map { $0.integerValue } + let fileSize = (try? FileManager.default.attributesOfItem(atPath: fileURL.path)) + .flatMap { $0[FileAttributeKey.size] as? NSNumber } + .map { $0.intValue } guard let bodyLength = fileSize else { - throw Error.CannotGetFileSize(fileURL) + throw Error.cannotGetFileSize(fileURL) } - let detectedMimeType = fileURL.pathExtension - .flatMap { UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, $0, nil)?.takeRetainedValue() } + let detectedMimeType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileURL.pathExtension as CFString, nil) + .map { $0.takeRetainedValue() } .flatMap { UTTypeCopyPreferredTagWithClass($0, kUTTagClassMIMEType)?.takeRetainedValue() } .map { $0 as String } @@ -113,13 +112,13 @@ public extension MultipartFormDataBodyParameters { self.name = name self.mimeType = mimeType ?? detectedMimeType ?? "application/octet-stream" self.fileName = fileName ?? fileURL.lastPathComponent - self.length = bodyLength + self.count = bodyLength } } internal class PartInputStream: AbstractInputStream { - let headerData: NSData - let footerData: NSData + let headerData: Data + let footerData: Data let bodyPart: Part let totalLength: Int @@ -138,33 +137,33 @@ public extension MultipartFormDataBodyParameters { header = "--\(boundary)\r\nContent-Disposition: form-data; name=\"\(part.name)\"\r\n\r\n" } - headerData = header.dataUsingEncoding(NSUTF8StringEncoding)! - footerData = "\r\n".dataUsingEncoding(NSUTF8StringEncoding)! + headerData = header.data(using: .utf8)! + footerData = "\r\n".data(using: .utf8)! bodyPart = part - totalLength = headerData.length + bodyPart.length + footerData.length + totalLength = headerData.count + bodyPart.count + footerData.count totalSentLength = 0 super.init() } var headerRange: Range { - return 0.. { - return headerRange.endIndex..<(headerRange.endIndex + bodyPart.length) + return headerRange.upperBound..<(headerRange.upperBound + bodyPart.count) } var footerRange: Range { - return bodyRange.endIndex..<(bodyRange.endIndex + footerData.length) + return bodyRange.upperBound..<(bodyRange.upperBound + footerData.count) } - // MARK: NSInputStream + // MARK: InputStream override var hasBytesAvailable: Bool { return totalSentLength < totalLength } - override func read(buffer: UnsafeMutablePointer, maxLength: Int) -> Int { + override func read(_ buffer: UnsafeMutablePointer, maxLength: Int) -> Int { var sentLength = 0 while sentLength < maxLength && totalSentLength < totalLength { @@ -173,14 +172,14 @@ public extension MultipartFormDataBodyParameters { switch totalSentLength { case headerRange: - let readLength = min(headerRange.endIndex - totalSentLength, availableLength) - let readRange = NSRange(location: totalSentLength - headerRange.startIndex, length: readLength) - headerData.getBytes(offsetBuffer, range: readRange) + let readLength = min(headerRange.upperBound - totalSentLength, availableLength) + let readRange = NSRange(location: totalSentLength - headerRange.lowerBound, length: readLength) + (headerData as NSData).getBytes(offsetBuffer, range: readRange) sentLength += readLength totalSentLength += sentLength case bodyRange: - if bodyPart.inputStream.streamStatus == .NotOpen { + if bodyPart.inputStream.streamStatus == .notOpen { bodyPart.inputStream.open() } @@ -189,14 +188,14 @@ public extension MultipartFormDataBodyParameters { totalSentLength += readLength case footerRange: - let readLength = min(footerRange.endIndex - totalSentLength, availableLength) - let range = NSRange(location: totalSentLength - footerRange.startIndex, length: readLength) - footerData.getBytes(offsetBuffer, range: range) + let readLength = min(footerRange.upperBound - totalSentLength, availableLength) + let range = NSRange(location: totalSentLength - footerRange.lowerBound, length: readLength) + (footerData as NSData).getBytes(offsetBuffer, range: range) sentLength += readLength totalSentLength += readLength default: - print("Illegal range access: \(totalSentLength) is out of \(headerRange.startIndex)..<\(footerRange.endIndex)") + print("Illegal range access: \(totalSentLength) is out of \(headerRange.lowerBound)..<\(footerRange.upperBound)") return -1 } } @@ -208,18 +207,18 @@ public extension MultipartFormDataBodyParameters { internal class MultipartInputStream: AbstractInputStream { let boundary: String let partStreams: [PartInputStream] - let footerData: NSData + let footerData: Data let totalLength: Int var totalSentLength: Int - private var privateStreamStatus = NSStreamStatus.NotOpen + private var privateStreamStatus = Stream.Status.notOpen init(parts: [Part], boundary: String) { self.boundary = boundary self.partStreams = parts.map { PartInputStream(part: $0, boundary: boundary) } - self.footerData = "--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)! - self.totalLength = partStreams.reduce(footerData.length) { $0 + $1.totalLength } + self.footerData = "--\(boundary)--\r\n".data(using: .utf8)! + self.totalLength = partStreams.reduce(footerData.count) { $0 + $1.totalLength } self.totalSentLength = 0 super.init() } @@ -229,7 +228,7 @@ public extension MultipartFormDataBodyParameters { } var footerRange: Range { - return partsRange.endIndex..<(partsRange.endIndex + footerData.length) + return partsRange.upperBound..<(partsRange.upperBound + footerData.count) } var currentPartInputStream: PartInputStream? { @@ -247,9 +246,9 @@ public extension MultipartFormDataBodyParameters { return nil } - // MARK: NSInputStream - // NOTE: NSInputStream does not have its own implementation because it is a class cluster. - override var streamStatus: NSStreamStatus { + // MARK: InputStream + // NOTE: InputStream does not have its own implementation because it is a class cluster. + override var streamStatus: Stream.Status { return privateStreamStatus } @@ -258,15 +257,15 @@ public extension MultipartFormDataBodyParameters { } override func open() { - privateStreamStatus = .Open + privateStreamStatus = .open } override func close() { - privateStreamStatus = .Closed + privateStreamStatus = .closed } - override func read(buffer: UnsafeMutablePointer, maxLength: Int) -> Int { - privateStreamStatus = .Reading + override func read(_ buffer: UnsafeMutablePointer, maxLength: Int) -> Int { + privateStreamStatus = .reading var sentLength = 0 @@ -286,35 +285,35 @@ public extension MultipartFormDataBodyParameters { totalSentLength += readLength case footerRange: - let readLength = min(footerRange.endIndex - totalSentLength, availableLength) - let range = NSRange(location: totalSentLength - footerRange.startIndex, length: readLength) - footerData.getBytes(offsetBuffer, range: range) + let readLength = min(footerRange.upperBound - totalSentLength, availableLength) + let range = NSRange(location: totalSentLength - footerRange.lowerBound, length: readLength) + (footerData as NSData).getBytes(offsetBuffer, range: range) sentLength += readLength totalSentLength += readLength default: - print("Illegal range access: \(totalSentLength) is out of \(partsRange.startIndex)..<\(footerRange.endIndex)") + print("Illegal range access: \(totalSentLength) is out of \(partsRange.lowerBound)..<\(footerRange.upperBound)") return -1 } - if privateStreamStatus != .Closed && !hasBytesAvailable { - privateStreamStatus = .AtEnd + if privateStreamStatus != .closed && !hasBytesAvailable { + privateStreamStatus = .atEnd } } return sentLength } - override var delegate: NSStreamDelegate? { + override var delegate: StreamDelegate? { get { return nil } set { } } - override func scheduleInRunLoop(runLoop: NSRunLoop, forMode mode: String) { + override func schedule(in aRunLoop: RunLoop, forMode mode: RunLoopMode) { } - override func removeFromRunLoop(runLoop: NSRunLoop, forMode mode: String) { + override func remove(from aRunLoop: RunLoop, forMode mode: RunLoopMode) { } } diff --git a/Sources/BodyParametersType/BodyParametersType.swift b/Sources/BodyParametersType/BodyParametersType.swift deleted file mode 100644 index 6ee549bf..00000000 --- a/Sources/BodyParametersType/BodyParametersType.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation - -/// `RequestBodyEntity` represents entity of HTTP body. -public enum RequestBodyEntity { - /// Expresses entity as `NSData`. The associated value will be set to `NSURLRequest.HTTPBody`. - case Data(NSData) - - /// Expresses entity as `NSInputStream`. The associated value will be set to `NSURLRequest.HTTPBodyStream`. - case InputStream(NSInputStream) -} - -/// `BodyParametersType` provides interface to parse HTTP response body and to state `Content-Type` to accept. -public protocol BodyParametersType { - /// `Content-Type` to send. The value for this property will be set to `Accept` HTTP header field. - var contentType: String { get } - - /// Builds `RequestBodyEntity`. - /// Throws: `ErrorType` - func buildEntity() throws -> RequestBodyEntity -} diff --git a/Sources/BodyParametersType/NSData+NSInputStream.swift b/Sources/BodyParametersType/NSData+NSInputStream.swift deleted file mode 100644 index fe722add..00000000 --- a/Sources/BodyParametersType/NSData+NSInputStream.swift +++ /dev/null @@ -1,38 +0,0 @@ -import Foundation - -enum InputStreamError: ErrorType { - case InvalidDataCapacity(Int) - case UnreadableStream(NSInputStream) -} - -extension NSData { - convenience init(inputStream: NSInputStream, capacity: Int = Int(UInt16.max)) throws { - guard let data = NSMutableData(capacity: capacity) else { - throw InputStreamError.InvalidDataCapacity(capacity) - } - - let bufferSize = min(Int(UInt16.max), capacity) - let buffer = UnsafeMutablePointer.alloc(bufferSize) - - var readSize: Int - - repeat { - readSize = inputStream.read(buffer, maxLength: bufferSize) - - switch readSize { - case let x where x > 0: - data.appendBytes(buffer, length: readSize) - - case let x where x < 0: - throw InputStreamError.UnreadableStream(inputStream) - - default: - break - } - } while readSize > 0 - - buffer.dealloc(bufferSize) - - self.init(data: data) - } -} diff --git a/Sources/Box.swift b/Sources/Box.swift deleted file mode 100644 index baacebee..00000000 --- a/Sources/Box.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -internal final class Box { - let value: T - - init(_ value: T) { - self.value = value - } -} diff --git a/Sources/CallbackQueue.swift b/Sources/CallbackQueue.swift index 59352d05..6611eb8d 100644 --- a/Sources/CallbackQueue.swift +++ b/Sources/CallbackQueue.swift @@ -1,36 +1,36 @@ import Foundation -/// `CallbackQueue` represents queue where `handler` of `Session.sendRequest(_:handler:)` runs. +/// `CallbackQueue` represents queue where `handler` of `Session.send(_:handler:)` runs. public enum CallbackQueue { /// Dispatches callback closure on main queue asynchronously. - case Main + case main /// Dispatches callback closure on the queue where backend adapter callback runs. - case SessionQueue + case sessionQueue /// Dispatches callback closure on associated operation queue. - case OperationQueue(NSOperationQueue) + case operationQueue(OperationQueue) /// Dispatches callback closure on associated dispatch queue. - case DispatchQueue(dispatch_queue_t) + case dispatchQueue(DispatchQueue) - internal func execute(closure: () -> Void) { + internal func execute(closure: @escaping () -> Void) { switch self { - case .Main: - dispatch_async(dispatch_get_main_queue()) { + case .main: + DispatchQueue.main.async { closure() } - case .SessionQueue: + case .sessionQueue: closure() - case .OperationQueue(let operationQueue): - operationQueue.addOperationWithBlock { + case .operationQueue(let operationQueue): + operationQueue.addOperation { closure() } - case .DispatchQueue(let dispatchQueue): - dispatch_async(dispatchQueue) { + case .dispatchQueue(let dispatchQueue): + dispatchQueue.async { closure() } } diff --git a/Sources/DataParser/DataParser.swift b/Sources/DataParser/DataParser.swift new file mode 100644 index 00000000..1722bb51 --- /dev/null +++ b/Sources/DataParser/DataParser.swift @@ -0,0 +1,11 @@ +import Foundation + +/// `DataParser` protocol provides inteface to parse HTTP response body and to state Content-Type to accept. +public protocol DataParser { + /// Value for `Accept` header field of HTTP request. + var contentType: String? { get } + + /// Return `Any` that expresses structure of response such as JSON and XML. + /// - Throws: `ErrorType` when parser encountered invalid format data. + func parse(data: Data) throws -> Any +} diff --git a/Sources/DataParserType/FormURLEncodedDataParser.swift b/Sources/DataParser/FormURLEncodedDataParser.swift similarity index 52% rename from Sources/DataParserType/FormURLEncodedDataParser.swift rename to Sources/DataParser/FormURLEncodedDataParser.swift index 536033b0..fc46457e 100644 --- a/Sources/DataParserType/FormURLEncodedDataParser.swift +++ b/Sources/DataParser/FormURLEncodedDataParser.swift @@ -1,38 +1,38 @@ import Foundation /// `FormURLEncodedDataParser` parses form URL encoded response data. -public class FormURLEncodedDataParser: DataParserType { - public enum Error: ErrorType { - case CannotGetStringFromData(NSData) +public class FormURLEncodedDataParser: DataParser { + public enum Error: Swift.Error { + case cannotGetStringFromData(Data) } /// The string encoding of the data. - public let encoding: NSStringEncoding + public let encoding: String.Encoding /// Returns `FormURLEncodedDataParser` with the string encoding. - public init(encoding: NSStringEncoding) { + public init(encoding: String.Encoding) { self.encoding = encoding } - // MARK: - DataParserType + // MARK: - DataParser /// Value for `Accept` header field of HTTP request. public var contentType: String? { return "application/x-www-form-urlencoded" } - /// Return `AnyObject` that expresses structure of response. - /// - Throws: `FormURLEncodedDataParser.Error` when the parser fails to initialize `NSString` from `NSData`. - public func parseData(data: NSData) throws -> AnyObject { - guard let string = NSString(data: data, encoding: encoding) as? String else { - throw Error.CannotGetStringFromData(data) + /// Return `Any` that expresses structure of response. + /// - Throws: `FormURLEncodedDataParser.Error` when the parser fails to initialize `String` from `Data`. + public func parse(data: Data) throws -> Any { + guard let string = String(data: data, encoding: encoding) else { + throw Error.cannotGetStringFromData(data) } - let components = NSURLComponents() + var components = URLComponents() components.percentEncodedQuery = string let queryItems = components.queryItems ?? [] - var dictionary = [String: AnyObject]() + var dictionary = [String: Any]() for queryItem in queryItems { dictionary[queryItem.name] = queryItem.value diff --git a/Sources/DataParser/JSONDataParser.swift b/Sources/DataParser/JSONDataParser.swift new file mode 100644 index 00000000..0b28e98a --- /dev/null +++ b/Sources/DataParser/JSONDataParser.swift @@ -0,0 +1,29 @@ +import Foundation + +/// `JSONDataParser` response JSON data. +public class JSONDataParser: DataParser { + /// Options for reading the JSON data and creating the objects. + public let readingOptions: JSONSerialization.ReadingOptions + + /// Returns `JSONDataParser` with the reading options. + public init(readingOptions: JSONSerialization.ReadingOptions) { + self.readingOptions = readingOptions + } + + // MARK: - DataParser + + /// Value for `Accept` header field of HTTP request. + public var contentType: String? { + return "application/json" + } + + /// Return `Any` that expresses structure of JSON response. + /// - Throws: `NSError` when `JSONSerialization` fails to deserialize `Data` into `Any`. + public func parse(data: Data) throws -> Any { + guard data.count > 0 else { + return [:] + } + + return try JSONSerialization.jsonObject(with: data, options: readingOptions) + } +} diff --git a/Sources/DataParser/StringDataParser.swift b/Sources/DataParser/StringDataParser.swift new file mode 100644 index 00000000..6ae648f6 --- /dev/null +++ b/Sources/DataParser/StringDataParser.swift @@ -0,0 +1,33 @@ +import Foundation + +/// `StringDataParser` parses data and convert it to string. +public class StringDataParser: DataParser { + public enum Error: Swift.Error { + case invalidData(Data) + } + + /// The string encoding of the data. + public let encoding: String.Encoding + + /// Returns `StringDataParser` with the string encoding. + public init(encoding: String.Encoding = .utf8) { + self.encoding = encoding + } + + // MARK: - DataParser + + /// Value for `Accept` header field of HTTP request. + public var contentType: String? { + return nil + } + + /// Return `String` that converted from `Data`. + /// - Throws: `StringDataParser.Error` when the parser fails to initialize `String` from `Data`. + public func parse(data: Data) throws -> Any { + guard let string = String(data: data, encoding: encoding) else { + throw Error.invalidData(data) + } + + return string + } +} diff --git a/Sources/DataParserType/DataParserType.swift b/Sources/DataParserType/DataParserType.swift deleted file mode 100644 index 0777043b..00000000 --- a/Sources/DataParserType/DataParserType.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -/// `DataParserType` protocol provides inteface to parse HTTP response body and to state Content-Type to accept. -public protocol DataParserType { - /// Value for `Accept` header field of HTTP request. - var contentType: String? { get } - - /// Return `AnyObject` that expresses structure of response such as JSON and XML. - /// - Throws: `ErrorType` when parser encountered invalid format data. - func parseData(data: NSData) throws -> AnyObject -} diff --git a/Sources/DataParserType/JSONDataParser.swift b/Sources/DataParserType/JSONDataParser.swift deleted file mode 100644 index 227ea1fd..00000000 --- a/Sources/DataParserType/JSONDataParser.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation - -/// `JSONDataParser` response JSON data. -public class JSONDataParser: DataParserType { - /// Options for reading the JSON data and creating the objects. - public let readingOptions: NSJSONReadingOptions - - /// Returns `JSONDataParser` with the reading options. - public init(readingOptions: NSJSONReadingOptions) { - self.readingOptions = readingOptions - } - - // MARK: - DataParserType - - /// Value for `Accept` header field of HTTP request. - public var contentType: String? { - return "application/json" - } - - /// Return `AnyObject` that expresses structure of JSON response. - /// - Throws: `NSError` when `NSJSONSerialization` fails to deserialize `NSData` into `AnyObject`. - public func parseData(data: NSData) throws -> AnyObject { - guard data.length > 0 else { - return [:] - } - - return try NSJSONSerialization.JSONObjectWithData(data, options: readingOptions) - } -} diff --git a/Sources/DataParserType/StringDataParser.swift b/Sources/DataParserType/StringDataParser.swift deleted file mode 100644 index 21d08d30..00000000 --- a/Sources/DataParserType/StringDataParser.swift +++ /dev/null @@ -1,33 +0,0 @@ -import Foundation - -/// `StringDataParser` parses data and convert it to string. -public class StringDataParser: DataParserType { - public enum Error: ErrorType { - case InvalidData(NSData) - } - - /// The string encoding of the data. - public let encoding: NSStringEncoding - - /// Returns `StringDataParser` with the string encoding. - public init(encoding: NSStringEncoding = NSUTF8StringEncoding) { - self.encoding = encoding - } - - // MARK: - DataParserType - - /// Value for `Accept` header field of HTTP request. - public var contentType: String? { - return nil - } - - /// Return `String` that converted from `NSData`. - /// - Throws: `StringDataParser.Error` when the parser fails to initialize `NSString` from `NSData`. - public func parseData(data: NSData) throws -> AnyObject { - guard let string = NSString(data: data, encoding: encoding) else { - throw Error.InvalidData(data) - } - - return string - } -} diff --git a/Sources/Error/RequestError.swift b/Sources/Error/RequestError.swift index 222ff38d..59be47bd 100644 --- a/Sources/Error/RequestError.swift +++ b/Sources/Error/RequestError.swift @@ -1,10 +1,10 @@ import Foundation -/// `RequestError` represents a common error that occurs while building `NSURLRequest` from `RequestType`. -public enum RequestError: ErrorType { - /// Indicates `baseURL` of a type that conforms `RequestType` is invalid. - case InvalidBaseURL(NSURL) +/// `RequestError` represents a common error that occurs while building `URLRequest` from `Request`. +public enum RequestError: Error { + /// Indicates `baseURL` of a type that conforms `Request` is invalid. + case invalidBaseURL(URL) - /// Indicates `NSURLRequest` built by `RequestType.buildURLRequest` is unexpected. - case UnexpectedURLRequest(NSURLRequest) + /// Indicates `URLRequest` built by `Request.buildURLRequest` is unexpected. + case unexpectedURLRequest(URLRequest) } diff --git a/Sources/Error/ResponseError.swift b/Sources/Error/ResponseError.swift index ef0a5c72..50efdecd 100644 --- a/Sources/Error/ResponseError.swift +++ b/Sources/Error/ResponseError.swift @@ -1,15 +1,15 @@ import Foundation -/// `ResponseError` represents a common error that occurs while getting `RequestType.Response` -/// from raw result tuple `(NSData?, NSURLResponse?, NSError?)`. -public enum ResponseError: ErrorType { - /// Indicates the session adapter returned `NSURLResponse` that fails to down-cast to `NSHTTPURLResponse`. - case NonHTTPURLResponse(NSURLResponse?) +/// `ResponseError` represents a common error that occurs while getting `Request.Response` +/// from raw result tuple `(Data?, URLResponse?, Error?)`. +public enum ResponseError: Error { + /// Indicates the session adapter returned `URLResponse` that fails to down-cast to `HTTPURLResponse`. + case nonHTTPURLResponse(URLResponse?) - /// Indicates `NSHTTPURLResponse.statusCode` is not acceptable. + /// Indicates `HTTPURLResponse.statusCode` is not acceptable. /// In most cases, *acceptable* means the value is in `200..<300`. - case UnacceptableStatusCode(Int) + case unacceptableStatusCode(Int) - /// Indicates `AnyObject` that represents the response is unexpected. - case UnexpectedObject(AnyObject) + /// Indicates `Any` that represents the response is unexpected. + case unexpectedObject(Any) } diff --git a/Sources/Error/SessionTaskError.swift b/Sources/Error/SessionTaskError.swift index 7a31fa4c..cb9eb929 100644 --- a/Sources/Error/SessionTaskError.swift +++ b/Sources/Error/SessionTaskError.swift @@ -1,13 +1,13 @@ import Foundation /// `SessionTaskError` represents an error that occurs while task for a request. -public enum SessionTaskError: ErrorType { - /// Error of `NSURLSession`. - case ConnectionError(ErrorType) +public enum SessionTaskError: Error { + /// Error of `URLSession`. + case connectionError(Error) - /// Error while creating `NSURLReqeust` from `Request`. - case RequestError(ErrorType) + /// Error while creating `URLReqeust` from `Request`. + case requestError(Error) - /// Error while creating `RequestType.Response` from `(NSData, NSURLResponse)`. - case ResponseError(ErrorType) + /// Error while creating `Request.Response` from `(Data, URLResponse)`. + case responseError(Error) } diff --git a/Sources/HTTPMethod.swift b/Sources/HTTPMethod.swift index d6ceaa1d..6a29f58e 100644 --- a/Sources/HTTPMethod.swift +++ b/Sources/HTTPMethod.swift @@ -2,20 +2,20 @@ import Foundation /// `HTTPMethod` represents HTTP methods. public enum HTTPMethod: String { - case GET - case POST - case PUT - case HEAD - case DELETE - case PATCH - case TRACE - case OPTIONS - case CONNECT + case get = "GET" + case post = "POST" + case put = "PUT" + case head = "HEAD" + case delete = "DELETE" + case patch = "PATCH" + case trace = "TRACE" + case options = "OPTIONS" + case connect = "CONNECT" /// Indicates if the query parameters are suitable for parameters. public var prefersQueryParameters: Bool { switch self { - case .GET, .HEAD, .DELETE: + case .get, .head, .delete: return true default: diff --git a/Sources/Request.swift b/Sources/Request.swift new file mode 100644 index 00000000..30610937 --- /dev/null +++ b/Sources/Request.swift @@ -0,0 +1,148 @@ +import Foundation +import Result + +/// `Request` protocol represents a request for Web API. +/// Following 5 items must be implemented. +/// - `typealias Response` +/// - `var baseURL: URL` +/// - `var method: HTTPMethod` +/// - `var path: String` +/// - `func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response` +public protocol Request { + /// The response type associated with the request type. + associatedtype Response + + /// The base URL. + var baseURL: URL { get } + + /// The HTTP request method. + var method: HTTPMethod { get } + + /// The path URL component. + var path: String { get } + + /// The convenience property for `queryParameters` and `bodyParameters`. If the implementation of + /// `queryParameters` and `bodyParameters` are not provided, the values for them will be computed + /// from this property depending on `method`. + var parameters: Any? { get } + + /// The actual parameters for the URL query. The values of this property will be escaped using `URLEncodedSerialization`. + /// If this property is not implemented and `method.prefersQueryParameter` is `true`, the value of this property + /// will be computed from `parameters`. + var queryParameters: [String: Any]? { get } + + /// The actual parameters for the HTTP body. If this property is not implemented and `method.prefersQueryParameter` is `false`, + /// the value of this property will be computed from `parameters` using `JSONBodyParameters`. + var bodyParameters: BodyParameters? { get } + + /// The HTTP header fields. In addition to fields defined in this property, `Accept` and `Content-Type` + /// fields will be added by `dataParser` and `bodyParameters`. If you define `Accept` and `Content-Type` + /// in this property, the values in this property are preferred. + var headerFields: [String: String] { get } + + /// The parser object that states `Content-Type` to accept and parses response body. + var dataParser: DataParser { get } + + /// Intercepts `URLRequest` which is created by `Request.buildURLRequest()`. If an error is + /// thrown in this method, the result of `Session.send()` turns `.failure(.requestError(error))`. + /// - Throws: `ErrorType` + func intercept(urlRequest: URLRequest) throws -> URLRequest + + /// Intercepts response `Any` and `HTTPURLResponse`. If an error is thrown in this method, + /// the result of `Session.send()` turns `.failure(.responseError(error))`. + /// The default implementation of this method is provided to throw `RequestError.unacceptableStatusCode` + /// if the HTTP status code is not in `200..<300`. + /// - Throws: `ErrorType` + func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any + + /// Build `Response` instance from raw response object. This method is called after + /// `intercept(object:urlResponse:)` if it does not throw any error. + /// - Throws: `ErrorType` + func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response +} + +public extension Request { + public var parameters: Any? { + return nil + } + + public var queryParameters: [String: Any]? { + guard let parameters = parameters as? [String: Any], method.prefersQueryParameters else { + return nil + } + + return parameters + } + + public var bodyParameters: BodyParameters? { + guard let parameters = parameters, !method.prefersQueryParameters else { + return nil + } + + return JSONBodyParameters(JSONObject: parameters) + } + + public var headerFields: [String: String] { + return [:] + } + + public var dataParser: DataParser { + return JSONDataParser(readingOptions: []) + } + + public func intercept(urlRequest: URLRequest) throws -> URLRequest { + return urlRequest + } + + public func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any { + guard (200..<300).contains(urlResponse.statusCode) else { + throw ResponseError.unacceptableStatusCode(urlResponse.statusCode) + } + return object + } + + /// Builds `URLRequest` from properties of `self`. + /// - Throws: `RequestError`, `ErrorType` + public func buildURLRequest() throws -> URLRequest { + let url = path.isEmpty ? baseURL : baseURL.appendingPathComponent(path) + guard var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { + throw RequestError.invalidBaseURL(baseURL) + } + + var urlRequest = URLRequest(url: url) + + if let queryParameters = queryParameters, !queryParameters.isEmpty { + components.percentEncodedQuery = URLEncodedSerialization.string(from: queryParameters) + } + + if let bodyParameters = bodyParameters { + urlRequest.setValue(bodyParameters.contentType, forHTTPHeaderField: "Content-Type") + + switch try bodyParameters.buildEntity() { + case .data(let data): + urlRequest.httpBody = data + + case .inputStream(let inputStream): + urlRequest.httpBodyStream = inputStream + } + } + + urlRequest.url = components.url + urlRequest.httpMethod = method.rawValue + urlRequest.setValue(dataParser.contentType, forHTTPHeaderField: "Accept") + + headerFields.forEach { key, value in + urlRequest.setValue(value, forHTTPHeaderField: key) + } + + return (try intercept(urlRequest: urlRequest) as URLRequest) + } + + /// Builds `Response` from response `Data`. + /// - Throws: `ResponseError`, `ErrorType` + public func parse(data: Data, urlResponse: HTTPURLResponse) throws -> Response { + let parsedObject = try dataParser.parse(data: data) + let passedObject = try intercept(object: parsedObject, urlResponse: urlResponse) + return try response(from: passedObject, urlResponse: urlResponse) + } +} diff --git a/Sources/RequestType.swift b/Sources/RequestType.swift deleted file mode 100644 index 04a05c1a..00000000 --- a/Sources/RequestType.swift +++ /dev/null @@ -1,154 +0,0 @@ -import Foundation -import Result - -/// `RequestType` protocol represents a request for Web API. -/// Following 5 items must be implemented. -/// - `typealias Response` -/// - `var baseURL: NSURL` -/// - `var method: HTTPMethod` -/// - `var path: String` -/// - `func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response` -public protocol RequestType { - /// The response type associated with the request type. - associatedtype Response - - /// The base URL. - var baseURL: NSURL { get } - - /// The HTTP request method. - var method: HTTPMethod { get } - - /// The path URL component. - var path: String { get } - - /// The convenience property for `queryParameters` and `bodyParameters`. If the implementation of - /// `queryParameters` and `bodyParameters` are not provided, the values for them will be computed - /// from this property depending on `method`. - var parameters: AnyObject? { get } - - /// The actual parameters for the URL query. The values of this property will be escaped using `URLEncodedSerialization`. - /// If this property is not implemented and `method.prefersQueryParameter` is `true`, the value of this property - /// will be computed from `parameters`. - var queryParameters: [String: AnyObject]? { get } - - /// The actual parameters for the HTTP body. If this property is not implemented and `method.prefersQueryParameter` is `false`, - /// the value of this property will be computed from `parameters` using `JSONBodyParameters`. - var bodyParameters: BodyParametersType? { get } - - /// The HTTP header fields. In addition to fields defined in this property, `Accept` and `Content-Type` - /// fields will be added by `dataParser` and `bodyParameters`. If you define `Accept` and `Content-Type` - /// in this property, the values in this property are preferred. - var headerFields: [String: String] { get } - - /// The parser object that states `Content-Type` to accept and parses response body. - var dataParser: DataParserType { get } - - /// Intercepts `NSURLRequest` which is created by `RequestType.buildURLRequest()`. If an error is - /// thrown in this method, the result of `Session.sendRequest()` turns `.Failure(.RequestError(error))`. - /// - Throws: `ErrorType` - func interceptURLRequest(URLRequest: NSMutableURLRequest) throws -> NSMutableURLRequest - - /// Intercepts response `AnyObject` and `NSHTTPURLResponse`. If an error is thrown in this method, - /// the result of `Session.sendRequest()` turns `.Failure(.ResponseError(error))`. - /// The default implementation of this method is provided to throw `RequestError.UnacceptableStatusCode` - /// if the HTTP status code is not in `200..<300`. - /// - Throws: `ErrorType` - func interceptObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> AnyObject - - /// Build `Response` instance from raw response object. This method is called after - /// `interceptObject(:URLResponse:)` if it does not throw any error. - /// - Throws: `ErrorType` - func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response -} - -public extension RequestType { - public var parameters: AnyObject? { - return nil - } - - public var queryParameters: [String: AnyObject]? { - guard let parameters = parameters as? [String: AnyObject] where method.prefersQueryParameters else { - return nil - } - - return parameters - } - - public var bodyParameters: BodyParametersType? { - guard let parameters = parameters where !method.prefersQueryParameters else { - return nil - } - - return JSONBodyParameters(JSONObject: parameters) - } - - public var headerFields: [String: String] { - return [:] - } - - public var dataParser: DataParserType { - return JSONDataParser(readingOptions: []) - } - - public func interceptURLRequest(URLRequest: NSMutableURLRequest) throws -> NSMutableURLRequest { - return URLRequest - } - - public func interceptObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> AnyObject { - guard (200..<300).contains(URLResponse.statusCode) else { - throw ResponseError.UnacceptableStatusCode(URLResponse.statusCode) - } - return object - } - - /// Builds `NSURLRequest` from properties of `self`. - /// - Throws: `RequestError`, `ErrorType` - public func buildURLRequest() throws -> NSURLRequest { - let URL = path.isEmpty ? baseURL : baseURL.URLByAppendingPathComponent(path) - #if swift(>=2.3) - guard let unwrapped = URL, components = NSURLComponents(URL: unwrapped, resolvingAgainstBaseURL: true) else { - throw RequestError.InvalidBaseURL(baseURL) - } - #else - guard let components = NSURLComponents(URL: URL, resolvingAgainstBaseURL: true) else { - throw RequestError.InvalidBaseURL(baseURL) - } - #endif - - let URLRequest = NSMutableURLRequest() - - if let queryParameters = queryParameters where !queryParameters.isEmpty { - components.percentEncodedQuery = URLEncodedSerialization.stringFromDictionary(queryParameters) - } - - if let bodyParameters = bodyParameters { - URLRequest.setValue(bodyParameters.contentType, forHTTPHeaderField: "Content-Type") - - switch try bodyParameters.buildEntity() { - case .Data(let data): - URLRequest.HTTPBody = data - - case .InputStream(let inputStream): - URLRequest.HTTPBodyStream = inputStream - } - } - - URLRequest.URL = components.URL - URLRequest.HTTPMethod = method.rawValue - URLRequest.setValue(dataParser.contentType, forHTTPHeaderField: "Accept") - - headerFields.forEach { key, value in - URLRequest.setValue(value, forHTTPHeaderField: key) - } - - return (try interceptURLRequest(URLRequest)) - } - - /// Builds `Response` from response `NSData`. - /// - Throws: `ResponseError`, `ErrorType` - public func parseData(data: NSData, URLResponse: NSHTTPURLResponse) throws -> Response { - let parsedObject = try dataParser.parseData(data) - let passedObject = try interceptObject(parsedObject, URLResponse: URLResponse) - return try responseFromObject(passedObject, URLResponse: URLResponse) - } -} diff --git a/Sources/Serializations/URLEncodedSerialization.swift b/Sources/Serializations/URLEncodedSerialization.swift index 7d8351c2..48d000d8 100644 --- a/Sources/Serializations/URLEncodedSerialization.swift +++ b/Sources/Serializations/URLEncodedSerialization.swift @@ -1,15 +1,15 @@ import Foundation -private func escape(string: String) -> String { +private func escape(_ string: String) -> String { // Reserved characters defined by RFC 3986 // Reference: https://www.ietf.org/rfc/rfc3986.txt let generalDelimiters = ":#[]@" let subDelimiters = "!$&'()*+,;=" let reservedCharacters = generalDelimiters + subDelimiters - let allowedCharacterSet = NSMutableCharacterSet() - allowedCharacterSet.formUnionWithCharacterSet(NSCharacterSet.URLQueryAllowedCharacterSet()) - allowedCharacterSet.removeCharactersInString(reservedCharacters) + var allowedCharacterSet = CharacterSet() + allowedCharacterSet.formUnion(.urlQueryAllowed) + allowedCharacterSet.remove(charactersIn: reservedCharacters) // Crashes due to internal bug in iOS 7 ~ iOS 8.2. // References: @@ -24,12 +24,12 @@ private func escape(string: String) -> String { while index != string.endIndex { let startIndex = index - let endIndex = index.advancedBy(batchSize, limit: string.endIndex) + let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex let range = startIndex.. String { return escaped } -private func unescape(string: String) -> String { - return CFURLCreateStringByReplacingPercentEscapes(nil, string, nil) as String +private func unescape(_ string: String) -> String { + return CFURLCreateStringByReplacingPercentEscapes(nil, string as CFString, nil) as String } -/// `URLEncodedSerialization` parses `NSData` and `String` as urlencoded, +/// `URLEncodedSerialization` parses `Data` and `String` as urlencoded, /// and returns dictionary that represents the data or the string. public final class URLEncodedSerialization { - public enum Error: ErrorType { - case CannotGetStringFromData(NSData, NSStringEncoding) - case CannotGetDataFromString(String, NSStringEncoding) - case CannotCastObjectToDictionary(AnyObject) - case InvalidFormatString(String) + public enum Error: Swift.Error { + case cannotGetStringFromData(Data, String.Encoding) + case cannotGetDataFromString(String, String.Encoding) + case cannotCastObjectToDictionary(Any) + case invalidFormatString(String) } - /// Returns `[String: String]` that represents urlencoded `NSData`. + /// Returns `[String: String]` that represents urlencoded `Data`. /// - Throws: URLEncodedSerialization.Error - public static func objectFromData(data: NSData, encoding: NSStringEncoding) throws -> [String: String] { + public static func object(from data: Data, encoding: String.Encoding) throws -> [String: String] { guard let string = String(data: data, encoding: encoding) else { - throw Error.CannotGetStringFromData(data, encoding) + throw Error.cannotGetStringFromData(data, encoding) } var dictionary = [String: String]() - for pair in string.componentsSeparatedByString("&") { - let contents = pair.componentsSeparatedByString("=") + for pair in string.components(separatedBy: "&") { + let contents = pair.components(separatedBy: "=") guard contents.count == 2 else { - throw Error.InvalidFormatString(string) + throw Error.invalidFormatString(string) } dictionary[contents[0]] = unescape(contents[1]) @@ -72,23 +72,23 @@ public final class URLEncodedSerialization { return dictionary } - /// Returns urlencoded `NSData` from the object. + /// Returns urlencoded `Data` from the object. /// - Throws: URLEncodedSerialization.Error - public static func dataFromObject(object: AnyObject, encoding: NSStringEncoding) throws -> NSData { - guard let dictionary = object as? [String: AnyObject] else { - throw Error.CannotCastObjectToDictionary(object) + public static func data(from object: Any, encoding: String.Encoding) throws -> Data { + guard let dictionary = object as? [String: Any] else { + throw Error.cannotCastObjectToDictionary(object) } - let string = stringFromDictionary(dictionary) - guard let data = string.dataUsingEncoding(encoding, allowLossyConversion: false) else { - throw Error.CannotGetDataFromString(string, encoding) + let string = self.string(from: dictionary) + guard let data = string.data(using: encoding, allowLossyConversion: false) else { + throw Error.cannotGetDataFromString(string, encoding) } return data } - /// Returns urlencoded `NSData` from the string. - public static func stringFromDictionary(dictionary: [String: AnyObject]) -> String { + /// Returns urlencoded `Data` from the string. + public static func string(from dictionary: [String: Any]) -> String { let pairs = dictionary.map { key, value -> String in if value is NSNull { return "\(escape(key))" @@ -98,6 +98,6 @@ public final class URLEncodedSerialization { return "\(escape(key))=\(escape(valueAsString))" } - return pairs.joinWithSeparator("&") + return pairs.joined(separator: "&") } } diff --git a/Sources/Session.swift b/Sources/Session.swift index 5e91ba44..b557652c 100644 --- a/Sources/Session.swift +++ b/Sources/Session.swift @@ -4,84 +4,86 @@ import Result private var taskRequestKey = 0 /// `Session` manages tasks for HTTP/HTTPS requests. -public class Session { +open class Session { /// The adapter that connects `Session` instance and lower level backend. - public let adapter: SessionAdapterType + public let adapter: SessionAdapter - /// The default callback queue for `sendRequest(_:handler:)`. + /// The default callback queue for `send(_:handler:)`. public let callbackQueue: CallbackQueue /// Returns `Session` instance that is initialized with `adapter`. /// - parameter adapter: The adapter that connects lower level backend with Session interface. - /// - parameter callbackQueue: The default callback queue for `sendRequest(_:handler:)`. - public init(adapter: SessionAdapterType, callbackQueue: CallbackQueue = .Main) { + /// - parameter callbackQueue: The default callback queue for `send(_:handler:)`. + public init(adapter: SessionAdapter, callbackQueue: CallbackQueue = .main) { self.adapter = adapter self.callbackQueue = callbackQueue } // Shared session for class methods private static let privateSharedSession: Session = { - let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() - let adapter = NSURLSessionAdapter(configuration: configuration) + let configuration = URLSessionConfiguration.default + let adapter = URLSessionAdapter(configuration: configuration) return Session(adapter: adapter) }() - /// The shared `Session` instance for class methods, `Session.sendRequest(_:handler:)` and `Session.cancelRequest(_:passingTest:)`. - public class var sharedSession: Session { + /// The shared `Session` instance for class methods, `Session.send(_:handler:)` and `Session.cancelRequests(withType:passingTest:)`. + open class var sharedSession: Session { return privateSharedSession } - /// Calls `sendRequest(_:handler:)` of `sharedSession`. + /// Calls `send(_:handler:)` of `sharedSession`. /// - parameter request: The request to be sent. /// - parameter callbackQueue: The queue where the handler runs. If this parameters is `nil`, default `callbackQueue` of `Session` will be used. /// - parameter handler: The closure that receives result of the request. /// - returns: The new session task. - public class func sendRequest(request: Request, callbackQueue: CallbackQueue? = nil, handler: (Result) -> Void = { _ in }) -> SessionTaskType? { - return sharedSession.sendRequest(request, callbackQueue: callbackQueue, handler: handler) + @discardableResult + open class func send(_ request: Req, callbackQueue: CallbackQueue? = nil, handler: @escaping (Result) -> Void = { _ in }) -> SessionTaskType? { + return sharedSession.send(request, callbackQueue: callbackQueue, handler: handler) } - /// Calls `cancelRequest(_:passingTest:)` of `sharedSession`. - public class func cancelRequest(requestType: Request.Type, passingTest test: Request -> Bool = { _ in true }) { - sharedSession.cancelRequest(requestType, passingTest: test) + /// Calls `cancelRequests(withType:passingTest:)` of `sharedSession`. + open class func cancelRequests(withType requestType: Req.Type, passingTest test: @escaping (Req) -> Bool = { _ in true }) { + sharedSession.cancelRequests(withType: requestType, passingTest: test) } /// Sends a request and receives the result as the argument of `handler` closure. This method takes - /// a type parameter `Request` that conforms to `RequestType` protocol. The result of passed request is + /// a type parameter `Request` that conforms to `Request` protocol. The result of passed request is /// expressed as `Result`. Since the response type /// `Request.Response` is inferred from `Request` type parameter, the it changes depending on the request type. /// - parameter request: The request to be sent. /// - parameter callbackQueue: The queue where the handler runs. If this parameters is `nil`, default `callbackQueue` of `Session` will be used. /// - parameter handler: The closure that receives result of the request. /// - returns: The new session task. - public func sendRequest(request: Request, callbackQueue: CallbackQueue? = nil, handler: (Result) -> Void = { _ in }) -> SessionTaskType? { + @discardableResult + open func send(_ request: Req, callbackQueue: CallbackQueue? = nil, handler: @escaping (Result) -> Void = { _ in }) -> SessionTaskType? { let callbackQueue = callbackQueue ?? self.callbackQueue - let URLRequest: NSURLRequest + let urlRequest: URLRequest do { - URLRequest = try request.buildURLRequest() + urlRequest = try request.buildURLRequest() } catch { callbackQueue.execute { - handler(.Failure(.RequestError(error))) + handler(.failure(.requestError(error))) } return nil } - let task = adapter.createTaskWithURLRequest(URLRequest) { data, URLResponse, error in - let result: Result + let task = adapter.createTask(with: urlRequest) { data, urlResponse, error in + let result: Result - switch (data, URLResponse, error) { + switch (data, urlResponse, error) { case (_, _, let error?): - result = .Failure(.ConnectionError(error)) + result = .failure(.connectionError(error)) - case (let data?, let URLResponse as NSHTTPURLResponse, _): + case (let data?, let urlResponse as HTTPURLResponse, _): do { - result = .Success(try request.parseData(data, URLResponse: URLResponse)) + result = .success(try request.parse(data: data as Data, urlResponse: urlResponse)) } catch { - result = .Failure(.ResponseError(error)) + result = .failure(.responseError(error)) } default: - result = .Failure(.ResponseError(ResponseError.NonHTTPURLResponse(URLResponse))) + result = .failure(.responseError(ResponseError.nonHTTPURLResponse(urlResponse))) } callbackQueue.execute { @@ -98,11 +100,11 @@ public class Session { /// Cancels requests that passes the test. /// - parameter requestType: The request type to cancel. /// - parameter test: The test closure that determines if a request should be cancelled or not. - public func cancelRequest(requestType: Request.Type, passingTest test: Request -> Bool = { _ in true }) { - adapter.getTasksWithHandler { [weak self] tasks in + open func cancelRequests(withType requestType: Req.Type, passingTest test: @escaping (Req) -> Bool = { _ in true }) { + adapter.getTasks { [weak self] tasks in return tasks .filter { task in - if let request = self?.requestForTask(task) as Request? { + if let request = self?.requestForTask(task) as Req? { return test(request) } else { return false @@ -112,11 +114,11 @@ public class Session { } } - private func setRequest(request: Request, forTask task: SessionTaskType) { - objc_setAssociatedObject(task, &taskRequestKey, Box(request), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + private func setRequest(_ request: Req, forTask task: SessionTaskType) { + objc_setAssociatedObject(task, &taskRequestKey, request, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } - private func requestForTask(task: SessionTaskType) -> Request? { - return (objc_getAssociatedObject(task, &taskRequestKey) as? Box)?.value + private func requestForTask(_ task: SessionTaskType) -> Req? { + return objc_getAssociatedObject(task, &taskRequestKey) as? Req } } diff --git a/Sources/SessionAdapter/SessionAdapter.swift b/Sources/SessionAdapter/SessionAdapter.swift new file mode 100644 index 00000000..df9cdc85 --- /dev/null +++ b/Sources/SessionAdapter/SessionAdapter.swift @@ -0,0 +1,18 @@ +import Foundation + +/// `SessionTaskType` protocol represents a task for a request. +public protocol SessionTaskType: class { + func resume() + func cancel() +} + +/// `SessionAdapter` protocol provides interface to connect lower level networking backend with `Session`. +/// APIKit provides `URLSessionAdapter`, which conforms to `SessionAdapter`, to connect `URLSession` +/// with `Session`. +public protocol SessionAdapter { + /// Returns instance that conforms to `SessionTaskType`. `handler` must be called after success or failure. + func createTask(with URLRequest: URLRequest, handler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTaskType + + /// Collects tasks from backend networking stack. `handler` must be called after collecting. + func getTasks(with handler: @escaping ([SessionTaskType]) -> Void) +} diff --git a/Sources/SessionAdapter/URLSessionAdapter.swift b/Sources/SessionAdapter/URLSessionAdapter.swift new file mode 100644 index 00000000..edcc610b --- /dev/null +++ b/Sources/SessionAdapter/URLSessionAdapter.swift @@ -0,0 +1,75 @@ +import Foundation + +extension URLSessionTask: SessionTaskType { + +} + +private var dataTaskResponseBufferKey = 0 +private var taskAssociatedObjectCompletionHandlerKey = 0 + +/// `URLSessionAdapter` connects `URLSession` with `Session`. +/// +/// If you want to add custom behavior of `URLSession` by implementing delegate methods defined in +/// `URLSessionDelegate` and related protocols, define a subclass of `URLSessionAdapter` and implment +/// delegate methods that you want to implement. Since `URLSessionAdapter` also implements delegate methods +/// `URLSession(_:task: didCompleteWithError:)` and `URLSession(_:dataTask:didReceiveData:)`, you have to call +/// `super` in these methods if you implement them. +open class URLSessionAdapter: NSObject, SessionAdapter, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDataDelegate { + /// The undelying `URLSession` instance. + open var urlSession: URLSession! + + /// Returns `URLSessionAdapter` initialized with `URLSessionConfiguration`. + public init(configuration: URLSessionConfiguration) { + super.init() + self.urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: nil) + } + + /// Creates `URLSessionDataTask` instance using `dataTaskWithRequest(_:completionHandler:)`. + open func createTask(with URLRequest: URLRequest, handler: @escaping (Data?, URLResponse?, Error?) -> Void) -> SessionTaskType { + let task = urlSession.dataTask(with: URLRequest) + + setBuffer(NSMutableData(), forTask: task) + setHandler(handler, forTask: task) + + task.resume() + + return task + } + + /// Aggregates `URLSessionTask` instances in `URLSession` using `getTasksWithCompletionHandler(_:)`. + open func getTasks(with handler: @escaping ([SessionTaskType]) -> Void) { + urlSession.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in + let allTasks = dataTasks as [URLSessionTask] + + uploadTasks as [URLSessionTask] + + downloadTasks as [URLSessionTask] + + handler(allTasks.map { $0 }) + } + } + + private func setBuffer(_ buffer: NSMutableData, forTask task: URLSessionTask) { + objc_setAssociatedObject(task, &dataTaskResponseBufferKey, buffer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + private func buffer(for task: URLSessionTask) -> NSMutableData? { + return objc_getAssociatedObject(task, &dataTaskResponseBufferKey) as? NSMutableData + } + + private func setHandler(_ handler: @escaping (Data?, URLResponse?, Error?) -> Void, forTask task: URLSessionTask) { + objc_setAssociatedObject(task, &taskAssociatedObjectCompletionHandlerKey, handler as Any, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + + private func handler(for task: URLSessionTask) -> ((Data?, URLResponse?, Error?) -> Void)? { + return objc_getAssociatedObject(task, &taskAssociatedObjectCompletionHandlerKey) as? (Data?, URLResponse?, Error?) -> Void + } + + // MARK: URLSessionTaskDelegate + open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + handler(for: task)?(buffer(for: task) as Data?, task.response, error) + } + + // MARK: URLSessionDataDelegate + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + buffer(for: dataTask)?.append(data) + } +} diff --git a/Sources/SessionAdapterType/NSURLSessionAdapter.swift b/Sources/SessionAdapterType/NSURLSessionAdapter.swift deleted file mode 100644 index 3919d169..00000000 --- a/Sources/SessionAdapterType/NSURLSessionAdapter.swift +++ /dev/null @@ -1,75 +0,0 @@ -import Foundation - -extension NSURLSessionTask: SessionTaskType { - -} - -private var dataTaskResponseBufferKey = 0 -private var taskAssociatedObjectCompletionHandlerKey = 0 - -/// `NSURLSessionAdapter` connects `NSURLSession` with `Session`. -/// -/// If you want to add custom behavior of `NSURLSession` by implementing delegate methods defined in -/// `NSURLSessionDelegate` and related protocols, define a subclass of `NSURLSessionAdapter` and implment -/// delegate methods that you want to implement. Since `NSURLSessionAdapter` also implements delegate methods -/// `URLSession(_:task: didCompleteWithError:)` and `URLSession(_:dataTask:didReceiveData:)`, you have to call -/// `super` in these methods if you implement them. -public class NSURLSessionAdapter: NSObject, SessionAdapterType, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate { - /// The undelying `NSURLSession` instance. - public var URLSession: NSURLSession! - - /// Returns `NSURLSessionAdapter` initialized with `NSURLSessionConfiguration`. - public init(configuration: NSURLSessionConfiguration) { - super.init() - self.URLSession = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil) - } - - /// Creates `NSURLSessionDataTask` instance using `dataTaskWithRequest(_:completionHandler:)`. - public func createTaskWithURLRequest(URLRequest: NSURLRequest, handler: (NSData?, NSURLResponse?, ErrorType?) -> Void) -> SessionTaskType { - let task = URLSession.dataTaskWithRequest(URLRequest) - - setBuffer(NSMutableData(), forTask: task) - setHandler(handler, forTask: task) - - task.resume() - - return task - } - - /// Aggregates `NSURLSessionTask` instances in `URLSession` using `getTasksWithCompletionHandler(_:)`. - public func getTasksWithHandler(handler: [SessionTaskType] -> Void) { - URLSession.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in - let allTasks = dataTasks as [NSURLSessionTask] - + uploadTasks as [NSURLSessionTask] - + downloadTasks as [NSURLSessionTask] - - handler(allTasks.map { $0 }) - } - } - - private func setBuffer(buffer: NSMutableData, forTask task: NSURLSessionTask) { - objc_setAssociatedObject(task, &dataTaskResponseBufferKey, buffer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - - private func bufferForTask(task: NSURLSessionTask) -> NSMutableData? { - return objc_getAssociatedObject(task, &dataTaskResponseBufferKey) as? NSMutableData - } - - private func setHandler(handler: (NSData?, NSURLResponse?, NSError?) -> Void, forTask task: NSURLSessionTask) { - objc_setAssociatedObject(task, &taskAssociatedObjectCompletionHandlerKey, Box(handler), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) - } - - private func handlerForTask(task: NSURLSessionTask) -> ((NSData?, NSURLResponse?, NSError?) -> Void)? { - return (objc_getAssociatedObject(task, &taskAssociatedObjectCompletionHandlerKey) as? Box<(NSData?, NSURLResponse?, NSError?) -> Void>)?.value - } - - // MARK: NSURLSessionTaskDelegate - public func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError connectionError: NSError?) { - handlerForTask(task)?(bufferForTask(task), task.response, connectionError) - } - - // MARK: NSURLSessionDataDelegate - public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) { - bufferForTask(dataTask)?.appendData(data) - } -} diff --git a/Sources/SessionAdapterType/SessionAdapterType.swift b/Sources/SessionAdapterType/SessionAdapterType.swift deleted file mode 100644 index 46f4cde2..00000000 --- a/Sources/SessionAdapterType/SessionAdapterType.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -/// `SessionTaskType` protocol represents a task for a request. -public protocol SessionTaskType: class { - func resume() - func cancel() -} - -/// `SessionAdapterType` protocol provides interface to connect lower level networking backend with `Session`. -/// APIKit provides `NSURLSessionAdapter`, which conforms to `SessionAdapterType`, to connect `NSURLSession` -/// with `Session`. -public protocol SessionAdapterType { - /// Returns instance that conforms to `SessionTaskType`. `handler` must be called after success or failure. - func createTaskWithURLRequest(URLRequest: NSURLRequest, handler: (NSData?, NSURLResponse?, ErrorType?) -> Void) -> SessionTaskType - - /// Collects tasks from backend networking stack. `handler` must be called after collecting. - func getTasksWithHandler(handler: [SessionTaskType] -> Void) -} diff --git a/Tests/APIKit/BodyParametersType/FormURLEncodedBodyParametersTests.swift b/Tests/APIKit/BodyParametersType/FormURLEncodedBodyParametersTests.swift index c823da25..3afa7262 100644 --- a/Tests/APIKit/BodyParametersType/FormURLEncodedBodyParametersTests.swift +++ b/Tests/APIKit/BodyParametersType/FormURLEncodedBodyParametersTests.swift @@ -9,12 +9,12 @@ class FormURLEncodedBodyParametersTests: XCTestCase { XCTAssertEqual(parameters.contentType, "application/x-www-form-urlencoded") do { - guard case .Data(let data) = try parameters.buildEntity() else { + guard case .data(let data) = try parameters.buildEntity() else { XCTFail() return } - let createdObject = try URLEncodedSerialization.objectFromData(data, encoding: NSUTF8StringEncoding) + let createdObject = try URLEncodedSerialization.object(from: data, encoding: .utf8) XCTAssertEqual(createdObject["foo"], "1") XCTAssertEqual(createdObject["bar"], "2") XCTAssertEqual(createdObject["baz"], "3") diff --git a/Tests/APIKit/BodyParametersType/JSONBodyParametersTests.swift b/Tests/APIKit/BodyParametersType/JSONBodyParametersTests.swift index c7d20d95..006c8e04 100644 --- a/Tests/APIKit/BodyParametersType/JSONBodyParametersTests.swift +++ b/Tests/APIKit/BodyParametersType/JSONBodyParametersTests.swift @@ -9,15 +9,15 @@ class JSONBodyParametersTests: XCTestCase { XCTAssertEqual(parameters.contentType, "application/json") do { - guard case .Data(let data) = try parameters.buildEntity() else { + guard case .data(let data) = try parameters.buildEntity() else { XCTFail() return } - let dictionary = try NSJSONSerialization.JSONObjectWithData(data, options: []) - XCTAssertEqual(dictionary["foo"], 1) - XCTAssertEqual(dictionary["bar"], 2) - XCTAssertEqual(dictionary["baz"], 3) + let dictionary = try JSONSerialization.jsonObject(with: data, options: []) + XCTAssertEqual((dictionary as? [String: Int])?["foo"], 1) + XCTAssertEqual((dictionary as? [String: Int])?["bar"], 2) + XCTAssertEqual((dictionary as? [String: Int])?["baz"], 3) } catch { XCTFail() } @@ -28,7 +28,7 @@ class JSONBodyParametersTests: XCTestCase { let parameters = JSONBodyParameters(JSONObject: object) do { - try parameters.buildEntity() + try _ = parameters.buildEntity() XCTFail() } catch { let nserror = error as NSError diff --git a/Tests/APIKit/BodyParametersType/MultipartFormDataParametersTests.swift b/Tests/APIKit/BodyParametersType/MultipartFormDataParametersTests.swift index 7ce72fa6..78345a8a 100644 --- a/Tests/APIKit/BodyParametersType/MultipartFormDataParametersTests.swift +++ b/Tests/APIKit/BodyParametersType/MultipartFormDataParametersTests.swift @@ -5,8 +5,8 @@ import XCTest class MultipartFormDataParametersTests: XCTestCase { // MARK: Entity func testDataEntitySuccess() { - let value1 = "1".dataUsingEncoding(NSUTF8StringEncoding)! - let value2 = "2".dataUsingEncoding(NSUTF8StringEncoding)! + let value1 = "1".data(using: .utf8)! + let value2 = "2".data(using: .utf8)! let parameters = MultipartFormDataBodyParameters(parts: [ MultipartFormDataBodyParameters.Part(data: value1, name: "foo"), @@ -14,21 +14,21 @@ class MultipartFormDataParametersTests: XCTestCase { ]) do { - guard case .Data(let data) = try parameters.buildEntity() else { + guard case .data(let data) = try parameters.buildEntity() else { XCTFail() return } - let encodedData = String(data: data, encoding:NSUTF8StringEncoding)! + let encodedData = String(data: data, encoding:.utf8)! let returnCode = "\r\n" let pattern = "^multipart/form-data; boundary=([\\w.]+)$" let regexp = try NSRegularExpression(pattern: pattern, options: []) let range = NSRange(location: 0, length: parameters.contentType.characters.count) - let match = regexp.matchesInString(parameters.contentType, options: [], range: range) + let match = regexp.matches(in: parameters.contentType, options: [], range: range) XCTAssertTrue(match.count > 0) - let boundary = (parameters.contentType as NSString).substringWithRange(match.first!.rangeAtIndex(1)) + let boundary = (parameters.contentType as NSString).substring(with: match.first!.rangeAt(1)) XCTAssertEqual(parameters.contentType, "multipart/form-data; boundary=\(boundary)") XCTAssertEqual(encodedData, "--\(boundary)\(returnCode)Content-Disposition: form-data; name=\"foo\"\(returnCode)\(returnCode)1\(returnCode)--\(boundary)\(returnCode)Content-Disposition: form-data; name=\"bar\"\(returnCode)\(returnCode)2\(returnCode)--\(boundary)--\(returnCode)") } catch { @@ -37,31 +37,31 @@ class MultipartFormDataParametersTests: XCTestCase { } func testInputStreamEntitySuccess() { - let value1 = "1".dataUsingEncoding(NSUTF8StringEncoding)! - let value2 = "2".dataUsingEncoding(NSUTF8StringEncoding)! + let value1 = "1".data(using: .utf8)! + let value2 = "2".data(using: .utf8)! let parameters = MultipartFormDataBodyParameters(parts: [ MultipartFormDataBodyParameters.Part(data: value1, name: "foo"), MultipartFormDataBodyParameters.Part(data: value2, name: "bar"), - ], entityType: .InputStream) + ], entityType: .inputStream) do { - guard case .InputStream(let inputStream) = try parameters.buildEntity() else { + guard case .inputStream(let inputStream) = try parameters.buildEntity() else { XCTFail() return } - let data = try NSData(inputStream: inputStream) - let encodedData = String(data: data, encoding:NSUTF8StringEncoding)! + let data = try Data(inputStream: inputStream) + let encodedData = String(data: data, encoding:.utf8)! let returnCode = "\r\n" let pattern = "^multipart/form-data; boundary=([\\w.]+)$" let regexp = try NSRegularExpression(pattern: pattern, options: []) let range = NSRange(location: 0, length: parameters.contentType.characters.count) - let match = regexp.matchesInString(parameters.contentType, options: [], range: range) + let match = regexp.matches(in: parameters.contentType, options: [], range: range) XCTAssertTrue(match.count > 0) - let boundary = (parameters.contentType as NSString).substringWithRange(match.first!.rangeAtIndex(1)) + let boundary = (parameters.contentType as NSString).substring(with: match.first!.rangeAt(1)) XCTAssertEqual(parameters.contentType, "multipart/form-data; boundary=\(boundary)") XCTAssertEqual(encodedData, "--\(boundary)\(returnCode)Content-Disposition: form-data; name=\"foo\"\(returnCode)\(returnCode)1\(returnCode)--\(boundary)\(returnCode)Content-Disposition: form-data; name=\"bar\"\(returnCode)\(returnCode)2\(returnCode)--\(boundary)--\(returnCode)") } catch { @@ -71,29 +71,29 @@ class MultipartFormDataParametersTests: XCTestCase { // MARK: Values func testFileValue() { - let fileURL = NSBundle(forClass: self.dynamicType).URLForResource("test", withExtension: "json")! + let fileURL = Bundle(for: type(of: self)).url(forResource: "test", withExtension: "json")! let part = try! MultipartFormDataBodyParameters.Part(fileURL: fileURL, name: "test") let parameters = MultipartFormDataBodyParameters(parts: [part]) do { - guard case .Data(let data) = try parameters.buildEntity() else { + guard case .data(let data) = try parameters.buildEntity() else { XCTFail() return } - let testData = NSData(contentsOfURL: fileURL)! - let testString = NSString(data: testData, encoding: NSUTF8StringEncoding)! + let testData = try! Data(contentsOf: fileURL) + let testString = String(data: testData, encoding: .utf8)! - let encodedData = String(data: data, encoding:NSUTF8StringEncoding)! + let encodedData = String(data: data, encoding:.utf8)! let returnCode = "\r\n" let pattern = "^multipart/form-data; boundary=([\\w.]+)$" let regexp = try NSRegularExpression(pattern: pattern, options: []) let range = NSRange(location: 0, length: parameters.contentType.characters.count) - let match = regexp.matchesInString(parameters.contentType, options: [], range: range) + let match = regexp.matches(in: parameters.contentType, options: [], range: range) XCTAssertTrue(match.count > 0) - let boundary = (parameters.contentType as NSString).substringWithRange(match.first!.rangeAtIndex(1)) + let boundary = (parameters.contentType as NSString).substring(with: match.first!.rangeAt(1)) XCTAssertEqual(parameters.contentType, "multipart/form-data; boundary=\(boundary)") XCTAssertEqual(encodedData, "--\(boundary)\(returnCode)Content-Disposition: form-data; name=\"test\"; filename=\"test.json\"\r\nContent-Type: application/json\(returnCode)\(returnCode)\(testString)\(returnCode)--\(boundary)--\(returnCode)") } catch { @@ -106,12 +106,12 @@ class MultipartFormDataParametersTests: XCTestCase { let parameters = MultipartFormDataBodyParameters(parts: [part]) do { - guard case .Data(let data) = try parameters.buildEntity() else { + guard case .data(let data) = try parameters.buildEntity() else { XCTFail() return } - let string = String(data: data, encoding:NSUTF8StringEncoding)! + let string = String(data: data, encoding:.utf8)! XCTAssertEqual(string, "--\(parameters.boundary)\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nabcdef\r\n--\(parameters.boundary)--\r\n") } catch { XCTFail() @@ -123,12 +123,12 @@ class MultipartFormDataParametersTests: XCTestCase { let parameters = MultipartFormDataBodyParameters(parts: [part]) do { - guard case .Data(let data) = try parameters.buildEntity() else { + guard case .data(let data) = try parameters.buildEntity() else { XCTFail() return } - let string = String(data: data, encoding:NSUTF8StringEncoding)! + let string = String(data: data, encoding:.utf8)! XCTAssertEqual(string, "--\(parameters.boundary)\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\n123\r\n--\(parameters.boundary)--\r\n") } catch { XCTFail() @@ -140,12 +140,12 @@ class MultipartFormDataParametersTests: XCTestCase { let parameters = MultipartFormDataBodyParameters(parts: [part]) do { - guard case .Data(let data) = try parameters.buildEntity() else { + guard case .data(let data) = try parameters.buildEntity() else { XCTFail() return } - let string = String(data: data, encoding:NSUTF8StringEncoding)! + let string = String(data: data, encoding:.utf8)! XCTAssertEqual(string, "--\(parameters.boundary)\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\n3.14\r\n--\(parameters.boundary)--\r\n") } catch { XCTFail() diff --git a/Tests/APIKit/BodyParametersType/URLEncodedSerializationTests.swift b/Tests/APIKit/BodyParametersType/URLEncodedSerializationTests.swift index 8750178f..89702e4d 100644 --- a/Tests/APIKit/BodyParametersType/URLEncodedSerializationTests.swift +++ b/Tests/APIKit/BodyParametersType/URLEncodedSerializationTests.swift @@ -3,10 +3,10 @@ import XCTest import APIKit class URLEncodedSerializationTests: XCTestCase { - // MARK: NSData -> AnyObject + // MARK: NSData -> Any func testObjectFromData() { - let data = "key1=value1&key2=value2".dataUsingEncoding(NSUTF8StringEncoding)! - let object = try? URLEncodedSerialization.objectFromData(data, encoding: NSUTF8StringEncoding) + let data = "key1=value1&key2=value2".data(using: .utf8)! + let object = try? URLEncodedSerialization.object(from: data, encoding: .utf8) XCTAssertEqual(object?["key1"], "value1") XCTAssertEqual(object?["key2"], "value2") } @@ -15,12 +15,12 @@ class URLEncodedSerializationTests: XCTestCase { let string = "key==value&" do { - let data = string.dataUsingEncoding(NSUTF8StringEncoding)! - try URLEncodedSerialization.objectFromData(data, encoding: NSUTF8StringEncoding) + let data = string.data(using: .utf8)! + try _ = URLEncodedSerialization.object(from: data, encoding: .utf8) XCTFail() } catch { guard let error = error as? URLEncodedSerialization.Error, - case .InvalidFormatString(let invalidString) = error else { + case .invalidFormatString(let invalidString) = error else { XCTFail() return } @@ -31,45 +31,45 @@ class URLEncodedSerializationTests: XCTestCase { func testInvalidString() { var bytes = [UInt8]([0xed, 0xa0, 0x80]) // U+D800 (high surrogate) - let data = NSData(bytes: &bytes, length: bytes.count) + let data = Data(bytes: &bytes, count: bytes.count) do { - try URLEncodedSerialization.objectFromData(data, encoding: NSUTF8StringEncoding) + try _ = URLEncodedSerialization.object(from: data, encoding: .utf8) XCTFail() } catch { guard let error = error as? URLEncodedSerialization.Error, - case .CannotGetStringFromData(let invalidData, let encoding) = error else { + case .cannotGetStringFromData(let invalidData, let encoding) = error else { XCTFail() return } XCTAssertEqual(data, invalidData) - XCTAssertEqual(encoding, NSUTF8StringEncoding) + XCTAssertEqual(encoding, .utf8) } } - // MARK: AnyObject -> NSData + // MARK: Any -> NSData func testDataFromObject() { - let object = ["hey": "yo"] as AnyObject - let data = try? URLEncodedSerialization.dataFromObject(object, encoding: NSUTF8StringEncoding) - let string = data.flatMap { NSString(data: $0, encoding: NSUTF8StringEncoding) } + let object = ["hey": "yo"] as Any + let data = try? URLEncodedSerialization.data(from: object, encoding: .utf8) + let string = data.flatMap { String(data: $0, encoding: .utf8) } XCTAssertEqual(string, "hey=yo") } func testNonDictionaryObject() { - let dictionaries = [["hey": "yo"]] as AnyObject + let dictionaries = [["hey": "yo"]] as Any do { - try URLEncodedSerialization.dataFromObject(dictionaries, encoding: NSUTF8StringEncoding) + try _ = URLEncodedSerialization.data(from: dictionaries, encoding: .utf8) XCTFail() } catch { guard let error = error as? URLEncodedSerialization.Error, - case .CannotCastObjectToDictionary(let object) = error else { + case .cannotCastObjectToDictionary(let object) = error else { XCTFail() return } - XCTAssertEqual(object["hey"], dictionaries["hey"]) + XCTAssertEqual((object as AnyObject)["hey"], (dictionaries as AnyObject)["hey"]) } } } diff --git a/Tests/APIKit/DataParserType/FormURLEncodedDataParserTests.swift b/Tests/APIKit/DataParserType/FormURLEncodedDataParserTests.swift index d7ab1d95..8bf66a77 100644 --- a/Tests/APIKit/DataParserType/FormURLEncodedDataParserTests.swift +++ b/Tests/APIKit/DataParserType/FormURLEncodedDataParserTests.swift @@ -4,17 +4,17 @@ import XCTest class FormURLEncodedDataParserTests: XCTestCase { func testURLAcceptHeader() { - let parser = FormURLEncodedDataParser(encoding: NSUTF8StringEncoding) + let parser = FormURLEncodedDataParser(encoding: .utf8) XCTAssertEqual(parser.contentType, "application/x-www-form-urlencoded") } func testURLSuccess() { let string = "foo=1&bar=2&baz=3" - let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! - let parser = FormURLEncodedDataParser(encoding: NSUTF8StringEncoding) + let data = string.data(using: .utf8, allowLossyConversion: false)! + let parser = FormURLEncodedDataParser(encoding: .utf8) do { - let object = try parser.parseData(data) + let object = try parser.parse(data: data) let dictionary = object as? [String: String] XCTAssertEqual(dictionary?["foo"], "1") XCTAssertEqual(dictionary?["bar"], "2") @@ -26,15 +26,15 @@ class FormURLEncodedDataParserTests: XCTestCase { func testInvalidString() { var bytes = [UInt8]([0xed, 0xa0, 0x80]) // U+D800 (high surrogate) - let data = NSData(bytes: &bytes, length: bytes.count) - let parser = FormURLEncodedDataParser(encoding: NSUTF8StringEncoding) + let data = Data(bytes: &bytes, count: bytes.count) + let parser = FormURLEncodedDataParser(encoding: .utf8) do { - try parser.parseData(data) + try _ = parser.parse(data: data) XCTFail() } catch { guard let error = error as? FormURLEncodedDataParser.Error, - case .CannotGetStringFromData(let invalidData) = error else { + case .cannotGetStringFromData(let invalidData) = error else { XCTFail() return } diff --git a/Tests/APIKit/DataParserType/JSONDataParserTests.swift b/Tests/APIKit/DataParserType/JSONDataParserTests.swift index 10083ca5..e7faaca4 100644 --- a/Tests/APIKit/DataParserType/JSONDataParserTests.swift +++ b/Tests/APIKit/DataParserType/JSONDataParserTests.swift @@ -10,11 +10,11 @@ class JSONDataParserTests: XCTestCase { func testJSONSuccess() { let string = "{\"foo\": 1, \"bar\": 2, \"baz\": 3}" - let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! + let data = string.data(using: .utf8, allowLossyConversion: false)! let parser = JSONDataParser(readingOptions: []) do { - let object = try parser.parseData(data) + let object = try parser.parse(data: data) let dictionary = object as? [String: Int] XCTAssertEqual(dictionary?["foo"], 1) XCTAssertEqual(dictionary?["bar"], 2) diff --git a/Tests/APIKit/DataParserType/StringDataParserTests.swift b/Tests/APIKit/DataParserType/StringDataParserTests.swift index cd7b77ca..0dc0255a 100644 --- a/Tests/APIKit/DataParserType/StringDataParserTests.swift +++ b/Tests/APIKit/DataParserType/StringDataParserTests.swift @@ -4,17 +4,17 @@ import APIKit class StringDataParserTests: XCTestCase { func testAcceptHeader() { - let parser = StringDataParser(encoding: NSUTF8StringEncoding) + let parser = StringDataParser(encoding: .utf8) XCTAssertNil(parser.contentType) } func testParseData() { let string = "abcdef" - let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! - let parser = StringDataParser(encoding: NSUTF8StringEncoding) + let data = string.data(using: .utf8, allowLossyConversion: false)! + let parser = StringDataParser(encoding: .utf8) do { - let object = try parser.parseData(data) + let object = try parser.parse(data: data) XCTAssertEqual(object as? String, string) } catch { XCTFail() @@ -23,15 +23,15 @@ class StringDataParserTests: XCTestCase { func testInvalidString() { var bytes = [UInt8]([0xed, 0xa0, 0x80]) // U+D800 (high surrogate) - let data = NSData(bytes: &bytes, length: bytes.count) - let parser = StringDataParser(encoding: NSUTF8StringEncoding) + let data = Data(bytes: &bytes, count: bytes.count) + let parser = StringDataParser(encoding: .utf8) do { - try parser.parseData(data) + try _ = parser.parse(data: data) XCTFail() } catch { guard let error = error as? StringDataParser.Error, - case .InvalidData(let invalidData) = error else { + case .invalidData(let invalidData) = error else { XCTFail() return } diff --git a/Tests/APIKit/RequestTypeTests.swift b/Tests/APIKit/RequestTests.swift similarity index 66% rename from Tests/APIKit/RequestTypeTests.swift rename to Tests/APIKit/RequestTests.swift index 2b387ad7..bd10942b 100644 --- a/Tests/APIKit/RequestTypeTests.swift +++ b/Tests/APIKit/RequestTests.swift @@ -1,50 +1,50 @@ import XCTest import APIKit -class RequestTypeTests: XCTestCase { +class RequestTests: XCTestCase { func testJapanesesQueryParameters() { let request = TestRequest(parameters: ["q": "こんにちは"]) - let URLRequest = try? request.buildURLRequest() - XCTAssertEqual(URLRequest?.URL?.query, "q=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF") + let urlRequest = try? request.buildURLRequest() + XCTAssertEqual(urlRequest?.url?.query, "q=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF") } func testSymbolQueryParameters() { let request = TestRequest(parameters: ["q": "!\"#$%&'()0=~|`{}*+<>?/_"]) - let URLRequest = try? request.buildURLRequest() - XCTAssertEqual(URLRequest?.URL?.query, "q=%21%22%23%24%25%26%27%28%290%3D~%7C%60%7B%7D%2A%2B%3C%3E?/_") + let urlRequest = try? request.buildURLRequest() + XCTAssertEqual(urlRequest?.url?.query, "q=%21%22%23%24%25%26%27%28%290%3D~%7C%60%7B%7D%2A%2B%3C%3E?/_") } func testNullQueryParameters() { let request = TestRequest(parameters: ["null": NSNull()]) - let URLRequest = try? request.buildURLRequest() - XCTAssertEqual(URLRequest?.URL?.query, "null") + let urlRequest = try? request.buildURLRequest() + XCTAssertEqual(urlRequest?.url?.query, "null") } func testheaderFields() { let request = TestRequest(headerFields: ["Foo": "f", "Accept": "a", "Content-Type": "c"]) - let URLReqeust = try? request.buildURLRequest() - XCTAssertEqual(URLReqeust?.valueForHTTPHeaderField("Foo"), "f") - XCTAssertEqual(URLReqeust?.valueForHTTPHeaderField("Accept"), "a") - XCTAssertEqual(URLReqeust?.valueForHTTPHeaderField("Content-Type"), "c") + let urlReqeust = try? request.buildURLRequest() + XCTAssertEqual(urlReqeust?.value(forHTTPHeaderField: "Foo"), "f") + XCTAssertEqual(urlReqeust?.value(forHTTPHeaderField: "Accept"), "a") + XCTAssertEqual(urlReqeust?.value(forHTTPHeaderField: "Content-Type"), "c") } func testPOSTJSONRequest() { - let parameters: [AnyObject] = [ + let parameters: [Any] = [ ["id": "1"], ["id": "2"], ["hello", "yellow"] ] - let request = TestRequest(method: .POST, parameters: parameters) - XCTAssert(request.parameters?.count == 3) + let request = TestRequest(method: .post, parameters: parameters) + XCTAssert((request.parameters as? [Any])?.count == 3) - let URLRequest = try? request.buildURLRequest() - XCTAssertNotNil(URLRequest?.HTTPBody) + let urlRequest = try? request.buildURLRequest() + XCTAssertNotNil(urlRequest?.httpBody) - let json = URLRequest?.HTTPBody.flatMap { try? NSJSONSerialization.JSONObjectWithData($0, options: []) } as? [AnyObject] + let json = urlRequest?.httpBody.flatMap { try? JSONSerialization.jsonObject(with: $0, options: []) } as? [AnyObject] XCTAssertEqual(json?.count, 3) - XCTAssertEqual(json?[0]["id"], "1") - XCTAssertEqual(json?[1]["id"], "2") + XCTAssertEqual((json?[0] as? [String: String])?["id"], "1") + XCTAssertEqual((json?[1] as? [String: String])?["id"], "2") let array = json?[2] as? [String] XCTAssertEqual(array?[0], "hello") @@ -52,369 +52,369 @@ class RequestTypeTests: XCTestCase { } func testPOSTInvalidJSONRequest() { - let request = TestRequest(method: .POST, parameters: "foo") - let URLRequest = try? request.buildURLRequest() - XCTAssertNil(URLRequest?.HTTPBody) + let request = TestRequest(method: .post, parameters: "foo") + let urlRequest = try? request.buildURLRequest() + XCTAssertNil(urlRequest?.httpBody) } func testBuildURL() { // MARK: - baseURL = https://example.com XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "").absoluteURL, - NSURL(string: "https://example.com") + URL(string: "https://example.com") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/").absoluteURL, - NSURL(string: "https://example.com/") + URL(string: "https://example.com/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/?p=1") + URL(string: "https://example.com/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "foo").absoluteURL, - NSURL(string: "https://example.com/foo") + URL(string: "https://example.com/foo") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/foo", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/foo?p=1") + URL(string: "https://example.com/foo?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/foo/").absoluteURL, - NSURL(string: "https://example.com/foo/") + URL(string: "https://example.com/foo/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/foo/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/foo/?p=1") + URL(string: "https://example.com/foo/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "foo/bar").absoluteURL, - NSURL(string: "https://example.com/foo/bar") + URL(string: "https://example.com/foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/foo/bar").absoluteURL, - NSURL(string: "https://example.com/foo/bar") + URL(string: "https://example.com/foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/foo/bar", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/foo/bar?p=1") + URL(string: "https://example.com/foo/bar?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/foo/bar/").absoluteURL, - NSURL(string: "https://example.com/foo/bar/") + URL(string: "https://example.com/foo/bar/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/foo/bar/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/foo/bar/?p=1") + URL(string: "https://example.com/foo/bar/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com", path: "/foo/bar//").absoluteURL, - NSURL(string: "https://example.com/foo/bar//") + URL(string: "https://example.com/foo/bar//") ) // MARK: - baseURL = https://example.com/ XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "").absoluteURL, - NSURL(string: "https://example.com/") + URL(string: "https://example.com/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/").absoluteURL, - NSURL(string: "https://example.com//") + URL(string: "https://example.com//") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com//?p=1") + URL(string: "https://example.com//?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "foo").absoluteURL, - NSURL(string: "https://example.com/foo") + URL(string: "https://example.com/foo") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/foo").absoluteURL, - NSURL(string: "https://example.com//foo") + URL(string: "https://example.com//foo") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/foo", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com//foo?p=1") + URL(string: "https://example.com//foo?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/foo/").absoluteURL, - NSURL(string: "https://example.com//foo/") + URL(string: "https://example.com//foo/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/foo/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com//foo/?p=1") + URL(string: "https://example.com//foo/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "foo/bar").absoluteURL, - NSURL(string: "https://example.com/foo/bar") + URL(string: "https://example.com/foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/foo/bar").absoluteURL, - NSURL(string: "https://example.com//foo/bar") + URL(string: "https://example.com//foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/foo/bar", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com//foo/bar?p=1") + URL(string: "https://example.com//foo/bar?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/foo/bar/").absoluteURL, - NSURL(string: "https://example.com//foo/bar/") + URL(string: "https://example.com//foo/bar/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "/foo/bar/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com//foo/bar/?p=1") + URL(string: "https://example.com//foo/bar/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/", path: "foo//bar//").absoluteURL, - NSURL(string: "https://example.com/foo//bar//") + URL(string: "https://example.com/foo//bar//") ) // MARK: - baseURL = https://example.com/api XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "").absoluteURL, - NSURL(string: "https://example.com/api") + URL(string: "https://example.com/api") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/").absoluteURL, - NSURL(string: "https://example.com/api/") + URL(string: "https://example.com/api/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api/?p=1") + URL(string: "https://example.com/api/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "foo").absoluteURL, - NSURL(string: "https://example.com/api/foo") + URL(string: "https://example.com/api/foo") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/foo").absoluteURL, - NSURL(string: "https://example.com/api/foo") + URL(string: "https://example.com/api/foo") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/foo", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api/foo?p=1") + URL(string: "https://example.com/api/foo?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/foo/").absoluteURL, - NSURL(string: "https://example.com/api/foo/") + URL(string: "https://example.com/api/foo/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/foo/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api/foo/?p=1") + URL(string: "https://example.com/api/foo/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "foo/bar").absoluteURL, - NSURL(string: "https://example.com/api/foo/bar") + URL(string: "https://example.com/api/foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/foo/bar").absoluteURL, - NSURL(string: "https://example.com/api/foo/bar") + URL(string: "https://example.com/api/foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/foo/bar", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api/foo/bar?p=1") + URL(string: "https://example.com/api/foo/bar?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/foo/bar/").absoluteURL, - NSURL(string: "https://example.com/api/foo/bar/") + URL(string: "https://example.com/api/foo/bar/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "/foo/bar/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api/foo/bar/?p=1") + URL(string: "https://example.com/api/foo/bar/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api", path: "foo//bar//").absoluteURL, - NSURL(string: "https://example.com/api/foo//bar//") + URL(string: "https://example.com/api/foo//bar//") ) // MARK: - baseURL = https://example.com/api/ XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "").absoluteURL, - NSURL(string: "https://example.com/api/") + URL(string: "https://example.com/api/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/").absoluteURL, - NSURL(string: "https://example.com/api//") + URL(string: "https://example.com/api//") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api//?p=1") + URL(string: "https://example.com/api//?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "foo").absoluteURL, - NSURL(string: "https://example.com/api/foo") + URL(string: "https://example.com/api/foo") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/foo").absoluteURL, - NSURL(string: "https://example.com/api//foo") + URL(string: "https://example.com/api//foo") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/foo", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api//foo?p=1") + URL(string: "https://example.com/api//foo?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/foo/").absoluteURL, - NSURL(string: "https://example.com/api//foo/") + URL(string: "https://example.com/api//foo/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/foo/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api//foo/?p=1") + URL(string: "https://example.com/api//foo/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "foo/bar").absoluteURL, - NSURL(string: "https://example.com/api/foo/bar") + URL(string: "https://example.com/api/foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/foo/bar").absoluteURL, - NSURL(string: "https://example.com/api//foo/bar") + URL(string: "https://example.com/api//foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/foo/bar", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api//foo/bar?p=1") + URL(string: "https://example.com/api//foo/bar?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/foo/bar/").absoluteURL, - NSURL(string: "https://example.com/api//foo/bar/") + URL(string: "https://example.com/api//foo/bar/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "/foo/bar/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com/api//foo/bar/?p=1") + URL(string: "https://example.com/api//foo/bar/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com/api/", path: "foo//bar//").absoluteURL, - NSURL(string: "https://example.com/api/foo//bar//") + URL(string: "https://example.com/api/foo//bar//") ) // MARK: - baseURL = https://example.com/// XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "").absoluteURL, - NSURL(string: "https://example.com///") + URL(string: "https://example.com///") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/").absoluteURL, - NSURL(string: "https://example.com////") + URL(string: "https://example.com////") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com////?p=1") + URL(string: "https://example.com////?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "foo").absoluteURL, - NSURL(string: "https://example.com///foo") + URL(string: "https://example.com///foo") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/foo").absoluteURL, - NSURL(string: "https://example.com////foo") + URL(string: "https://example.com////foo") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/foo", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com////foo?p=1") + URL(string: "https://example.com////foo?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/foo/").absoluteURL, - NSURL(string: "https://example.com////foo/") + URL(string: "https://example.com////foo/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/foo/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com////foo/?p=1") + URL(string: "https://example.com////foo/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "foo/bar").absoluteURL, - NSURL(string: "https://example.com///foo/bar") + URL(string: "https://example.com///foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/foo/bar").absoluteURL, - NSURL(string: "https://example.com////foo/bar") + URL(string: "https://example.com////foo/bar") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/foo/bar", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com////foo/bar?p=1") + URL(string: "https://example.com////foo/bar?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/foo/bar/").absoluteURL, - NSURL(string: "https://example.com////foo/bar/") + URL(string: "https://example.com////foo/bar/") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "/foo/bar/", parameters: ["p": 1]).absoluteURL, - NSURL(string: "https://example.com////foo/bar/?p=1") + URL(string: "https://example.com////foo/bar/?p=1") ) XCTAssertEqual( TestRequest(baseURL: "https://example.com///", path: "foo//bar//").absoluteURL, - NSURL(string: "https://example.com///foo//bar//") + URL(string: "https://example.com///foo//bar//") ) } func testInterceptURLRequest() { - let URL = NSURL(string: "https://example.com/customize")! + let url = URL(string: "https://example.com/customize")! let request = TestRequest() { _ in - return NSMutableURLRequest(URL: URL) + return URLRequest(url: url) } - XCTAssertEqual((try? request.buildURLRequest())?.URL, URL) + XCTAssertEqual((try? request.buildURLRequest())?.url, url) } } diff --git a/Tests/APIKit/SessionAdapterType/NSURLSessionAdapterSubclassTests.swift b/Tests/APIKit/SessionAdapterType/NSURLSessionAdapterSubclassTests.swift deleted file mode 100644 index b01274a0..00000000 --- a/Tests/APIKit/SessionAdapterType/NSURLSessionAdapterSubclassTests.swift +++ /dev/null @@ -1,62 +0,0 @@ -import Foundation -import XCTest -import OHHTTPStubs -import APIKit - -class NSURLSessionAdapterSubclassTests: XCTestCase { - class SessionAdapter: NSURLSessionAdapter { - var functionCallFlags = [String: Bool]() - - override func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError connectionError: NSError?) { - functionCallFlags[(#function)] = true - super.URLSession(session, task: task, didCompleteWithError: connectionError) - } - - override func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) { - functionCallFlags[(#function)] = true - super.URLSession(session, dataTask: dataTask, didReceiveData: data) - } - } - - var adapter: SessionAdapter! - var session: Session! - - override func setUp() { - super.setUp() - - let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() - adapter = SessionAdapter(configuration: configuration) - session = Session(adapter: adapter) - } - - override func tearDown() { - OHHTTPStubs.removeAllStubs() - super.tearDown() - } - - func testDelegateMethodCall() { - let data = try! NSJSONSerialization.dataWithJSONObject([:], options: []) - - OHHTTPStubs.stubRequestsPassingTest({ request in - return true - }, withStubResponse: { request in - return OHHTTPStubsResponse(data: data, statusCode: 200, headers: nil) - }) - - let expectation = expectationWithDescription("wait for response") - let request = TestRequest() - - session.sendRequest(request) { result in - if case .Failure = result { - XCTFail() - } - - expectation.fulfill() - } - - waitForExpectationsWithTimeout(10.0, handler: nil) - - XCTAssertEqual(adapter.functionCallFlags["URLSession(_:task:didCompleteWithError:)"], true) - XCTAssertEqual(adapter.functionCallFlags["URLSession(_:dataTask:didReceiveData:)"], true) - } -} diff --git a/Tests/APIKit/SessionAdapterType/URLSessionAdapterSubclassTests.swift b/Tests/APIKit/SessionAdapterType/URLSessionAdapterSubclassTests.swift new file mode 100644 index 00000000..60c8f2e1 --- /dev/null +++ b/Tests/APIKit/SessionAdapterType/URLSessionAdapterSubclassTests.swift @@ -0,0 +1,62 @@ +import Foundation +import XCTest +import OHHTTPStubs +import APIKit + +class URLSessionAdapterSubclassTests: XCTestCase { + class SessionAdapter: URLSessionAdapter { + var functionCallFlags = [String: Bool]() + + override func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + functionCallFlags[(#function)] = true + super.urlSession(session, task: task, didCompleteWithError: error) + } + + override func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + functionCallFlags[(#function)] = true + super.urlSession(session, dataTask: dataTask, didReceive: data) + } + } + + var adapter: SessionAdapter! + var session: Session! + + override func setUp() { + super.setUp() + + let configuration = URLSessionConfiguration.default + adapter = SessionAdapter(configuration: configuration) + session = Session(adapter: adapter) + } + + override func tearDown() { + OHHTTPStubs.removeAllStubs() + super.tearDown() + } + + func testDelegateMethodCall() { + let data = try! JSONSerialization.data(withJSONObject: [:], options: []) + + OHHTTPStubs.stubRequests(passingTest: { request in + return true + }, withStubResponse: { request in + return OHHTTPStubsResponse(data: data, statusCode: 200, headers: nil) + }) + + let expectation = self.expectation(description: "wait for response") + let request = TestRequest() + + session.send(request) { result in + if case .failure = result { + XCTFail() + } + + expectation.fulfill() + } + + waitForExpectations(timeout: 10.0, handler: nil) + + XCTAssertEqual(adapter.functionCallFlags["urlSession(_:task:didCompleteWithError:)"], true) + XCTAssertEqual(adapter.functionCallFlags["urlSession(_:dataTask:didReceive:)"], true) + } +} diff --git a/Tests/APIKit/SessionAdapterType/NSURLSessionAdapterTests.swift b/Tests/APIKit/SessionAdapterType/URLSessionAdapterTests.swift similarity index 55% rename from Tests/APIKit/SessionAdapterType/NSURLSessionAdapterTests.swift rename to Tests/APIKit/SessionAdapterType/URLSessionAdapterTests.swift index e3421f21..ba5ba38a 100644 --- a/Tests/APIKit/SessionAdapterType/NSURLSessionAdapterTests.swift +++ b/Tests/APIKit/SessionAdapterType/URLSessionAdapterTests.swift @@ -3,14 +3,14 @@ import APIKit import XCTest import OHHTTPStubs -class NSURLSessionAdapterTests: XCTestCase { +class URLSessionAdapterTests: XCTestCase { var session: Session! override func setUp() { super.setUp() - let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() - let adapter = NSURLSessionAdapter(configuration: configuration) + let configuration = URLSessionConfiguration.default + let adapter = URLSessionAdapter(configuration: configuration) session = Session(adapter: adapter) } @@ -22,52 +22,52 @@ class NSURLSessionAdapterTests: XCTestCase { // MARK: - integration tests func testSuccess() { let dictionary = ["key": "value"] - let data = try! NSJSONSerialization.dataWithJSONObject(dictionary, options: []) + let data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) - OHHTTPStubs.stubRequestsPassingTest({ request in + OHHTTPStubs.stubRequests(passingTest: { request in return true }, withStubResponse: { request in return OHHTTPStubsResponse(data: data, statusCode: 200, headers: nil) }) - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request) { response in + session.send(request) { response in switch response { - case .Success(let dictionary): - XCTAssertEqual(dictionary["key"], "value") + case .success(let dictionary): + XCTAssertEqual((dictionary as? [String: String])?["key"], "value") - case .Failure: + case .failure: XCTFail() } expectation.fulfill() } - waitForExpectationsWithTimeout(10.0, handler: nil) + waitForExpectations(timeout: 10.0, handler: nil) } func testConnectionError() { let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorTimedOut, userInfo: nil) - OHHTTPStubs.stubRequestsPassingTest({ request in + OHHTTPStubs.stubRequests(passingTest: { request in return true }, withStubResponse: { request in return OHHTTPStubsResponse(error: error) }) - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request) { response in + session.send(request) { response in switch response { - case .Success: + case .success: XCTFail() - case .Failure(let error): + case .failure(let error): switch error { - case .ConnectionError(let error as NSError): + case .connectionError(let error as NSError): XCTAssertEqual(error.domain, NSURLErrorDomain) default: @@ -78,24 +78,24 @@ class NSURLSessionAdapterTests: XCTestCase { expectation.fulfill() } - waitForExpectationsWithTimeout(10.0, handler: nil) + waitForExpectations(timeout: 10.0, handler: nil) } func testCancel() { - let data = try! NSJSONSerialization.dataWithJSONObject([:], options: []) + let data = try! JSONSerialization.data(withJSONObject: [:], options: []) - OHHTTPStubs.stubRequestsPassingTest({ request in + OHHTTPStubs.stubRequests(passingTest: { request in return true }, withStubResponse: { request in return OHHTTPStubsResponse(data: data, statusCode: 200, headers: nil).responseTime(1.0) }) - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request) { result in - guard case .Failure(let error) = result, - case .ConnectionError(let connectionError as NSError) = error else { + session.send(request) { result in + guard case .failure(let error) = result, + case .connectionError(let connectionError as NSError) = error else { XCTFail() return } @@ -105,10 +105,10 @@ class NSURLSessionAdapterTests: XCTestCase { expectation.fulfill() } - dispatch_async(dispatch_get_main_queue()) { - self.session.cancelRequest(TestRequest.self) + DispatchQueue.main.async { + self.session.cancelRequests(withType: TestRequest.self) } - waitForExpectationsWithTimeout(10.0, handler: nil) + waitForExpectations(timeout: 10.0, handler: nil) } -} \ No newline at end of file +} diff --git a/Tests/APIKit/SessionCallbackQueueTests.swift b/Tests/APIKit/SessionCallbackQueueTests.swift index 7eb6ce31..fa300e1f 100644 --- a/Tests/APIKit/SessionCallbackQueueTests.swift +++ b/Tests/APIKit/SessionCallbackQueueTests.swift @@ -11,91 +11,91 @@ class SessionCallbackQueueTests: XCTestCase { super.setUp() adapter = TestSessionAdapter() - adapter.data = try! NSJSONSerialization.dataWithJSONObject(["key": "value"], options: []) + adapter.data = try! JSONSerialization.data(withJSONObject: ["key": "value"], options: []) - session = Session(adapter: adapter, callbackQueue: .Main) + session = Session(adapter: adapter, callbackQueue: .main) } func testMain() { - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request, callbackQueue: .Main) { result in - XCTAssert(NSThread.isMainThread()) + session.send(request, callbackQueue: .main) { result in + XCTAssert(Thread.isMainThread) expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } func testSessionQueue() { - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request, callbackQueue: .SessionQueue) { result in + session.send(request, callbackQueue: .sessionQueue) { result in // This depends on implementation of TestSessionAdapter - XCTAssert(NSThread.isMainThread()) + XCTAssert(Thread.isMainThread) expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } func testOperationQueue() { - let operationQueue = NSOperationQueue() - let expectation = expectationWithDescription("wait for response") + let operationQueue = OperationQueue() + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request, callbackQueue: .OperationQueue(operationQueue)) { result in - XCTAssertEqual(NSOperationQueue.currentQueue(), operationQueue) + session.send(request, callbackQueue: .operationQueue(operationQueue)) { result in + XCTAssertEqual(OperationQueue.current, operationQueue) expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } func testDispatchQueue() { - let dispatchQueue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0) - let expectation = expectationWithDescription("wait for response") + let dispatchQueue = DispatchQueue.global(qos: .default) + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request, callbackQueue: .DispatchQueue(dispatchQueue)) { result in + session.send(request, callbackQueue: .dispatchQueue(dispatchQueue)) { result in // There is no way to test current dispatch queue. - XCTAssert(!NSThread.isMainThread()) + XCTAssert(!Thread.isMainThread) expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } // MARK: Test Session.callbackQueue func testImplicitSessionCallbackQueue() { - let operationQueue = NSOperationQueue() - let session = Session(adapter: adapter, callbackQueue: .OperationQueue(operationQueue)) + let operationQueue = OperationQueue() + let session = Session(adapter: adapter, callbackQueue: .operationQueue(operationQueue)) - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request) { result in - XCTAssertEqual(NSOperationQueue.currentQueue(), operationQueue) + session.send(request) { result in + XCTAssertEqual(OperationQueue.current, operationQueue) expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } func testExplicitSessionCallbackQueue() { - let operationQueue = NSOperationQueue() - let session = Session(adapter: adapter, callbackQueue: .OperationQueue(operationQueue)) + let operationQueue = OperationQueue() + let session = Session(adapter: adapter, callbackQueue: .operationQueue(operationQueue)) - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request, callbackQueue: nil) { result in - XCTAssertEqual(NSOperationQueue.currentQueue(), operationQueue) + session.send(request, callbackQueue: nil) { result in + XCTAssertEqual(OperationQueue.current, operationQueue) expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } } diff --git a/Tests/APIKit/SessionTests.swift b/Tests/APIKit/SessionTests.swift index e6af1e38..7d154284 100644 --- a/Tests/APIKit/SessionTests.swift +++ b/Tests/APIKit/SessionTests.swift @@ -17,36 +17,36 @@ class SessionTests: XCTestCase { func testSuccess() { let dictionary = ["key": "value"] - adapter.data = try! NSJSONSerialization.dataWithJSONObject(dictionary, options: []) + adapter.data = try! JSONSerialization.data(withJSONObject: dictionary, options: []) - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request) { response in + session.send(request) { response in switch response { - case .Success(let dictionary): - XCTAssertEqual(dictionary["key"], "value") + case .success(let dictionary): + XCTAssertEqual((dictionary as? [String: String])?["key"], "value") - case .Failure: + case .failure: XCTFail() } expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } // MARK: Response error func testParseDataError() { - adapter.data = "{\"broken\": \"json}".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) + adapter.data = "{\"broken\": \"json}".data(using: .utf8, allowLossyConversion: false) - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request) { result in - if case .Failure(let error) = result, - case .ResponseError(let responseError as NSError) = error { + session.send(request) { result in + if case .failure(let error) = result, + case .responseError(let responseError as NSError) = error { XCTAssertEqual(responseError.domain, NSCocoaErrorDomain) XCTAssertEqual(responseError.code, 3840) } else { @@ -56,19 +56,19 @@ class SessionTests: XCTestCase { expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } func testUnacceptableStatusCodeError() { - adapter.URLResponse = NSHTTPURLResponse(URL: NSURL(), statusCode: 400, HTTPVersion: nil, headerFields: nil) + adapter.urlResponse = HTTPURLResponse(url: NSURL(string: "")! as URL, statusCode: 400, httpVersion: nil, headerFields: nil) - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request) { result in - if case .Failure(let error) = result, - case .ResponseError(let responseError as ResponseError) = error, - case .UnacceptableStatusCode(let statusCode) = responseError { + session.send(request) { result in + if case .failure(let error) = result, + case .responseError(let responseError as ResponseError) = error, + case .unacceptableStatusCode(let statusCode) = responseError { XCTAssertEqual(statusCode, 400) } else { XCTFail() @@ -77,20 +77,20 @@ class SessionTests: XCTestCase { expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } func testNonHTTPURLResponseError() { - adapter.URLResponse = NSURLResponse() + adapter.urlResponse = URLResponse() - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request) { result in - if case .Failure(let error) = result, - case .ResponseError(let responseError as ResponseError) = error, - case .NonHTTPURLResponse(let URLResponse) = responseError { - XCTAssert(URLResponse === self.adapter.URLResponse) + session.send(request) { result in + if case .failure(let error) = result, + case .responseError(let responseError as ResponseError) = error, + case .nonHTTPURLResponse(let urlResponse) = responseError { + XCTAssert(urlResponse === self.adapter.urlResponse) } else { XCTFail() } @@ -98,21 +98,21 @@ class SessionTests: XCTestCase { expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } // MARK: Request error func testRequestError() { - struct Error: ErrorType {} + struct Error: Swift.Error {} - let expectation = expectationWithDescription("wait for response") - let request = TestRequest() { URLRequest in + let expectation = self.expectation(description: "wait for response") + let request = TestRequest() { urlRequest in throw Error() } - session.sendRequest(request) { result in - if case .Failure(let error) = result, - case .RequestError(let requestError) = error { + session.send(request) { result in + if case .failure(let error) = result, + case .requestError(let requestError) = error { XCTAssert(requestError is Error) } else { XCTFail() @@ -121,18 +121,18 @@ class SessionTests: XCTestCase { expectation.fulfill() } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } // MARK: Cancel func testCancel() { - let expectation = expectationWithDescription("wait for response") + let expectation = self.expectation(description: "wait for response") let request = TestRequest() - session.sendRequest(request) { result in - if case .Failure(let error) = result, - case .ConnectionError(let connectionError as NSError) = error { + session.send(request) { result in + if case .failure(let error) = result, + case .connectionError(let connectionError as NSError) = error { XCTAssertEqual(connectionError.code, 0) } else { XCTFail() @@ -141,87 +141,87 @@ class SessionTests: XCTestCase { expectation.fulfill() } - session.cancelRequest(TestRequest.self) + session.cancelRequests(withType: TestRequest.self) - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } func testCancelFilter() { - let successExpectation = expectationWithDescription("wait for response") + let successExpectation = expectation(description: "wait for response") let successRequest = TestRequest(path: "/success") - session.sendRequest(successRequest) { result in - if case .Failure = result { + session.send(successRequest) { result in + if case .failure = result { XCTFail() } successExpectation.fulfill() } - let failureExpectation = expectationWithDescription("wait for response") + let failureExpectation = expectation(description: "wait for response") let failureRequest = TestRequest(path: "/failure") - session.sendRequest(failureRequest) { result in - if case .Success = result { + session.send(failureRequest) { result in + if case .success = result { XCTFail() } failureExpectation.fulfill() } - session.cancelRequest(TestRequest.self) { request in + session.cancelRequests(withType: TestRequest.self) { request in return request.path == failureRequest.path } - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } - struct AnotherTestRequest: RequestType { + struct AnotherTestRequest: Request { typealias Response = Void - var baseURL: NSURL { - return NSURL(string: "https://example.com")! + var baseURL: URL { + return URL(string: "https://example.com")! } var method: HTTPMethod { - return .GET + return .get } var path: String { return "/" } - func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response { + func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response { return () } } - func testCancelOtherRequestType() { - let successExpectation = expectationWithDescription("wait for response") + func testCancelOtherRequest() { + let successExpectation = expectation(description: "wait for response") let successRequest = AnotherTestRequest() - session.sendRequest(successRequest) { result in - if case .Failure = result { + session.send(successRequest) { result in + if case .failure = result { XCTFail() } successExpectation.fulfill() } - let failureExpectation = expectationWithDescription("wait for response") + let failureExpectation = expectation(description: "wait for response") let failureRequest = TestRequest() - session.sendRequest(failureRequest) { result in - if case .Success = result { + session.send(failureRequest) { result in + if case .success = result { XCTFail() } failureExpectation.fulfill() } - session.cancelRequest(TestRequest.self) + session.cancelRequests(withType: TestRequest.self) - waitForExpectationsWithTimeout(1.0, handler: nil) + waitForExpectations(timeout: 1.0, handler: nil) } // MARK: Class methods @@ -239,21 +239,21 @@ class SessionTests: XCTestCase { return testSesssion } - private override func sendRequest(request: Request, callbackQueue: CallbackQueue?, handler: (Result) -> Void) -> SessionTaskType? { + private override func send(_ request: Req, callbackQueue: CallbackQueue?, handler: @escaping (Result) -> Void) -> SessionTaskType? { functionCallFlags[(#function)] = true - return super.sendRequest(request) + return super.send(request) } - private override func cancelRequest(requestType: Request.Type, passingTest test: Request -> Bool) { + private override func cancelRequests(withType requestType: Req.Type, passingTest test: @escaping (Req) -> Bool) { functionCallFlags[(#function)] = true } } let testSession = SessionSubclass.testSesssion - SessionSubclass.sendRequest(TestRequest()) - SessionSubclass.cancelRequest(TestRequest.self) + SessionSubclass.send(TestRequest()) + SessionSubclass.cancelRequests(withType: TestRequest.self) - XCTAssertEqual(testSession.functionCallFlags["sendRequest(_:callbackQueue:handler:)"], true) - XCTAssertEqual(testSession.functionCallFlags["cancelRequest(_:passingTest:)"], true) + XCTAssertEqual(testSession.functionCallFlags["send(_:callbackQueue:handler:)"], true) + XCTAssertEqual(testSession.functionCallFlags["cancelRequests(withType:passingTest:)"], true) } } diff --git a/Tests/APIKit/TestComponents/TestRequest.swift b/Tests/APIKit/TestComponents/TestRequest.swift index a9e5ea31..e3bc69aa 100644 --- a/Tests/APIKit/TestComponents/TestRequest.swift +++ b/Tests/APIKit/TestComponents/TestRequest.swift @@ -1,17 +1,17 @@ import Foundation import APIKit -struct TestRequest: RequestType { - var absoluteURL: NSURL? { - let URLRequest = try? buildURLRequest() - return URLRequest?.URL +struct TestRequest: Request { + var absoluteURL: URL? { + let urlRequest = try? buildURLRequest() + return urlRequest?.url } - // MARK: RequestType - typealias Response = AnyObject + // MARK: Request + typealias Response = Any - init(baseURL: String = "https://example.com", path: String = "/", method: HTTPMethod = .GET, parameters: AnyObject? = [:], headerFields: [String: String] = [:], interceptURLRequest: NSMutableURLRequest throws -> NSMutableURLRequest = { $0 }) { - self.baseURL = NSURL(string: baseURL)! + init(baseURL: String = "https://example.com", path: String = "/", method: HTTPMethod = .get, parameters: Any? = [:], headerFields: [String: String] = [:], interceptURLRequest: @escaping (URLRequest) throws -> URLRequest = { $0 }) { + self.baseURL = URL(string: baseURL)! self.path = path self.method = method self.parameters = parameters @@ -19,18 +19,18 @@ struct TestRequest: RequestType { self.interceptURLRequest = interceptURLRequest } - let baseURL: NSURL + let baseURL: URL let method: HTTPMethod let path: String - let parameters: AnyObject? + let parameters: Any? let headerFields: [String: String] - let interceptURLRequest: NSMutableURLRequest throws -> NSMutableURLRequest + let interceptURLRequest: (URLRequest) throws -> URLRequest - func interceptURLRequest(URLRequest: NSMutableURLRequest) throws -> NSMutableURLRequest { - return try interceptURLRequest(URLRequest) + func intercept(urlRequest: URLRequest) throws -> URLRequest { + return try interceptURLRequest(urlRequest) } - func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response { + func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response { return object } } diff --git a/Tests/APIKit/TestComponents/TestSessionAdapter.swift b/Tests/APIKit/TestComponents/TestSessionAdapter.swift index 76b7b1c6..171996c2 100644 --- a/Tests/APIKit/TestComponents/TestSessionAdapter.swift +++ b/Tests/APIKit/TestComponents/TestSessionAdapter.swift @@ -1,14 +1,14 @@ import Foundation import APIKit -class TestSessionAdapter: SessionAdapterType { - enum Error: ErrorType { - case Cancelled +class TestSessionAdapter: SessionAdapter { + enum Error: Swift.Error { + case cancelled } - var data: NSData? - var URLResponse: NSURLResponse? - var error: ErrorType? + var data: Data? + var urlResponse: URLResponse? + var error: Error? private class Runner { weak var adapter: TestSessionAdapter? @@ -20,15 +20,15 @@ class TestSessionAdapter: SessionAdapterType { private var tasks = [TestSessionTask]() private let runner: Runner - private let timer: NSTimer + private let timer: Timer - init(data: NSData? = NSData(), URLResponse: NSURLResponse? = NSHTTPURLResponse(URL: NSURL(), statusCode: 200, HTTPVersion: nil, headerFields: nil), error: NSError? = nil) { + init(data: Data? = Data(), urlResponse: URLResponse? = HTTPURLResponse(url: NSURL(string: "")! as URL, statusCode: 200, httpVersion: nil, headerFields: nil), error: Error? = nil) { self.data = data - self.URLResponse = URLResponse + self.urlResponse = urlResponse self.error = error self.runner = Runner() - self.timer = NSTimer.scheduledTimerWithTimeInterval(0.001, + self.timer = Timer.scheduledTimer(timeInterval: 0.001, target: runner, selector: #selector(Runner.run), userInfo: nil, @@ -40,23 +40,23 @@ class TestSessionAdapter: SessionAdapterType { func executeAllTasks() { for task in tasks { if task.cancelled { - task.handler(nil, nil, Error.Cancelled) + task.handler(nil, nil, Error.cancelled) } else { - task.handler(data, URLResponse, error) + task.handler(data, urlResponse, error) } } tasks = [] } - func createTaskWithURLRequest(URLRequest: NSURLRequest, handler: (NSData?, NSURLResponse?, ErrorType?) -> Void) -> SessionTaskType { + func createTask(with URLRequest: URLRequest, handler: @escaping (Data?, URLResponse?, Swift.Error?) -> Void) -> SessionTaskType { let task = TestSessionTask(handler: handler) tasks.append(task) return task } - func getTasksWithHandler(handler: [SessionTaskType] -> Void) { + func getTasks(with handler: @escaping ([SessionTaskType]) -> Void) { handler(tasks.map { $0 }) } } diff --git a/Tests/APIKit/TestComponents/TestSessionTask.swift b/Tests/APIKit/TestComponents/TestSessionTask.swift index 15bf3add..50e96ed8 100644 --- a/Tests/APIKit/TestComponents/TestSessionTask.swift +++ b/Tests/APIKit/TestComponents/TestSessionTask.swift @@ -3,10 +3,10 @@ import APIKit class TestSessionTask: SessionTaskType { - var handler: (NSData?, NSURLResponse?, ErrorType?) -> Void + var handler: (Data?, URLResponse?, Error?) -> Void var cancelled = false - init(handler: (NSData?, NSURLResponse?, ErrorType?) -> Void) { + init(handler: @escaping (Data?, URLResponse?, Error?) -> Void) { self.handler = handler }