diff --git a/.gitmodules b/.gitmodules index 066d45e..e46fa76 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "Carthage/Checkouts/Result"] path = Carthage/Checkouts/Result url = https://github.com/antitypical/Result.git -[submodule "Carthage/Checkouts/ReactiveCocoa"] - path = Carthage/Checkouts/ReactiveCocoa - url = https://github.com/ReactiveCocoa/ReactiveCocoa.git [submodule "Carthage/Checkouts/Argo"] path = Carthage/Checkouts/Argo url = https://github.com/thoughtbot/Argo.git @@ -13,3 +10,9 @@ [submodule "Carthage/Checkouts/OHHTTPStubs"] path = Carthage/Checkouts/OHHTTPStubs url = https://github.com/AliSoftware/OHHTTPStubs.git +[submodule "Carthage/Checkouts/ReactiveSwift"] + path = Carthage/Checkouts/ReactiveSwift + url = https://github.com/ReactiveCocoa/ReactiveSwift.git +[submodule "Carthage/Checkouts/Runes"] + path = Carthage/Checkouts/Runes + url = https://github.com/thoughtbot/Runes.git diff --git a/.travis.yml b/.travis.yml index c6b3157..c94454c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,15 +10,6 @@ script: script/cibuild "$TRAVIS_XCODE_WORKSPACE" "$TRAVIS_XCODE_SCHEME" "$XCODE_ xcode_workspace: Tentacle.xcworkspace matrix: include: - - xcode_scheme: Tentacle-OSX - env: XCODE_ACTION=test - osx_image: xcode7.3 - - xcode_scheme: Tentacle-iOS - env: XCODE_ACTION=test - osx_image: xcode7.3 - - xcode_scheme: update-test-fixtures - env: XCODE_ACTION=build - osx_image: xcode7.3 - xcode_scheme: Tentacle-OSX env: XCODE_ACTION="build-for-testing test-without-building" osx_image: xcode8 diff --git a/Cartfile b/Cartfile index 0245a0b..c09492e 100644 --- a/Cartfile +++ b/Cartfile @@ -1,3 +1,3 @@ -github "ReactiveCocoa/ReactiveCocoa" ~> 4.2.2 -github "thoughtbot/Argo" ~> 3.0.3 -github "thoughtbot/Curry" ~> 2.3.3 +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" +github "thoughtbot/Argo" ~> 4.1 +github "thoughtbot/Curry" ~> 3.0 diff --git a/Cartfile.private b/Cartfile.private index 7f203a0..85088bd 100644 --- a/Cartfile.private +++ b/Cartfile.private @@ -1 +1 @@ -github "AliSoftware/OHHTTPStubs" "4995ecd" +github "AliSoftware/OHHTTPStubs" "5.2.2-swift3" diff --git a/Cartfile.resolved b/Cartfile.resolved index 952b916..64837f9 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,6 @@ -github "thoughtbot/Argo" "v3.0.3" -github "thoughtbot/Curry" "v2.3.3" -github "AliSoftware/OHHTTPStubs" "4995ecd762abdd81227d14faf65fde003fbbe789" -github "antitypical/Result" "2.1.3" -github "ReactiveCocoa/ReactiveCocoa" "v4.2.2" +github "thoughtbot/Curry" "v3.0.0" +github "AliSoftware/OHHTTPStubs" "5.2.2-swift3" +github "antitypical/Result" "3.0.0" +github "thoughtbot/Runes" "v4.0.1" +github "thoughtbot/Argo" "v4.1.0" +github "ReactiveCocoa/ReactiveSwift" "1.0.0-alpha.3" diff --git a/Carthage/Checkouts/Argo b/Carthage/Checkouts/Argo index ecd43f7..56cf43e 160000 --- a/Carthage/Checkouts/Argo +++ b/Carthage/Checkouts/Argo @@ -1 +1 @@ -Subproject commit ecd43f7245780dfd275bc044392afef66e5786a6 +Subproject commit 56cf43e0f251b74c05aebe1f6ad2de36f07a0580 diff --git a/Carthage/Checkouts/Curry b/Carthage/Checkouts/Curry index 2fdec50..c9f1a37 160000 --- a/Carthage/Checkouts/Curry +++ b/Carthage/Checkouts/Curry @@ -1 +1 @@ -Subproject commit 2fdec50caebc5dccd5d026e6d69d433a9eb86df4 +Subproject commit c9f1a37e0e12ad38af7933b9386bdc5f9e6222dc diff --git a/Carthage/Checkouts/OHHTTPStubs b/Carthage/Checkouts/OHHTTPStubs index 4995ecd..45a2cea 160000 --- a/Carthage/Checkouts/OHHTTPStubs +++ b/Carthage/Checkouts/OHHTTPStubs @@ -1 +1 @@ -Subproject commit 4995ecd762abdd81227d14faf65fde003fbbe789 +Subproject commit 45a2ceaebaef5ae434ffd3e2a5aef4c5ce3fa91b diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa deleted file mode 160000 index f214c9d..0000000 --- a/Carthage/Checkouts/ReactiveCocoa +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f214c9d508db77f751c3af5be967c4cb235fc0a1 diff --git a/Carthage/Checkouts/ReactiveSwift b/Carthage/Checkouts/ReactiveSwift new file mode 160000 index 0000000..55345eb --- /dev/null +++ b/Carthage/Checkouts/ReactiveSwift @@ -0,0 +1 @@ +Subproject commit 55345ebd4ec28baeacb4041a99d56839428bcaff diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 9b5e373..df233a8 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 9b5e373891dfe0de11ba2a8e9a5cafd570b858c4 +Subproject commit df233a8d23a545153d5b5de13b1ac8db2503f088 diff --git a/Carthage/Checkouts/Runes b/Carthage/Checkouts/Runes new file mode 160000 index 0000000..727fcfe --- /dev/null +++ b/Carthage/Checkouts/Runes @@ -0,0 +1 @@ +Subproject commit 727fcfe8d79ec92ce989a96329147190d20f7cd2 diff --git a/README.md b/README.md index 9cc0be7..871d6ab 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ client } ``` -Tentacle is built with [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa). +Tentacle is built with [ReactiveSwift](https://github.com/ReactiveCocoa/ReactiveSwift). ## License Tentacle is available under the [MIT License](LICENSE.md) diff --git a/Tentacle.xcodeproj/project.pbxproj b/Tentacle.xcodeproj/project.pbxproj index e1d66e8..e5a714f 100644 --- a/Tentacle.xcodeproj/project.pbxproj +++ b/Tentacle.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 61377C741C8A2B440081FF24 /* Argo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE88E85B1C88ED750034A112 /* Argo.framework */; }; 61377C751C8A2B440081FF24 /* Curry.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE88E85C1C88ED750034A112 /* Curry.framework */; }; - 61377C761C8A2B440081FF24 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE88E85D1C88ED750034A112 /* ReactiveCocoa.framework */; }; 61377C771C8A2B440081FF24 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE88E85E1C88ED750034A112 /* Result.framework */; }; 61377C781C8A2B760081FF24 /* Tentacle.h in Headers */ = {isa = PBXBuildFile; fileRef = BE88E7F51C88C6B30034A112 /* Tentacle.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61377C791C8A2B760081FF24 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE88E80C1C88C72B0034A112 /* Client.swift */; }; @@ -101,7 +100,6 @@ BE88E7F61C88C6B30034A112 /* Tentacle.h in Headers */ = {isa = PBXBuildFile; fileRef = BE88E7F51C88C6B30034A112 /* Tentacle.h */; settings = {ATTRIBUTES = (Public, ); }; }; BE88E7FD1C88C6B30034A112 /* Tentacle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE88E7F21C88C6B30034A112 /* Tentacle.framework */; }; BE88E80D1C88C72B0034A112 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE88E80C1C88C72B0034A112 /* Client.swift */; }; - BE88E82A1C88C8850034A112 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE88E8281C88C8850034A112 /* ReactiveCocoa.framework */; }; BE88E82B1C88C8850034A112 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE88E8291C88C8850034A112 /* Result.framework */; }; BE88E82D1C88C94D0034A112 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE88E82C1C88C94D0034A112 /* Server.swift */; }; BE88E8301C88CDAB0034A112 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE88E82F1C88CDAB0034A112 /* Repository.swift */; }; @@ -139,6 +137,12 @@ BEEE47461C91BB3A000FFC21 /* ArgoExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEEE47441C91BB3A000FFC21 /* ArgoExtensions.swift */; }; BEEE474E1C92623E000FFC21 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEEE474D1C92623E000FFC21 /* Response.swift */; }; BEEE474F1C92623E000FFC21 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEEE474D1C92623E000FFC21 /* Response.swift */; }; + CD5AE50A1DCDC6B600AD5EFB /* Runes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD5AE5091DCDC6B600AD5EFB /* Runes.framework */; }; + CD5AE50C1DCDC6BF00AD5EFB /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD5AE50B1DCDC6BF00AD5EFB /* ReactiveSwift.framework */; }; + CD5AE50E1DCDC71F00AD5EFB /* Runes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD5AE50D1DCDC71F00AD5EFB /* Runes.framework */; }; + CD5AE5101DCDC72B00AD5EFB /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD5AE50F1DCDC72B00AD5EFB /* ReactiveSwift.framework */; }; + CD83C7BA1DCE379D00BC2608 /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD83C7B91DCE379D00BC2608 /* Availability.swift */; }; + CD83C7BB1DCE379D00BC2608 /* Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD83C7B91DCE379D00BC2608 /* Availability.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -196,8 +200,8 @@ BE1E036A1C9AD87F001296C2 /* ResponseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseTests.swift; sourceTree = ""; }; BE1E036D1C9CF849001296C2 /* repos-mdiep-MDPSplitView-releases-tags-1.0.2.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "repos-mdiep-MDPSplitView-releases-tags-1.0.2.data"; sourceTree = ""; }; BE1E036E1C9CF849001296C2 /* repos-mdiep-MDPSplitView-releases-tags-1.0.2.response */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "repos-mdiep-MDPSplitView-releases-tags-1.0.2.response"; sourceTree = ""; }; - BE2B6A671C8B84CD0080BFEB /* OHHTTPStubs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OHHTTPStubs.framework; path = "Carthage/Checkouts/OHHTTPStubs/OHHTTPStubs/build/Debug-iphoneos/OHHTTPStubs.framework"; sourceTree = SOURCE_ROOT; }; - BE2B6A691C8B84D60080BFEB /* OHHTTPStubs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OHHTTPStubs.framework; path = Carthage/Checkouts/OHHTTPStubs/OHHTTPStubs/build/Debug/OHHTTPStubs.framework; sourceTree = SOURCE_ROOT; }; + BE2B6A671C8B84CD0080BFEB /* OHHTTPStubs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OHHTTPStubs.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BE2B6A691C8B84D60080BFEB /* OHHTTPStubs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OHHTTPStubs.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BE2B6A6B1C8B854F0080BFEB /* ClientTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientTests.swift; sourceTree = ""; }; BE88E7F21C88C6B30034A112 /* Tentacle.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Tentacle.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BE88E7F51C88C6B30034A112 /* Tentacle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Tentacle.h; sourceTree = ""; }; @@ -205,17 +209,15 @@ BE88E7FC1C88C6B30034A112 /* Tentacle-OSXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tentacle-OSXTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; BE88E8031C88C6B30034A112 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BE88E80C1C88C72B0034A112 /* Client.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; - BE88E8281C88C8850034A112 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = Carthage/Checkouts/ReactiveCocoa/build/Debug/ReactiveCocoa.framework; sourceTree = SOURCE_ROOT; }; - BE88E8291C88C8850034A112 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Checkouts/Result/build/Debug/Result.framework; sourceTree = SOURCE_ROOT; }; + BE88E8291C88C8850034A112 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BE88E82C1C88C94D0034A112 /* Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Server.swift; sourceTree = ""; }; BE88E82F1C88CDAB0034A112 /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repository.swift; sourceTree = ""; }; BE88E8311C88D33D0034A112 /* Release.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Release.swift; sourceTree = ""; }; - BE88E84B1C88E6D60034A112 /* Argo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Argo.framework; path = Carthage/Checkouts/Argo/build/Debug/Argo.framework; sourceTree = SOURCE_ROOT; }; - BE88E84C1C88E6D60034A112 /* Curry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Curry.framework; path = Carthage/Checkouts/Curry/build/Debug/Curry.framework; sourceTree = SOURCE_ROOT; }; - BE88E85B1C88ED750034A112 /* Argo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Argo.framework; path = Carthage/Checkouts/Argo/build/Debug/Argo.framework; sourceTree = SOURCE_ROOT; }; - BE88E85C1C88ED750034A112 /* Curry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Curry.framework; path = Carthage/Checkouts/Curry/build/Debug/Curry.framework; sourceTree = SOURCE_ROOT; }; - BE88E85D1C88ED750034A112 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Tentacle-ccmhupuixpvscyhijvefkbwpmbkt/Build/Products/Debug/ReactiveCocoa.framework"; sourceTree = ""; }; - BE88E85E1C88ED750034A112 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Tentacle-ccmhupuixpvscyhijvefkbwpmbkt/Build/Products/Debug/Result.framework"; sourceTree = ""; }; + BE88E84B1C88E6D60034A112 /* Argo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Argo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BE88E84C1C88E6D60034A112 /* Curry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Curry.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BE88E85B1C88ED750034A112 /* Argo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Argo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BE88E85C1C88ED750034A112 /* Curry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Curry.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BE88E85E1C88ED750034A112 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BE88E8671C88F0990034A112 /* update-test-fixtures.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "update-test-fixtures.app"; sourceTree = BUILT_PRODUCTS_DIR; }; BE88E8701C88F0990034A112 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; BE88E8741C88F0C50034A112 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; @@ -235,6 +237,11 @@ BEEE47411C91B8DF000FFC21 /* ResourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResourceType.swift; sourceTree = ""; }; BEEE47441C91BB3A000FFC21 /* ArgoExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArgoExtensions.swift; sourceTree = ""; }; BEEE474D1C92623E000FFC21 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; + CD5AE5091DCDC6B600AD5EFB /* Runes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Runes.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CD5AE50B1DCDC6BF00AD5EFB /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CD5AE50D1DCDC71F00AD5EFB /* Runes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Runes.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CD5AE50F1DCDC72B00AD5EFB /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CD83C7B91DCE379D00BC2608 /* Availability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Availability.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -243,8 +250,9 @@ buildActionMask = 2147483647; files = ( 61377C741C8A2B440081FF24 /* Argo.framework in Frameworks */, + CD5AE50E1DCDC71F00AD5EFB /* Runes.framework in Frameworks */, 61377C751C8A2B440081FF24 /* Curry.framework in Frameworks */, - 61377C761C8A2B440081FF24 /* ReactiveCocoa.framework in Frameworks */, + CD5AE5101DCDC72B00AD5EFB /* ReactiveSwift.framework in Frameworks */, 61377C771C8A2B440081FF24 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -263,8 +271,9 @@ buildActionMask = 2147483647; files = ( BE88E84D1C88E6D60034A112 /* Argo.framework in Frameworks */, + CD5AE50A1DCDC6B600AD5EFB /* Runes.framework in Frameworks */, BE88E84E1C88E6D60034A112 /* Curry.framework in Frameworks */, - BE88E82A1C88C8850034A112 /* ReactiveCocoa.framework in Frameworks */, + CD5AE50C1DCDC6BF00AD5EFB /* ReactiveSwift.framework in Frameworks */, BE88E82B1C88C8850034A112 /* Result.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -349,6 +358,7 @@ BE88E7F41C88C6B30034A112 /* Tentacle */ = { isa = PBXGroup; children = ( + CD83C7B91DCE379D00BC2608 /* Availability.swift */, BEEE47441C91BB3A000FFC21 /* ArgoExtensions.swift */, BE88E80C1C88C72B0034A112 /* Client.swift */, 7A1F20EC1D3E85BB00F275F8 /* Color.swift */, @@ -400,12 +410,14 @@ BE2B6A691C8B84D60080BFEB /* OHHTTPStubs.framework */, BE2B6A671C8B84CD0080BFEB /* OHHTTPStubs.framework */, BE88E85B1C88ED750034A112 /* Argo.framework */, + CD5AE50D1DCDC71F00AD5EFB /* Runes.framework */, BE88E85C1C88ED750034A112 /* Curry.framework */, - BE88E85D1C88ED750034A112 /* ReactiveCocoa.framework */, + CD5AE50F1DCDC72B00AD5EFB /* ReactiveSwift.framework */, BE88E85E1C88ED750034A112 /* Result.framework */, BE88E84B1C88E6D60034A112 /* Argo.framework */, + CD5AE5091DCDC6B600AD5EFB /* Runes.framework */, BE88E84C1C88E6D60034A112 /* Curry.framework */, - BE88E8281C88C8850034A112 /* ReactiveCocoa.framework */, + CD5AE50B1DCDC6BF00AD5EFB /* ReactiveSwift.framework */, BE88E8291C88C8850034A112 /* Result.framework */, ); name = Frameworks; @@ -539,28 +551,28 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0810; ORGANIZATIONNAME = "Matt Diephouse"; TargetAttributes = { 61377C6B1C8A2B2C0081FF24 = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; 6190817C1C8A2D2B001BE2F8 = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; BE88E7F11C88C6B30034A112 = { CreatedOnToolsVersion = 7.2.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; BE88E7FB1C88C6B30034A112 = { CreatedOnToolsVersion = 7.2.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; BE88E8661C88F0990034A112 = { CreatedOnToolsVersion = 7.2.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0810; }; }; }; @@ -692,6 +704,7 @@ BEEE474F1C92623E000FFC21 /* Response.swift in Sources */, 7A2F66121CF5280200463602 /* Label.swift in Sources */, 7A2F66131CF5280600463602 /* Milestone.swift in Sources */, + CD83C7BB1DCE379D00BC2608 /* Availability.swift in Sources */, 61377C7D1C8A2B760081FF24 /* Repository.swift in Sources */, BECB8A8F1CBDD91D005D70A6 /* FoundationExtensions.swift in Sources */, 61377C7B1C8A2B760081FF24 /* GitHubError.swift in Sources */, @@ -738,6 +751,7 @@ BEEE474E1C92623E000FFC21 /* Response.swift in Sources */, 7A1A82581CF3DE4C0076E2DD /* Label.swift in Sources */, 7A1A825A1CF3DEEA0076E2DD /* Milestone.swift in Sources */, + CD83C7BA1DCE379D00BC2608 /* Availability.swift in Sources */, BE88E8321C88D33D0034A112 /* Release.swift in Sources */, BE88E80D1C88C72B0034A112 /* Client.swift in Sources */, BE0F40A41C89135D00E3B11A /* Decodable.swift in Sources */, @@ -796,7 +810,8 @@ 61377C711C8A2B2C0081FF24 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + APPLICATION_EXTENSION_API_ONLY = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -804,7 +819,6 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tentacle/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.diephouse.matt.Tentacle; PRODUCT_NAME = Tentacle; @@ -817,7 +831,8 @@ 61377C721C8A2B2C0081FF24 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + APPLICATION_EXTENSION_API_ONLY = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -825,7 +840,6 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Tentacle/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.diephouse.matt.Tentacle; PRODUCT_NAME = Tentacle; @@ -878,8 +892,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -902,13 +918,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -927,8 +943,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -945,11 +963,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; diff --git a/Tentacle.xcodeproj/xcshareddata/xcschemes/Tentacle-OSX.xcscheme b/Tentacle.xcodeproj/xcshareddata/xcschemes/Tentacle-OSX.xcscheme index 282e05c..026dbe2 100644 --- a/Tentacle.xcodeproj/xcshareddata/xcschemes/Tentacle-OSX.xcscheme +++ b/Tentacle.xcodeproj/xcshareddata/xcschemes/Tentacle-OSX.xcscheme @@ -1,6 +1,6 @@ + + + + + BuildableName = "ReactiveSwift.framework" + BlueprintName = "ReactiveSwift-iOS" + ReferencedContainer = "container:Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj"> + + @@ -14,7 +17,7 @@ location = "group:Carthage/Checkouts/Result/Result.xcodeproj"> + location = "group:Carthage/Checkouts/ReactiveSwift/ReactiveSwift.xcodeproj"> diff --git a/Tentacle/ArgoExtensions.swift b/Tentacle/ArgoExtensions.swift index 797b25e..15835df 100644 --- a/Tentacle/ArgoExtensions.swift +++ b/Tentacle/ArgoExtensions.swift @@ -10,85 +10,85 @@ import Argo import Foundation import Result -internal func decode(object: AnyObject) -> Result { +internal func decode(_ object: Any) -> Result where T == T.DecodedType { let decoded: Decoded = decode(object) switch decoded { - case let .Success(object): - return .Success(object) - case let .Failure(error): - return .Failure(error) + case let .success(object): + return .success(object) + case let .failure(error): + return .failure(error) } } -internal func decode(object: AnyObject) -> Result<[T], DecodeError> { +internal func decode(_ object: Any) -> Result<[T], DecodeError> where T == T.DecodedType { let decoded: Decoded<[T]> = decode(object) switch decoded { - case let .Success(object): - return .Success(object) - case let .Failure(error): - return .Failure(error) + case let .success(object): + return .success(object) + case let .failure(error): + return .failure(error) } } -internal func toString(number: Int) -> Decoded { - return .Success(number.description) +internal func toString(_ number: Int) -> Decoded { + return .success(number.description) } -internal func toInt(string: String) -> Decoded { +internal func toInt(_ string: String) -> Decoded { if let int = Int(string) { - return .Success(int) + return .success(int) } else { - return .Failure(.Custom("String is not a valid number")) + return .failure(.custom("String is not a valid number")) } } -internal func toIssueState(string: String) -> Decoded { +internal func toIssueState(_ string: String) -> Decoded { if let state = Issue.State(rawValue: string) { - return .Success(state) + return .success(state) } else { - return .Failure(.Custom("String \(string) does not represent a valid issue state")) + return .failure(.custom("String \(string) does not represent a valid issue state")) } } -internal func toNSDate(string: String) -> Decoded { - if let date = NSDateFormatter.ISO8601.dateFromString(string) { - return .Success(date) +internal func toDate(_ string: String) -> Decoded { + if let date = DateFormatter.iso8601.date(from: string) { + return .success(date) } else { - return .Failure(.Custom("Date is not ISO8601 formatted")) + return .failure(.custom("Date is not ISO8601 formatted")) } } -internal func toOptionalNSDate(string: String?) -> Decoded { - guard let string = string else { return .Success(nil) } - if let date = NSDateFormatter.ISO8601.dateFromString(string) { - return .Success(date) +internal func toOptionalDate(_ string: String?) -> Decoded { + guard let string = string else { return .success(nil) } + if let date = DateFormatter.iso8601.date(from: string) { + return .success(date) } else { - return .Failure(.Custom("Date is not ISO8601 formatted")) + return .failure(.custom("Date is not ISO8601 formatted")) } } -internal func toNSURL(string: String) -> Decoded { - if let url = NSURL(string: string) { - return .Success(url) +internal func toURL(_ string: String) -> Decoded { + if let url = URL(string: string) { + return .success(url) } else { - return .Failure(.Custom("URL \(string) is not properly formatted")) + return .failure(.custom("URL \(string) is not properly formatted")) } } -internal func toOptionalNSURL(string: String?) -> Decoded { - guard let string = string else { return .Success(nil) } +internal func toOptionalURL(_ string: String?) -> Decoded { + guard let string = string else { return .success(nil) } - return .Success(NSURL(string: string)) + return .success(URL(string: string)) } -internal func toColor(string: String) -> Decoded { - return .Success(Color(hex: string)) +internal func toColor(_ string: String) -> Decoded { + return .success(Color(hex: string)) } -internal func toUserType(string: String) -> Decoded { +internal func toUserType(_ string: String) -> Decoded { if let type = User.UserType(rawValue: string) { - return .Success(type) + return .success(type) } else { - return .Failure(.Custom("String \(string) does not represent a valid user type")) + return .failure(.custom("String \(string) does not represent a valid user type")) } } diff --git a/Tentacle/Availability.swift b/Tentacle/Availability.swift new file mode 100644 index 0000000..bf874fa --- /dev/null +++ b/Tentacle/Availability.swift @@ -0,0 +1,55 @@ +// +// Availability.swift +// Tentacle +// +// Created by Syo Ikeda on 11/6/16. +// Copyright © 2016 Matt Diephouse. All rights reserved. +// + +import Foundation +import ReactiveSwift + +extension DateFormatter { + @available(*, unavailable, renamed: "iso8601") + @nonobjc public static var ISO8601: DateFormatter { fatalError() } +} + +extension Release.Asset { + @available(*, unavailable, renamed: "apiURL") + public var APIURL: URL { fatalError() } +} + +extension Client { + @available(*, unavailable, renamed: "releases(in:page:perPage:)") + public func releasesInRepository(_ repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Release]), Error> { fatalError() } + + @available(*, unavailable, renamed: "release(forTag:in:)") + public func releaseForTag(_ tag: String, inRepository repository: Repository) -> SignalProducer<(Response, Release), Error> { fatalError() } + + @available(*, unavailable, renamed: "download(asset:)") + public func downloadAsset(_ asset: Release.Asset) -> SignalProducer { fatalError() } + + @available(*, unavailable, renamed: "user(login:)") + public func userWithLogin(_ login: String) -> SignalProducer<(Response, UserInfo), Error> { fatalError() } + + @available(*, unavailable, renamed: "assignedIssues(page:perPage:)") + public func assignedIssues(_ page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Issue]), Error> { fatalError() } + + @available(*, unavailable, renamed: "issues(in:page:perPage:)") + public func issuesInRepository(_ repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Issue]), Error> { fatalError() } + + @available(*, unavailable, renamed: "comments(onIssue:in:page:perPage:)") + public func commentsOnIssue(_ issue: Int, repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Comment]), Error> { fatalError() } + + @available(*, unavailable, renamed: "repositories(page:perPage:)") + public func repositories(_ page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { fatalError() } + + @available(*, unavailable, renamed: "repositories(forUser:page:perPage:)") + public func repositoriesForUser(_ user: String, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { fatalError() } + + @available(*, unavailable, renamed: "repositories(forOrganization:page:perPage:)") + public func repositoriesForOrganization(_ organization: String, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { fatalError() } + + @available(*, unavailable, renamed: "publicRepositories(page:perPage:)") + public func publicRepositories(_ page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { fatalError() } +} diff --git a/Tentacle/Client.swift b/Tentacle/Client.swift index 070face..6c4d13a 100644 --- a/Tentacle/Client.swift +++ b/Tentacle/Client.swift @@ -8,51 +8,40 @@ import Argo import Foundation -import ReactiveCocoa +import ReactiveSwift import Result -extension NSJSONSerialization { - internal static func deserializeJSON(data: NSData) -> Result { - return Result(try NSJSONSerialization.JSONObjectWithData(data, options: [])) +extension JSONSerialization { + internal static func deserializeJSON(_ data: Data) -> Result { + return Result(try JSONSerialization.jsonObject(with: data, options: [])) } } -extension NSURL { - internal func URLWithQueryItems(queryItems: [NSURLQueryItem]) -> NSURL { - let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: true)! +extension URL { + internal func url(with queryItems: [URLQueryItem]) -> URL { + var components = URLComponents(url: self, resolvingAgainstBaseURL: true)! components.queryItems = (components.queryItems ?? []) + queryItems - return components.URL! + return components.url! } - internal convenience init(_ server: Server, _ endpoint: Client.Endpoint, page: UInt? = nil, pageSize: UInt? = nil) { + internal init(_ server: Server, _ endpoint: Client.Endpoint, page: UInt? = nil, pageSize: UInt? = nil) { let queryItems = [ ("page", page), ("per_page", pageSize) ] .filter { _, value in value != nil } - .map { name, value in NSURLQueryItem(name: name, value: "\(value!)") } - - #if swift(>=2.3) - let URL = NSURL(string: server.endpoint)! - .URLByAppendingPathComponent(endpoint.path)! - .URLWithQueryItems(endpoint.queryItems) - .URLWithQueryItems(queryItems) - #else - let URL = NSURL(string: server.endpoint)! - .URLByAppendingPathComponent(endpoint.path) - .URLWithQueryItems(endpoint.queryItems) - .URLWithQueryItems(queryItems) - #endif - - #if swift(>=2.3) - self.init(string: URL.absoluteString!)! - #else - self.init(string: URL.absoluteString)! - #endif + .map { name, value in URLQueryItem(name: name, value: "\(value!)") } + + let url = URL(string: server.endpoint)! + .appendingPathComponent(endpoint.path) + .url(with: endpoint.queryItems) + .url(with: queryItems) + + self.init(string: url.absoluteString)! } } -extension NSURLRequest { - internal static func create(URL: NSURL, _ credentials: Client.Credentials?, contentType: String? = Client.APIContentType) -> NSURLRequest { - let request = NSMutableURLRequest(URL: URL) +extension URLRequest { + internal static func create(_ url: URL, _ credentials: Client.Credentials?, contentType: String? = Client.APIContentType) -> URLRequest { + var request = URLRequest(url: url) request.setValue(contentType, forHTTPHeaderField: "Accept") @@ -68,25 +57,25 @@ extension NSURLRequest { } } -extension NSURLSession { +extension URLSession { /// Returns a producer that will download a file using the given request. The file will be /// deleted after the producer terminates. - internal func downloadFile(request: NSURLRequest) -> SignalProducer { + internal func downloadFile(_ request: URLRequest) -> SignalProducer { return SignalProducer { observer, disposable in let serialDisposable = SerialDisposable() - let handle = disposable.addDisposable(serialDisposable) + let handle = disposable.add(serialDisposable) - let task = self.downloadTaskWithRequest(request) { (URL, response, error) in + let task = self.downloadTask(with: request) { (url, response, error) in // Avoid invoking cancel(), or the download may be deleted. handle.remove() - if let URL = URL { - observer.sendNext(URL) + if let url = url { + observer.send(value: url) observer.sendCompleted() } else if let error = error { - observer.sendFailed(error) + observer.send(error: error as NSError) } else { - fatalError("Request neither succeeded nor failed: \(request.URL)") + fatalError("Request neither succeeded nor failed: \(request.url)") } } @@ -109,37 +98,37 @@ public final class Client { internal static let DownloadContentType = "application/octet-stream" /// An error from the Client. - public enum Error: Hashable, ErrorType { + public enum Error: Swift.Error, Hashable { /// An error occurred in a network operation. - case NetworkError(NSError) + case networkError(NSError) /// An error occurred while deserializing JSON. - case JSONDeserializationError(NSError) + case jsonDeserializationError(NSError) /// An error occurred while decoding JSON. - case JSONDecodingError(DecodeError) + case jsonDecodingError(DecodeError) /// A status code, response, and error that was returned from the API. - case APIError(Int, Response, GitHubError) + case apiError(Int, Response, GitHubError) /// The requested object does not exist. - case DoesNotExist + case doesNotExist public var hashValue: Int { switch self { - case let .NetworkError(error): + case let .networkError(error): return error.hashValue - case let .JSONDeserializationError(error): + case let .jsonDeserializationError(error): return error.hashValue - case let .JSONDecodingError(error): + case let .jsonDecodingError(error): return error.hashValue - case let .APIError(statusCode, response, error): + case let .apiError(statusCode, response, error): return statusCode.hashValue ^ response.hashValue ^ error.hashValue - case .DoesNotExist: + case .doesNotExist: return 4 } } @@ -147,16 +136,16 @@ public final class Client { /// Credentials for the GitHub API. internal enum Credentials { - case Token(String) - case Basic(username: String, password: String) + case token(String) + case basic(username: String, password: String) var authorizationHeader: String { switch self { - case let .Token(token): + case let .token(token): return "token \(token)" - case let .Basic(username, password): - let data = "\(username):\(password)".dataUsingEncoding(NSUTF8StringEncoding)! - let encodedString = data.base64EncodedStringWithOptions([]) + case let .basic(username, password): + let data = "\(username):\(password)".data(using: String.Encoding.utf8)! + let encodedString = data.base64EncodedString(options: []) return "Basic \(encodedString)" } } @@ -165,93 +154,93 @@ public final class Client { /// A GitHub API endpoint. internal enum Endpoint: Hashable { // https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name - case ReleaseByTagName(owner: String, repository: String, tag: String) + case releaseByTagName(owner: String, repository: String, tag: String) // https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository - case ReleasesInRepository(owner: String, repository: String) + case releasesInRepository(owner: String, repository: String) // https://developer.github.com/v3/users/#get-a-single-user - case UserInfo(login: String) + case userInfo(login: String) // https://developer.github.com/v3/issues/#list-issues - case AssignedIssues + case assignedIssues // https://developer.github.com/v3/issues/#list-issues-for-a-repository - case IssuesInRepository(owner: String, repository: String) + case issuesInRepository(owner: String, repository: String) // https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue - case CommentsOnIssue(number: Int, owner: String, repository: String) + case commentsOnIssue(number: Int, owner: String, repository: String) // https://developer.github.com/v3/users/#get-the-authenticated-user - case AuthenticatedUser + case authenticatedUser // https://developer.github.com/v3/repos/#list-your-repositories - case Repositories + case repositories // https://developer.github.com/v3/repos/#list-user-repositories - case RepositoriesForUser(user: String) + case repositoriesForUser(user: String) // https://developer.github.com/v3/repos/#list-organization-repositories - case RepositoriesForOrganization(organization: String) + case repositoriesForOrganization(organization: String) // https://developer.github.com/v3/repos/#list-all-public-repositories - case PublicRepositories + case publicRepositories var path: String { switch self { - case let .ReleaseByTagName(owner, repo, tag): + case let .releaseByTagName(owner, repo, tag): return "/repos/\(owner)/\(repo)/releases/tags/\(tag)" - case let .ReleasesInRepository(owner, repo): + case let .releasesInRepository(owner, repo): return "/repos/\(owner)/\(repo)/releases" - case let .UserInfo(login): + case let .userInfo(login): return "/users/\(login)" - case .AssignedIssues: + case .assignedIssues: return "/issues" - case .IssuesInRepository(let owner, let repository): + case .issuesInRepository(let owner, let repository): return "/repos/\(owner)/\(repository)/issues" - case .CommentsOnIssue(let issue, let owner, let repository): + case .commentsOnIssue(let issue, let owner, let repository): return "/repos/\(owner)/\(repository)/issues/\(issue)/comments" - case .AuthenticatedUser: + case .authenticatedUser: return "/user" - case .Repositories: + case .repositories: return "/user/repos" - case .RepositoriesForUser(let user): + case .repositoriesForUser(let user): return "/users/\(user)/repos" - case .RepositoriesForOrganization(let organisation): + case .repositoriesForOrganization(let organisation): return "/orgs/\(organisation)/repos" - case .PublicRepositories: + case .publicRepositories: return "/repositories" } } var hashValue: Int { switch self { - case let .ReleaseByTagName(owner, repo, tag): + case let .releaseByTagName(owner, repo, tag): return owner.hashValue ^ repo.hashValue ^ tag.hashValue - case let .ReleasesInRepository(owner, repo): + case let .releasesInRepository(owner, repo): return owner.hashValue ^ repo.hashValue - case let .UserInfo(login): + case let .userInfo(login): return login.hashValue - case .AssignedIssues: + case .assignedIssues: return "AssignedIssues".hashValue - case .IssuesInRepository(let owner, let repository): + case .issuesInRepository(let owner, let repository): return "Issues".hashValue ^ owner.hashValue ^ repository.hashValue - case .CommentsOnIssue(let issue, let owner, let repository): + case .commentsOnIssue(let issue, let owner, let repository): return issue.hashValue ^ owner.hashValue ^ repository.hashValue - case .AuthenticatedUser: + case .authenticatedUser: return "authenticated-user".hashValue - case .Repositories: + case .repositories: return "Repositories".hashValue - case .RepositoriesForUser(let user): + case .repositoriesForUser(let user): return user.hashValue - case .RepositoriesForOrganization(let organisation): + case .repositoriesForOrganization(let organisation): return organisation.hashValue - case .PublicRepositories: + case .publicRepositories: return "PublicRepositories".hashValue } } - var queryItems: [NSURLQueryItem] { + var queryItems: [URLQueryItem] { return [] } } @@ -279,13 +268,13 @@ public final class Client { /// Create an authenticated client for the given Server with a token. public init(_ server: Server, token: String) { self.server = server - self.credentials = .Token(token) + self.credentials = .token(token) } /// Create an authenticated client for the given Server with a username and password. public init(_ server: Server, username: String, password: String) { self.server = server - self.credentials = .Basic(username: username, password: password) + self.credentials = .basic(username: username, password: password) } /// Fetch the releases in the given repository, starting at the given page. @@ -294,103 +283,104 @@ public final class Client { /// will be the response and releases from a single page. /// /// https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository - public func releasesInRepository(repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Release]), Error> { + public func releases(in repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Release]), Error> { precondition(repository.server == server) - return fetchMany(.ReleasesInRepository(owner: repository.owner, repository: repository.name), page: page, pageSize: perPage) + return fetchMany(.releasesInRepository(owner: repository.owner, repository: repository.name), page: page, pageSize: perPage) } /// Fetch the release corresponding to the given tag in the given repository. /// /// If the tag exists, but there's not a correspoding GitHub Release, this method will return a /// `.DoesNotExist` error. This is indistinguishable from a nonexistent tag. - public func releaseForTag(tag: String, inRepository repository: Repository) -> SignalProducer<(Response, Release), Error> { + public func release(forTag tag: String, in repository: Repository) -> SignalProducer<(Response, Release), Error> { precondition(repository.server == server) - return fetchOne(.ReleaseByTagName(owner: repository.owner, repository: repository.name, tag: tag)) + return fetchOne(.releaseByTagName(owner: repository.owner, repository: repository.name, tag: tag)) } /// Downloads the indicated release asset to a temporary file, returning the URL to the file on /// disk. /// /// The downloaded file will be deleted after the URL has been sent upon the signal. - public func downloadAsset(asset: Release.Asset) -> SignalProducer { - return NSURLSession - .sharedSession() - .downloadFile(NSURLRequest.create(asset.APIURL, credentials, contentType: Client.DownloadContentType)) - .mapError(Error.NetworkError) + public func download(asset: Release.Asset) -> SignalProducer { + return URLSession + .shared + .downloadFile(URLRequest.create(asset.apiURL, credentials, contentType: Client.DownloadContentType)) + .mapError(Error.networkError) } /// Fetch the user with the given login. - public func userWithLogin(login: String) -> SignalProducer<(Response, UserInfo), Error> { - return fetchOne(.UserInfo(login: login)) + public func user(login: String) -> SignalProducer<(Response, UserInfo), Error> { + return fetchOne(.userInfo(login: login)) } /// Fetch the currently authenticated user public func authenticatedUser() -> SignalProducer<(Response, UserInfo), Error> { - return fetchOne(.AuthenticatedUser) + return fetchOne(.authenticatedUser) } public func assignedIssues(page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Issue]), Error> { - return fetchMany(.AssignedIssues, page: page, pageSize: perPage) + return fetchMany(.assignedIssues, page: page, pageSize: perPage) } - public func issuesInRepository(repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Issue]), Error> { + public func issues(in repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Issue]), Error> { precondition(repository.server == server) - return fetchMany(.IssuesInRepository(owner: repository.owner, repository: repository.name), page: page, pageSize: perPage) + return fetchMany(.issuesInRepository(owner: repository.owner, repository: repository.name), page: page, pageSize: perPage) } /// Fetch the comments posted on an issue - public func commentsOnIssue(issue: Int, repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Comment]), Error> { + public func comments(onIssue issue: Int, in repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Comment]), Error> { precondition(repository.server == server) - return fetchMany(.CommentsOnIssue(number: issue, owner: repository.owner, repository: repository.name), page: page, pageSize: perPage) + return fetchMany(.commentsOnIssue(number: issue, owner: repository.owner, repository: repository.name), page: page, pageSize: perPage) } /// Fetch the authenticated user's repositories public func repositories(page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { - return fetchMany(.Repositories, page: page, pageSize: perPage) + return fetchMany(.repositories, page: page, pageSize: perPage) } /// Fetch the repositories for a specific user - public func repositoriesForUser(user: String, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { - return fetchMany(.RepositoriesForUser(user: user), page: page, pageSize: perPage) + public func repositories(forUser user: String, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { + return fetchMany(.repositoriesForUser(user: user), page: page, pageSize: perPage) } /// Fetch the repositories for a specific organisation - public func repositoriesForOrganization(organization: String, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { - return fetchMany(.RepositoriesForOrganization(organization: organization), page: page, pageSize: perPage) + public func repositories(forOrganization organization: String, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { + return fetchMany(.repositoriesForOrganization(organization: organization), page: page, pageSize: perPage) } /// Fetch the public repositories on Github public func publicRepositories(page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [RepositoryInfo]), Error> { - return fetchMany(.PublicRepositories, page: page, pageSize: perPage) + return fetchMany(.publicRepositories, page: page, pageSize: perPage) } /// Fetch an endpoint from the API. - private func fetch(endpoint: Endpoint, page: UInt?, pageSize: UInt?) -> SignalProducer<(Response, AnyObject), Error> { - let URL = NSURL(server, endpoint, page: page, pageSize: pageSize) - let request = NSURLRequest.create(URL, credentials) - return NSURLSession - .sharedSession() - .rac_dataWithRequest(request) - .mapError(Error.NetworkError) - .flatMap(.Concat) { data, response -> SignalProducer<(Response, AnyObject), Error> in - let response = response as! NSHTTPURLResponse + private func fetch(_ endpoint: Endpoint, page: UInt?, pageSize: UInt?) -> SignalProducer<(Response, Any), Error> { + let url = URL(server, endpoint, page: page, pageSize: pageSize) + let request = URLRequest.create(url, credentials) + return URLSession + .shared + .reactive + .data(with: request) + .mapError(Error.networkError) + .flatMap(.concat) { data, response -> SignalProducer<(Response, Any), Error> in + let response = response as! HTTPURLResponse let headers = response.allHeaderFields as! [String:String] return SignalProducer .attempt { - return NSJSONSerialization.deserializeJSON(data).mapError(Error.JSONDeserializationError) + return JSONSerialization.deserializeJSON(data).mapError(Error.jsonDeserializationError) } .attemptMap { JSON in if response.statusCode == 404 { - return .Failure(.DoesNotExist) + return .failure(.doesNotExist) } if response.statusCode >= 400 && response.statusCode < 600 { return decode(JSON) - .mapError(Error.JSONDecodingError) + .mapError(Error.jsonDecodingError) .flatMap { error in - .Failure(Error.APIError(response.statusCode, Response(headerFields: headers), error)) + .failure(Error.apiError(response.statusCode, Response(headerFields: headers), error)) } } - return .Success(JSON) + return .success(JSON) } .map { JSON in return (Response(headerFields: headers), JSON) @@ -400,8 +390,8 @@ public final class Client { /// Fetch an object from the API. internal func fetchOne - - (endpoint: Endpoint) -> SignalProducer<(Response, Resource), Error> + + (_ endpoint: Endpoint) -> SignalProducer<(Response, Resource), Error> where Resource.DecodedType == Resource { return fetch(endpoint, page: nil, pageSize: nil) .attemptMap { response, JSON in @@ -409,14 +399,14 @@ public final class Client { .map { resource in (response, resource) } - .mapError(Error.JSONDecodingError) + .mapError(Error.jsonDecodingError) } } /// Fetch a list of objects from the API. internal func fetchMany - - (endpoint: Endpoint, page: UInt?, pageSize: UInt?) -> SignalProducer<(Response, [Resource]), Error> + + (_ endpoint: Endpoint, page: UInt?, pageSize: UInt?) -> SignalProducer<(Response, [Resource]), Error> where Resource.DecodedType == Resource { let nextPage = (page ?? 1) + 1 return fetch(endpoint, page: page, pageSize: pageSize) @@ -425,9 +415,9 @@ public final class Client { .map { resource in (response, resource) } - .mapError(Error.JSONDecodingError) + .mapError(Error.jsonDecodingError) } - .flatMap(.Concat) { response, JSON -> SignalProducer<(Response, [Resource]), Error> in + .flatMap(.concat) { response, JSON -> SignalProducer<(Response, [Resource]), Error> in return SignalProducer(value: (response, JSON)) .concat(response.links["next"] == nil ? SignalProducer.empty : self.fetchMany(endpoint, page: nextPage, pageSize: pageSize)) } @@ -436,19 +426,19 @@ public final class Client { public func ==(lhs: Client.Error, rhs: Client.Error) -> Bool { switch (lhs, rhs) { - case let (.NetworkError(error1), .NetworkError(error2)): + case let (.networkError(error1), .networkError(error2)): return error1 == error2 - case let (.JSONDeserializationError(error1), .JSONDeserializationError(error2)): + case let (.jsonDeserializationError(error1), .jsonDeserializationError(error2)): return error1 == error2 - case let (.JSONDecodingError(error1), .JSONDecodingError(error2)): + case let (.jsonDecodingError(error1), .jsonDecodingError(error2)): return error1 == error2 - case let (.APIError(statusCode1, response1, error1), .APIError(statusCode2, response2, error2)): + case let (.apiError(statusCode1, response1, error1), .apiError(statusCode2, response2, error2)): return statusCode1 == statusCode2 && response1 == response2 && error1 == error2 - case (.DoesNotExist, .DoesNotExist): + case (.doesNotExist, .doesNotExist): return true default: @@ -458,11 +448,11 @@ public func ==(lhs: Client.Error, rhs: Client.Error) -> Bool { internal func ==(lhs: Client.Endpoint, rhs: Client.Endpoint) -> Bool { switch (lhs, rhs) { - case let (.ReleaseByTagName(owner1, repo1, tag1), .ReleaseByTagName(owner2, repo2, tag2)): + case let (.releaseByTagName(owner1, repo1, tag1), .releaseByTagName(owner2, repo2, tag2)): return owner1 == owner2 && repo1 == repo2 && tag1 == tag2 - case let (.ReleasesInRepository(owner1, repo1), .ReleasesInRepository(owner2, repo2)): + case let (.releasesInRepository(owner1, repo1), .releasesInRepository(owner2, repo2)): return owner1 == owner2 && repo1 == repo2 - case let (.UserInfo(login1), .UserInfo(login2)): + case let (.userInfo(login1), .userInfo(login2)): return login1 == login2 default: return false diff --git a/Tentacle/Color.swift b/Tentacle/Color.swift index 4828bdf..7255e00 100644 --- a/Tentacle/Color.swift +++ b/Tentacle/Color.swift @@ -20,13 +20,13 @@ extension Color { convenience init(hex: String) { precondition(hex.characters.count == 6) - let scanner = NSScanner(string: hex) + let scanner = Scanner(string: hex) var rgb: UInt32 = 0 - scanner.scanHexInt(&rgb) + scanner.scanHexInt32(&rgb) let r = CGFloat((rgb & 0xff0000) >> 16) / 255.0 let g = CGFloat((rgb & 0x00ff00) >> 8) / 255.0 let b = CGFloat((rgb & 0x0000ff) >> 0) / 255.0 self.init(red: r, green: g, blue: b, alpha: 1) } -} \ No newline at end of file +} diff --git a/Tentacle/Comment.swift b/Tentacle/Comment.swift index 2fc5f2a..2427885 100644 --- a/Tentacle/Comment.swift +++ b/Tentacle/Comment.swift @@ -9,17 +9,18 @@ import Foundation import Curry import Argo +import Runes public struct Comment: Hashable, CustomStringConvertible { /// The id of the issue public let ID: String /// The URL to view this comment in a browser - public let URL: NSURL + public let url: URL /// The date this comment was created at - public let createdAt: NSDate + public let createdAt: Date /// The date this comment was last updated at - public let updatedAt: NSDate + public let updatedAt: Date /// The body of the comment public let body: String /// The author of this comment @@ -36,19 +37,19 @@ public struct Comment: Hashable, CustomStringConvertible { public func ==(lhs: Comment, rhs: Comment) -> Bool { return lhs.ID == rhs.ID - && lhs.URL == rhs.URL + && lhs.url == rhs.url && lhs.body == rhs.body } extension Comment: ResourceType { - public static func decode(j: JSON) -> Decoded { + public static func decode(_ j: JSON) -> Decoded { let f = curry(Comment.init) return f <^> (j <| "id" >>- toString) - <*> (j <| "html_url" >>- toNSURL) - <*> (j <| "created_at" >>- toNSDate) - <*> (j <| "updated_at" >>- toNSDate) + <*> (j <| "html_url" >>- toURL) + <*> (j <| "created_at" >>- toDate) + <*> (j <| "updated_at" >>- toDate) <*> j <| "body" <*> j <| "user" } diff --git a/Tentacle/Decodable.swift b/Tentacle/Decodable.swift index 1afcd31..5d3d3d1 100644 --- a/Tentacle/Decodable.swift +++ b/Tentacle/Decodable.swift @@ -9,8 +9,8 @@ import Argo import Foundation -extension NSURL: Decodable { - public class func decode(json: JSON) -> Decoded { +extension URL: Decodable { + public static func decode(_ json: JSON) -> Decoded { return String.decode(json).flatMap { URLString in return .fromOptional(self.init(string: URLString)) } diff --git a/Tentacle/FoundationExtensions.swift b/Tentacle/FoundationExtensions.swift index cff7e9d..dede352 100644 --- a/Tentacle/FoundationExtensions.swift +++ b/Tentacle/FoundationExtensions.swift @@ -8,12 +8,12 @@ import Foundation -extension NSDateFormatter { - @nonobjc public static var ISO8601: NSDateFormatter = { - let formatter = NSDateFormatter() - formatter.locale = NSLocale(localeIdentifier:"en_US_POSIX") +extension DateFormatter { + @nonobjc public static var iso8601: DateFormatter = { + let formatter = DateFormatter() + formatter.locale = Locale(identifier:"en_US_POSIX") formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'" - formatter.timeZone = NSTimeZone(abbreviation:"UTC") + formatter.timeZone = TimeZone(abbreviation:"UTC") return formatter }() } diff --git a/Tentacle/GitHubError.swift b/Tentacle/GitHubError.swift index 9ea385a..5507c11 100644 --- a/Tentacle/GitHubError.swift +++ b/Tentacle/GitHubError.swift @@ -8,11 +8,12 @@ import Argo import Curry +import Runes import Foundation /// An error from the GitHub API. -public struct GitHubError: Hashable, CustomStringConvertible, ErrorType { +public struct GitHubError: Hashable, CustomStringConvertible, Error { /// The error message from the API. public let message: String @@ -34,7 +35,7 @@ public func ==(lhs: GitHubError, rhs: GitHubError) -> Bool { } extension GitHubError: Decodable { - public static func decode(j: JSON) -> Decoded { + public static func decode(_ j: JSON) -> Decoded { return curry(self.init) <^> j <| "message" } } diff --git a/Tentacle/Issue.swift b/Tentacle/Issue.swift index bc86427..e0850d5 100644 --- a/Tentacle/Issue.swift +++ b/Tentacle/Issue.swift @@ -8,6 +8,7 @@ import Argo import Curry +import Runes /// An Issue on Github public struct Issue: Hashable, CustomStringConvertible { @@ -20,7 +21,7 @@ public struct Issue: Hashable, CustomStringConvertible { public let ID: String /// The URL to view this issue in a browser - public let URL: NSURL? + public let url: URL? /// The number of the issue in the repository it belongs to public let number: Int @@ -56,13 +57,13 @@ public struct Issue: Hashable, CustomStringConvertible { public let pullRequest: PullRequest? /// The date this issue was closed at, if it ever were - public let closedAt: NSDate? + public let closedAt: Date? /// The date this issue was created at - public let createdAt: NSDate + public let createdAt: Date /// The date this issue was updated at - public let updatedAt: NSDate + public let updatedAt: Date public var hashValue: Int { return ID.hashValue @@ -72,9 +73,9 @@ public struct Issue: Hashable, CustomStringConvertible { return title } - public init(ID: String, URL: NSURL?, number: Int, state: State, title: String, body: String, user: User, labels: [Label], assignees: [User], milestone: Milestone?, locked: Bool, commentCount: Int, pullRequest: PullRequest?, closedAt: NSDate?, createdAt: NSDate, updatedAt: NSDate) { + public init(ID: String, url: URL?, number: Int, state: State, title: String, body: String, user: User, labels: [Label], assignees: [User], milestone: Milestone?, locked: Bool, commentCount: Int, pullRequest: PullRequest?, closedAt: Date?, createdAt: Date, updatedAt: Date) { self.ID = ID - self.URL = URL + self.url = url self.number = number self.state = state self.title = title @@ -95,7 +96,7 @@ public struct Issue: Hashable, CustomStringConvertible { public func ==(lhs: Issue, rhs: Issue) -> Bool { return lhs.ID == rhs.ID - && lhs.URL == rhs.URL + && lhs.url == rhs.url && lhs.number == rhs.number && lhs.state == rhs.state && lhs.title == rhs.title @@ -110,25 +111,27 @@ public func ==(lhs: Issue, rhs: Issue) -> Bool { } extension Issue: ResourceType { - public static func decode(j: JSON) -> Decoded { + public static func decode(_ j: JSON) -> Decoded { let f = curry(Issue.init) - return f + let ff = f <^> (j <| "id" >>- toString) - <*> (j <| "html_url" >>- toNSURL) + <*> (j <| "html_url" >>- toURL) <*> j <| "number" <*> (j <| "state" >>- toIssueState) <*> j <| "title" + let fff = ff <*> j <| "body" <*> j <| "user" <*> j <|| "labels" <*> j <|| "assignees" <*> j <|? "milestone" + return fff <*> j <| "locked" <*> j <| "comments" <*> j <|? "pull_request" - <*> (j <|? "closed_at" >>- toOptionalNSDate) - <*> (j <| "created_at" >>- toNSDate) - <*> (j <| "updated_at" >>- toNSDate) + <*> (j <|? "closed_at" >>- toOptionalDate) + <*> (j <| "created_at" >>- toDate) + <*> (j <| "updated_at" >>- toDate) } } diff --git a/Tentacle/Label.swift b/Tentacle/Label.swift index 2f1d17a..1b62ce8 100644 --- a/Tentacle/Label.swift +++ b/Tentacle/Label.swift @@ -9,6 +9,7 @@ import Foundation import Argo import Curry +import Runes public struct Label: Hashable, CustomStringConvertible { public let name: String @@ -28,10 +29,10 @@ public func ==(lhs: Label, rhs: Label) -> Bool { } extension Label: ResourceType { - public static func decode(json: JSON) -> Decoded