├── .gitignore ├── .travis.yml ├── Cartfile ├── Cartfile.resolved ├── Dratini.podspec ├── Dratini.xcodeproj ├── Ditto_Info.plist ├── DratiniTests_Info.plist ├── Dratini_Info.plist ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── Dratini.xcscmblueprint └── xcshareddata │ └── xcschemes │ ├── Dratini-iOS.xcscheme │ ├── Dratini-macOS.xcscheme │ ├── Dratini-tvOS.xcscheme │ ├── Dratini-watchOS.xcscheme │ └── xcschememanagement.plist ├── LICENSE ├── Package.pins ├── Package.swift ├── README.md ├── Sources ├── DRError.swift ├── Info.plist ├── Parameters.swift ├── Request.swift ├── RequestConverter.swift ├── RequestQueue.swift └── Response.swift ├── Tests ├── DratiniTests │ ├── ParametersTests.swift │ ├── RequestConverterTests.swift │ ├── RequestQueueTests.swift │ ├── RequestTests.swift │ └── ResponseTests.swift └── Info.plist └── codecov.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | 67 | Carthage/ 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: swift 2 | osx_image: xcode8.3 3 | script: 4 | - carthage update 5 | - xcodebuild test -project Dratini.xcodeproj -scheme "Dratini-iOS" -sdk "iphonesimulator" -destination "OS=10.3.1,name=iPhone 7" 6 | - xcodebuild test -project Dratini.xcodeproj -scheme "Dratini-macOS" -sdk "macosx" -destination "arch=x86_64" 7 | - xcodebuild -project Dratini.xcodeproj -scheme "Dratini-watchOS" -sdk "watchsimulator" -destination "OS=3.2,name=Apple Watch - 42mm" 8 | - xcodebuild test -project Dratini.xcodeproj -scheme "Dratini-tvOS" -sdk "appletvsimulator" -destination "OS=10.0,name=Apple TV 1080p" 9 | after_success: 10 | - bash <(curl -s https://codecov.io/bash) -J 'Dratini' -X xcodeplist 11 | 12 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "kevin0571/Ditto" ~> 1.0 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "kevin0571/Ditto" "1.0.4" 2 | -------------------------------------------------------------------------------- /Dratini.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Dratini' 3 | s.version = '1.1.2' 4 | s.license = 'MIT' 5 | s.summary = 'Dratini is a neat network abstraction layer.' 6 | s.homepage = 'https://github.com/kevin0571/Dratini' 7 | s.authors = { 'Kevin Lin' => 'kevin_lyn@outlook.com' } 8 | s.source = { :git => 'https://github.com/kevin0571/Dratini.git', :tag => s.version } 9 | 10 | s.ios.deployment_target = '8.0' 11 | s.osx.deployment_target = '10.10' 12 | s.tvos.deployment_target = '9.0' 13 | s.watchos.deployment_target = '2.0' 14 | 15 | s.source_files = 'Sources/*.swift' 16 | s.dependency 'Ditto-Swift', '>= 1.0.2' 17 | end 18 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/Ditto_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/DratiniTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/Dratini_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 769999521F3F293A00EC035C /* Dratini.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 769999491F3F293A00EC035C /* Dratini.framework */; }; 11 | 769999601F3F2A6B00EC035C /* DRError.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* DRError.swift */; }; 12 | 769999611F3F2A6B00EC035C /* Parameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Parameters.swift */; }; 13 | 769999621F3F2A6B00EC035C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Request.swift */; }; 14 | 769999631F3F2A6B00EC035C /* RequestConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* RequestConverter.swift */; }; 15 | 769999641F3F2A6B00EC035C /* RequestQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* RequestQueue.swift */; }; 16 | 769999651F3F2A6B00EC035C /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Response.swift */; }; 17 | 769999661F3F2A9F00EC035C /* ResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6C9FD1F245DE70021198C /* ResponseTests.swift */; }; 18 | 769999671F3F2A9F00EC035C /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6C9FE1F245DE70021198C /* RequestTests.swift */; }; 19 | 769999681F3F2A9F00EC035C /* RequestQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6C9FF1F245DE70021198C /* RequestQueueTests.swift */; }; 20 | 769999691F3F2A9F00EC035C /* RequestConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6CA001F245DE70021198C /* RequestConverterTests.swift */; }; 21 | 7699996A1F3F2A9F00EC035C /* ParametersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6CA011F245DE70021198C /* ParametersTests.swift */; }; 22 | 7699996C1F3F2B3000EC035C /* Ditto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7699996B1F3F2B3000EC035C /* Ditto.framework */; }; 23 | 7699996D1F3F2BC200EC035C /* Ditto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7699996B1F3F2B3000EC035C /* Ditto.framework */; }; 24 | 7699997D1F3F2D1100EC035C /* Dratini.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 769999741F3F2D1100EC035C /* Dratini.framework */; }; 25 | 7699998B1F3F2D6700EC035C /* DRError.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* DRError.swift */; }; 26 | 7699998C1F3F2D6700EC035C /* Parameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Parameters.swift */; }; 27 | 7699998D1F3F2D6700EC035C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Request.swift */; }; 28 | 7699998E1F3F2D6700EC035C /* RequestConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* RequestConverter.swift */; }; 29 | 7699998F1F3F2D6700EC035C /* RequestQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* RequestQueue.swift */; }; 30 | 769999901F3F2D6700EC035C /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Response.swift */; }; 31 | 769999911F3F2D8E00EC035C /* ResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6C9FD1F245DE70021198C /* ResponseTests.swift */; }; 32 | 769999921F3F2D8E00EC035C /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6C9FE1F245DE70021198C /* RequestTests.swift */; }; 33 | 769999931F3F2D8E00EC035C /* RequestQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6C9FF1F245DE70021198C /* RequestQueueTests.swift */; }; 34 | 769999941F3F2D8E00EC035C /* RequestConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6CA001F245DE70021198C /* RequestConverterTests.swift */; }; 35 | 769999951F3F2D8E00EC035C /* ParametersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6CA011F245DE70021198C /* ParametersTests.swift */; }; 36 | 769999971F3F2D9D00EC035C /* Ditto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 769999961F3F2D9D00EC035C /* Ditto.framework */; }; 37 | 769999981F3F2DB300EC035C /* Ditto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 769999961F3F2D9D00EC035C /* Ditto.framework */; }; 38 | 7699999F1F3F2E8B00EC035C /* Ditto.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 769999961F3F2D9D00EC035C /* Ditto.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 39 | 769999AD1F3F2F0900EC035C /* DRError.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* DRError.swift */; }; 40 | 769999AE1F3F2F0900EC035C /* Parameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Parameters.swift */; }; 41 | 769999AF1F3F2F0900EC035C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Request.swift */; }; 42 | 769999B01F3F2F0900EC035C /* RequestConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* RequestConverter.swift */; }; 43 | 769999B11F3F2F0900EC035C /* RequestQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* RequestQueue.swift */; }; 44 | 769999B21F3F2F0900EC035C /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Response.swift */; }; 45 | 769999B41F3F2F1300EC035C /* Ditto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 769999B31F3F2F1300EC035C /* Ditto.framework */; }; 46 | 769999C31F3F2F3300EC035C /* Dratini.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 769999BA1F3F2F3200EC035C /* Dratini.framework */; }; 47 | 769999D21F3F2F7900EC035C /* Ditto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 769999D11F3F2F7900EC035C /* Ditto.framework */; }; 48 | 769999D31F3F2F8500EC035C /* DRError.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* DRError.swift */; }; 49 | 769999D41F3F2F8500EC035C /* Parameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Parameters.swift */; }; 50 | 769999D51F3F2F8500EC035C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Request.swift */; }; 51 | 769999D61F3F2F8500EC035C /* RequestConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* RequestConverter.swift */; }; 52 | 769999D71F3F2F8500EC035C /* RequestQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* RequestQueue.swift */; }; 53 | 769999D81F3F2F8500EC035C /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Response.swift */; }; 54 | 769999D91F3F2FAB00EC035C /* ResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6C9FD1F245DE70021198C /* ResponseTests.swift */; }; 55 | 769999DA1F3F2FAB00EC035C /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6C9FE1F245DE70021198C /* RequestTests.swift */; }; 56 | 769999DB1F3F2FAB00EC035C /* RequestQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6C9FF1F245DE70021198C /* RequestQueueTests.swift */; }; 57 | 769999DC1F3F2FAB00EC035C /* RequestConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6CA001F245DE70021198C /* RequestConverterTests.swift */; }; 58 | 769999DD1F3F2FAB00EC035C /* ParametersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D6CA011F245DE70021198C /* ParametersTests.swift */; }; 59 | 769999DE1F3F2FB200EC035C /* Ditto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 769999D11F3F2F7900EC035C /* Ditto.framework */; }; 60 | /* End PBXBuildFile section */ 61 | 62 | /* Begin PBXContainerItemProxy section */ 63 | 769999531F3F293A00EC035C /* PBXContainerItemProxy */ = { 64 | isa = PBXContainerItemProxy; 65 | containerPortal = OBJ_1 /* Project object */; 66 | proxyType = 1; 67 | remoteGlobalIDString = 769999481F3F293A00EC035C; 68 | remoteInfo = "Dratini-iOS"; 69 | }; 70 | 7699997E1F3F2D1100EC035C /* PBXContainerItemProxy */ = { 71 | isa = PBXContainerItemProxy; 72 | containerPortal = OBJ_1 /* Project object */; 73 | proxyType = 1; 74 | remoteGlobalIDString = 769999731F3F2D1100EC035C; 75 | remoteInfo = "Dratini-macOS"; 76 | }; 77 | 769999C41F3F2F3300EC035C /* PBXContainerItemProxy */ = { 78 | isa = PBXContainerItemProxy; 79 | containerPortal = OBJ_1 /* Project object */; 80 | proxyType = 1; 81 | remoteGlobalIDString = 769999B91F3F2F3200EC035C; 82 | remoteInfo = "Dratini-tvOS"; 83 | }; 84 | /* End PBXContainerItemProxy section */ 85 | 86 | /* Begin PBXCopyFilesBuildPhase section */ 87 | 769999991F3F2E0C00EC035C /* CopyFiles */ = { 88 | isa = PBXCopyFilesBuildPhase; 89 | buildActionMask = 2147483647; 90 | dstPath = ""; 91 | dstSubfolderSpec = 16; 92 | files = ( 93 | 7699999F1F3F2E8B00EC035C /* Ditto.framework in CopyFiles */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXCopyFilesBuildPhase section */ 98 | 99 | /* Begin PBXFileReference section */ 100 | 769999491F3F293A00EC035C /* Dratini.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dratini.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 101 | 769999511F3F293A00EC035C /* Dratini-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Dratini-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 102 | 7699996B1F3F2B3000EC035C /* Ditto.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ditto.framework; path = Carthage/Build/iOS/Ditto.framework; sourceTree = ""; }; 103 | 769999741F3F2D1100EC035C /* Dratini.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dratini.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 104 | 7699997C1F3F2D1100EC035C /* Dratini-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Dratini-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 105 | 769999961F3F2D9D00EC035C /* Ditto.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ditto.framework; path = Carthage/Build/Mac/Ditto.framework; sourceTree = ""; }; 106 | 769999A51F3F2EBB00EC035C /* Dratini.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dratini.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 107 | 769999B31F3F2F1300EC035C /* Ditto.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ditto.framework; path = Carthage/Build/watchOS/Ditto.framework; sourceTree = ""; }; 108 | 769999BA1F3F2F3200EC035C /* Dratini.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dratini.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 109 | 769999C21F3F2F3300EC035C /* Dratini-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Dratini-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 110 | 769999D11F3F2F7900EC035C /* Ditto.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ditto.framework; path = Carthage/Build/tvOS/Ditto.framework; sourceTree = ""; }; 111 | 76D6C9FD1F245DE70021198C /* ResponseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseTests.swift; sourceTree = ""; }; 112 | 76D6C9FE1F245DE70021198C /* RequestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = ""; }; 113 | 76D6C9FF1F245DE70021198C /* RequestQueueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestQueueTests.swift; sourceTree = ""; }; 114 | 76D6CA001F245DE70021198C /* RequestConverterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestConverterTests.swift; sourceTree = ""; }; 115 | 76D6CA011F245DE70021198C /* ParametersTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParametersTests.swift; sourceTree = ""; }; 116 | OBJ_10 /* Request.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; 117 | OBJ_11 /* RequestConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestConverter.swift; sourceTree = ""; }; 118 | OBJ_12 /* RequestQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestQueue.swift; sourceTree = ""; }; 119 | OBJ_13 /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; 120 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 121 | OBJ_8 /* DRError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DRError.swift; sourceTree = ""; }; 122 | OBJ_9 /* Parameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parameters.swift; sourceTree = ""; }; 123 | /* End PBXFileReference section */ 124 | 125 | /* Begin PBXFrameworksBuildPhase section */ 126 | 769999451F3F293A00EC035C /* Frameworks */ = { 127 | isa = PBXFrameworksBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | 7699996C1F3F2B3000EC035C /* Ditto.framework in Frameworks */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | 7699994E1F3F293A00EC035C /* Frameworks */ = { 135 | isa = PBXFrameworksBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | 769999521F3F293A00EC035C /* Dratini.framework in Frameworks */, 139 | 7699996D1F3F2BC200EC035C /* Ditto.framework in Frameworks */, 140 | ); 141 | runOnlyForDeploymentPostprocessing = 0; 142 | }; 143 | 769999701F3F2D1100EC035C /* Frameworks */ = { 144 | isa = PBXFrameworksBuildPhase; 145 | buildActionMask = 2147483647; 146 | files = ( 147 | 769999971F3F2D9D00EC035C /* Ditto.framework in Frameworks */, 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | 769999791F3F2D1100EC035C /* Frameworks */ = { 152 | isa = PBXFrameworksBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | 7699997D1F3F2D1100EC035C /* Dratini.framework in Frameworks */, 156 | 769999981F3F2DB300EC035C /* Ditto.framework in Frameworks */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | 769999A11F3F2EBB00EC035C /* Frameworks */ = { 161 | isa = PBXFrameworksBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | 769999B41F3F2F1300EC035C /* Ditto.framework in Frameworks */, 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | 769999B61F3F2F3200EC035C /* Frameworks */ = { 169 | isa = PBXFrameworksBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | 769999D21F3F2F7900EC035C /* Ditto.framework in Frameworks */, 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | 769999BF1F3F2F3300EC035C /* Frameworks */ = { 177 | isa = PBXFrameworksBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | 769999C31F3F2F3300EC035C /* Dratini.framework in Frameworks */, 181 | 769999DE1F3F2FB200EC035C /* Ditto.framework in Frameworks */, 182 | ); 183 | runOnlyForDeploymentPostprocessing = 0; 184 | }; 185 | /* End PBXFrameworksBuildPhase section */ 186 | 187 | /* Begin PBXGroup section */ 188 | 767BDF211F372B5600859513 /* Frameworks */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 769999D11F3F2F7900EC035C /* Ditto.framework */, 192 | 769999B31F3F2F1300EC035C /* Ditto.framework */, 193 | 769999961F3F2D9D00EC035C /* Ditto.framework */, 194 | 7699996B1F3F2B3000EC035C /* Ditto.framework */, 195 | ); 196 | name = Frameworks; 197 | sourceTree = ""; 198 | }; 199 | OBJ_14 /* Tests */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | OBJ_15 /* DratiniTests */, 203 | ); 204 | name = Tests; 205 | sourceTree = SOURCE_ROOT; 206 | }; 207 | OBJ_15 /* DratiniTests */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | 76D6C9FD1F245DE70021198C /* ResponseTests.swift */, 211 | 76D6C9FE1F245DE70021198C /* RequestTests.swift */, 212 | 76D6C9FF1F245DE70021198C /* RequestQueueTests.swift */, 213 | 76D6CA001F245DE70021198C /* RequestConverterTests.swift */, 214 | 76D6CA011F245DE70021198C /* ParametersTests.swift */, 215 | ); 216 | name = DratiniTests; 217 | path = Tests/DratiniTests; 218 | sourceTree = SOURCE_ROOT; 219 | }; 220 | OBJ_25 /* Products */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | 769999491F3F293A00EC035C /* Dratini.framework */, 224 | 769999511F3F293A00EC035C /* Dratini-Tests.xctest */, 225 | 769999741F3F2D1100EC035C /* Dratini.framework */, 226 | 7699997C1F3F2D1100EC035C /* Dratini-Tests.xctest */, 227 | 769999A51F3F2EBB00EC035C /* Dratini.framework */, 228 | 769999BA1F3F2F3200EC035C /* Dratini.framework */, 229 | 769999C21F3F2F3300EC035C /* Dratini-Tests.xctest */, 230 | ); 231 | name = Products; 232 | sourceTree = BUILT_PRODUCTS_DIR; 233 | }; 234 | OBJ_5 = { 235 | isa = PBXGroup; 236 | children = ( 237 | OBJ_6 /* Package.swift */, 238 | OBJ_7 /* Sources */, 239 | OBJ_14 /* Tests */, 240 | OBJ_25 /* Products */, 241 | 767BDF211F372B5600859513 /* Frameworks */, 242 | ); 243 | sourceTree = ""; 244 | }; 245 | OBJ_7 /* Sources */ = { 246 | isa = PBXGroup; 247 | children = ( 248 | OBJ_8 /* DRError.swift */, 249 | OBJ_9 /* Parameters.swift */, 250 | OBJ_10 /* Request.swift */, 251 | OBJ_11 /* RequestConverter.swift */, 252 | OBJ_12 /* RequestQueue.swift */, 253 | OBJ_13 /* Response.swift */, 254 | ); 255 | path = Sources; 256 | sourceTree = SOURCE_ROOT; 257 | }; 258 | /* End PBXGroup section */ 259 | 260 | /* Begin PBXHeadersBuildPhase section */ 261 | 769999461F3F293A00EC035C /* Headers */ = { 262 | isa = PBXHeadersBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | }; 268 | 769999711F3F2D1100EC035C /* Headers */ = { 269 | isa = PBXHeadersBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | }; 275 | 769999A21F3F2EBB00EC035C /* Headers */ = { 276 | isa = PBXHeadersBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | 769999B71F3F2F3200EC035C /* Headers */ = { 283 | isa = PBXHeadersBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXHeadersBuildPhase section */ 290 | 291 | /* Begin PBXNativeTarget section */ 292 | 769999481F3F293A00EC035C /* Dratini-iOS */ = { 293 | isa = PBXNativeTarget; 294 | buildConfigurationList = 7699995A1F3F293A00EC035C /* Build configuration list for PBXNativeTarget "Dratini-iOS" */; 295 | buildPhases = ( 296 | 769999441F3F293A00EC035C /* Sources */, 297 | 769999451F3F293A00EC035C /* Frameworks */, 298 | 769999461F3F293A00EC035C /* Headers */, 299 | 769999471F3F293A00EC035C /* Resources */, 300 | ); 301 | buildRules = ( 302 | ); 303 | dependencies = ( 304 | ); 305 | name = "Dratini-iOS"; 306 | productName = "Dratini-iOS"; 307 | productReference = 769999491F3F293A00EC035C /* Dratini.framework */; 308 | productType = "com.apple.product-type.framework"; 309 | }; 310 | 769999501F3F293A00EC035C /* Dratini-iOSTests */ = { 311 | isa = PBXNativeTarget; 312 | buildConfigurationList = 7699995D1F3F293A00EC035C /* Build configuration list for PBXNativeTarget "Dratini-iOSTests" */; 313 | buildPhases = ( 314 | 7699994D1F3F293A00EC035C /* Sources */, 315 | 7699994E1F3F293A00EC035C /* Frameworks */, 316 | 7699994F1F3F293A00EC035C /* Resources */, 317 | 7699996E1F3F2C8400EC035C /* Copy Frameworks */, 318 | ); 319 | buildRules = ( 320 | ); 321 | dependencies = ( 322 | 769999541F3F293A00EC035C /* PBXTargetDependency */, 323 | ); 324 | name = "Dratini-iOSTests"; 325 | productName = "Dratini-iOSTests"; 326 | productReference = 769999511F3F293A00EC035C /* Dratini-Tests.xctest */; 327 | productType = "com.apple.product-type.bundle.unit-test"; 328 | }; 329 | 769999731F3F2D1100EC035C /* Dratini-macOS */ = { 330 | isa = PBXNativeTarget; 331 | buildConfigurationList = 769999851F3F2D1100EC035C /* Build configuration list for PBXNativeTarget "Dratini-macOS" */; 332 | buildPhases = ( 333 | 7699996F1F3F2D1100EC035C /* Sources */, 334 | 769999701F3F2D1100EC035C /* Frameworks */, 335 | 769999711F3F2D1100EC035C /* Headers */, 336 | 769999721F3F2D1100EC035C /* Resources */, 337 | ); 338 | buildRules = ( 339 | ); 340 | dependencies = ( 341 | ); 342 | name = "Dratini-macOS"; 343 | productName = "Dratini-macOS"; 344 | productReference = 769999741F3F2D1100EC035C /* Dratini.framework */; 345 | productType = "com.apple.product-type.framework"; 346 | }; 347 | 7699997B1F3F2D1100EC035C /* Dratini-macOSTests */ = { 348 | isa = PBXNativeTarget; 349 | buildConfigurationList = 769999881F3F2D1100EC035C /* Build configuration list for PBXNativeTarget "Dratini-macOSTests" */; 350 | buildPhases = ( 351 | 769999781F3F2D1100EC035C /* Sources */, 352 | 769999791F3F2D1100EC035C /* Frameworks */, 353 | 7699997A1F3F2D1100EC035C /* Resources */, 354 | 769999991F3F2E0C00EC035C /* CopyFiles */, 355 | ); 356 | buildRules = ( 357 | ); 358 | dependencies = ( 359 | 7699997F1F3F2D1100EC035C /* PBXTargetDependency */, 360 | ); 361 | name = "Dratini-macOSTests"; 362 | productName = "Dratini-macOSTests"; 363 | productReference = 7699997C1F3F2D1100EC035C /* Dratini-Tests.xctest */; 364 | productType = "com.apple.product-type.bundle.unit-test"; 365 | }; 366 | 769999A41F3F2EBB00EC035C /* Dratini-watchOS */ = { 367 | isa = PBXNativeTarget; 368 | buildConfigurationList = 769999AA1F3F2EBB00EC035C /* Build configuration list for PBXNativeTarget "Dratini-watchOS" */; 369 | buildPhases = ( 370 | 769999A01F3F2EBB00EC035C /* Sources */, 371 | 769999A11F3F2EBB00EC035C /* Frameworks */, 372 | 769999A21F3F2EBB00EC035C /* Headers */, 373 | 769999A31F3F2EBB00EC035C /* Resources */, 374 | ); 375 | buildRules = ( 376 | ); 377 | dependencies = ( 378 | ); 379 | name = "Dratini-watchOS"; 380 | productName = "Dratini-watchOS"; 381 | productReference = 769999A51F3F2EBB00EC035C /* Dratini.framework */; 382 | productType = "com.apple.product-type.framework"; 383 | }; 384 | 769999B91F3F2F3200EC035C /* Dratini-tvOS */ = { 385 | isa = PBXNativeTarget; 386 | buildConfigurationList = 769999CB1F3F2F3300EC035C /* Build configuration list for PBXNativeTarget "Dratini-tvOS" */; 387 | buildPhases = ( 388 | 769999B51F3F2F3200EC035C /* Sources */, 389 | 769999B61F3F2F3200EC035C /* Frameworks */, 390 | 769999B71F3F2F3200EC035C /* Headers */, 391 | 769999B81F3F2F3200EC035C /* Resources */, 392 | ); 393 | buildRules = ( 394 | ); 395 | dependencies = ( 396 | ); 397 | name = "Dratini-tvOS"; 398 | productName = "Dratini-tvOS"; 399 | productReference = 769999BA1F3F2F3200EC035C /* Dratini.framework */; 400 | productType = "com.apple.product-type.framework"; 401 | }; 402 | 769999C11F3F2F3300EC035C /* Dratini-tvOSTests */ = { 403 | isa = PBXNativeTarget; 404 | buildConfigurationList = 769999CE1F3F2F3300EC035C /* Build configuration list for PBXNativeTarget "Dratini-tvOSTests" */; 405 | buildPhases = ( 406 | 769999BE1F3F2F3300EC035C /* Sources */, 407 | 769999BF1F3F2F3300EC035C /* Frameworks */, 408 | 769999C01F3F2F3300EC035C /* Resources */, 409 | 769999DF1F3F2FDC00EC035C /* Copy Frameworks */, 410 | ); 411 | buildRules = ( 412 | ); 413 | dependencies = ( 414 | 769999C51F3F2F3300EC035C /* PBXTargetDependency */, 415 | ); 416 | name = "Dratini-tvOSTests"; 417 | productName = "Dratini-tvOSTests"; 418 | productReference = 769999C21F3F2F3300EC035C /* Dratini-Tests.xctest */; 419 | productType = "com.apple.product-type.bundle.unit-test"; 420 | }; 421 | /* End PBXNativeTarget section */ 422 | 423 | /* Begin PBXProject section */ 424 | OBJ_1 /* Project object */ = { 425 | isa = PBXProject; 426 | attributes = { 427 | LastSwiftUpdateCheck = 0830; 428 | LastUpgradeCheck = 9999; 429 | TargetAttributes = { 430 | 769999481F3F293A00EC035C = { 431 | CreatedOnToolsVersion = 8.3.2; 432 | ProvisioningStyle = Automatic; 433 | }; 434 | 769999501F3F293A00EC035C = { 435 | CreatedOnToolsVersion = 8.3.2; 436 | ProvisioningStyle = Automatic; 437 | }; 438 | 769999731F3F2D1100EC035C = { 439 | CreatedOnToolsVersion = 8.3.2; 440 | ProvisioningStyle = Automatic; 441 | }; 442 | 7699997B1F3F2D1100EC035C = { 443 | CreatedOnToolsVersion = 8.3.2; 444 | ProvisioningStyle = Automatic; 445 | }; 446 | 769999A41F3F2EBB00EC035C = { 447 | CreatedOnToolsVersion = 8.3.2; 448 | ProvisioningStyle = Automatic; 449 | }; 450 | 769999B91F3F2F3200EC035C = { 451 | CreatedOnToolsVersion = 8.3.2; 452 | ProvisioningStyle = Automatic; 453 | }; 454 | 769999C11F3F2F3300EC035C = { 455 | CreatedOnToolsVersion = 8.3.2; 456 | ProvisioningStyle = Automatic; 457 | }; 458 | }; 459 | }; 460 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "Dratini" */; 461 | compatibilityVersion = "Xcode 3.2"; 462 | developmentRegion = English; 463 | hasScannedForEncodings = 0; 464 | knownRegions = ( 465 | en, 466 | ); 467 | mainGroup = OBJ_5; 468 | productRefGroup = OBJ_25 /* Products */; 469 | projectDirPath = ""; 470 | projectRoot = ""; 471 | targets = ( 472 | 769999481F3F293A00EC035C /* Dratini-iOS */, 473 | 769999731F3F2D1100EC035C /* Dratini-macOS */, 474 | 769999A41F3F2EBB00EC035C /* Dratini-watchOS */, 475 | 769999B91F3F2F3200EC035C /* Dratini-tvOS */, 476 | 769999501F3F293A00EC035C /* Dratini-iOSTests */, 477 | 7699997B1F3F2D1100EC035C /* Dratini-macOSTests */, 478 | 769999C11F3F2F3300EC035C /* Dratini-tvOSTests */, 479 | ); 480 | }; 481 | /* End PBXProject section */ 482 | 483 | /* Begin PBXResourcesBuildPhase section */ 484 | 769999471F3F293A00EC035C /* Resources */ = { 485 | isa = PBXResourcesBuildPhase; 486 | buildActionMask = 2147483647; 487 | files = ( 488 | ); 489 | runOnlyForDeploymentPostprocessing = 0; 490 | }; 491 | 7699994F1F3F293A00EC035C /* Resources */ = { 492 | isa = PBXResourcesBuildPhase; 493 | buildActionMask = 2147483647; 494 | files = ( 495 | ); 496 | runOnlyForDeploymentPostprocessing = 0; 497 | }; 498 | 769999721F3F2D1100EC035C /* Resources */ = { 499 | isa = PBXResourcesBuildPhase; 500 | buildActionMask = 2147483647; 501 | files = ( 502 | ); 503 | runOnlyForDeploymentPostprocessing = 0; 504 | }; 505 | 7699997A1F3F2D1100EC035C /* Resources */ = { 506 | isa = PBXResourcesBuildPhase; 507 | buildActionMask = 2147483647; 508 | files = ( 509 | ); 510 | runOnlyForDeploymentPostprocessing = 0; 511 | }; 512 | 769999A31F3F2EBB00EC035C /* Resources */ = { 513 | isa = PBXResourcesBuildPhase; 514 | buildActionMask = 2147483647; 515 | files = ( 516 | ); 517 | runOnlyForDeploymentPostprocessing = 0; 518 | }; 519 | 769999B81F3F2F3200EC035C /* Resources */ = { 520 | isa = PBXResourcesBuildPhase; 521 | buildActionMask = 2147483647; 522 | files = ( 523 | ); 524 | runOnlyForDeploymentPostprocessing = 0; 525 | }; 526 | 769999C01F3F2F3300EC035C /* Resources */ = { 527 | isa = PBXResourcesBuildPhase; 528 | buildActionMask = 2147483647; 529 | files = ( 530 | ); 531 | runOnlyForDeploymentPostprocessing = 0; 532 | }; 533 | /* End PBXResourcesBuildPhase section */ 534 | 535 | /* Begin PBXShellScriptBuildPhase section */ 536 | 7699996E1F3F2C8400EC035C /* Copy Frameworks */ = { 537 | isa = PBXShellScriptBuildPhase; 538 | buildActionMask = 2147483647; 539 | files = ( 540 | ); 541 | inputPaths = ( 542 | "$(SRCROOT)/Carthage/Build/iOS/Ditto.framework", 543 | ); 544 | name = "Copy Frameworks"; 545 | outputPaths = ( 546 | "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Ditto.framework", 547 | ); 548 | runOnlyForDeploymentPostprocessing = 0; 549 | shellPath = /bin/sh; 550 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 551 | }; 552 | 769999DF1F3F2FDC00EC035C /* Copy Frameworks */ = { 553 | isa = PBXShellScriptBuildPhase; 554 | buildActionMask = 2147483647; 555 | files = ( 556 | ); 557 | inputPaths = ( 558 | "$(SRCROOT)/Carthage/Build/tvOS/Ditto.framework", 559 | ); 560 | name = "Copy Frameworks"; 561 | outputPaths = ( 562 | "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Ditto.framework", 563 | ); 564 | runOnlyForDeploymentPostprocessing = 0; 565 | shellPath = /bin/sh; 566 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 567 | }; 568 | /* End PBXShellScriptBuildPhase section */ 569 | 570 | /* Begin PBXSourcesBuildPhase section */ 571 | 769999441F3F293A00EC035C /* Sources */ = { 572 | isa = PBXSourcesBuildPhase; 573 | buildActionMask = 2147483647; 574 | files = ( 575 | 769999601F3F2A6B00EC035C /* DRError.swift in Sources */, 576 | 769999611F3F2A6B00EC035C /* Parameters.swift in Sources */, 577 | 769999621F3F2A6B00EC035C /* Request.swift in Sources */, 578 | 769999631F3F2A6B00EC035C /* RequestConverter.swift in Sources */, 579 | 769999641F3F2A6B00EC035C /* RequestQueue.swift in Sources */, 580 | 769999651F3F2A6B00EC035C /* Response.swift in Sources */, 581 | ); 582 | runOnlyForDeploymentPostprocessing = 0; 583 | }; 584 | 7699994D1F3F293A00EC035C /* Sources */ = { 585 | isa = PBXSourcesBuildPhase; 586 | buildActionMask = 2147483647; 587 | files = ( 588 | 769999661F3F2A9F00EC035C /* ResponseTests.swift in Sources */, 589 | 769999671F3F2A9F00EC035C /* RequestTests.swift in Sources */, 590 | 769999681F3F2A9F00EC035C /* RequestQueueTests.swift in Sources */, 591 | 769999691F3F2A9F00EC035C /* RequestConverterTests.swift in Sources */, 592 | 7699996A1F3F2A9F00EC035C /* ParametersTests.swift in Sources */, 593 | ); 594 | runOnlyForDeploymentPostprocessing = 0; 595 | }; 596 | 7699996F1F3F2D1100EC035C /* Sources */ = { 597 | isa = PBXSourcesBuildPhase; 598 | buildActionMask = 2147483647; 599 | files = ( 600 | 7699998B1F3F2D6700EC035C /* DRError.swift in Sources */, 601 | 7699998C1F3F2D6700EC035C /* Parameters.swift in Sources */, 602 | 7699998D1F3F2D6700EC035C /* Request.swift in Sources */, 603 | 7699998E1F3F2D6700EC035C /* RequestConverter.swift in Sources */, 604 | 7699998F1F3F2D6700EC035C /* RequestQueue.swift in Sources */, 605 | 769999901F3F2D6700EC035C /* Response.swift in Sources */, 606 | ); 607 | runOnlyForDeploymentPostprocessing = 0; 608 | }; 609 | 769999781F3F2D1100EC035C /* Sources */ = { 610 | isa = PBXSourcesBuildPhase; 611 | buildActionMask = 2147483647; 612 | files = ( 613 | 769999911F3F2D8E00EC035C /* ResponseTests.swift in Sources */, 614 | 769999921F3F2D8E00EC035C /* RequestTests.swift in Sources */, 615 | 769999931F3F2D8E00EC035C /* RequestQueueTests.swift in Sources */, 616 | 769999941F3F2D8E00EC035C /* RequestConverterTests.swift in Sources */, 617 | 769999951F3F2D8E00EC035C /* ParametersTests.swift in Sources */, 618 | ); 619 | runOnlyForDeploymentPostprocessing = 0; 620 | }; 621 | 769999A01F3F2EBB00EC035C /* Sources */ = { 622 | isa = PBXSourcesBuildPhase; 623 | buildActionMask = 2147483647; 624 | files = ( 625 | 769999AD1F3F2F0900EC035C /* DRError.swift in Sources */, 626 | 769999AE1F3F2F0900EC035C /* Parameters.swift in Sources */, 627 | 769999AF1F3F2F0900EC035C /* Request.swift in Sources */, 628 | 769999B01F3F2F0900EC035C /* RequestConverter.swift in Sources */, 629 | 769999B11F3F2F0900EC035C /* RequestQueue.swift in Sources */, 630 | 769999B21F3F2F0900EC035C /* Response.swift in Sources */, 631 | ); 632 | runOnlyForDeploymentPostprocessing = 0; 633 | }; 634 | 769999B51F3F2F3200EC035C /* Sources */ = { 635 | isa = PBXSourcesBuildPhase; 636 | buildActionMask = 2147483647; 637 | files = ( 638 | 769999D31F3F2F8500EC035C /* DRError.swift in Sources */, 639 | 769999D41F3F2F8500EC035C /* Parameters.swift in Sources */, 640 | 769999D51F3F2F8500EC035C /* Request.swift in Sources */, 641 | 769999D61F3F2F8500EC035C /* RequestConverter.swift in Sources */, 642 | 769999D71F3F2F8500EC035C /* RequestQueue.swift in Sources */, 643 | 769999D81F3F2F8500EC035C /* Response.swift in Sources */, 644 | ); 645 | runOnlyForDeploymentPostprocessing = 0; 646 | }; 647 | 769999BE1F3F2F3300EC035C /* Sources */ = { 648 | isa = PBXSourcesBuildPhase; 649 | buildActionMask = 2147483647; 650 | files = ( 651 | 769999D91F3F2FAB00EC035C /* ResponseTests.swift in Sources */, 652 | 769999DA1F3F2FAB00EC035C /* RequestTests.swift in Sources */, 653 | 769999DB1F3F2FAB00EC035C /* RequestQueueTests.swift in Sources */, 654 | 769999DC1F3F2FAB00EC035C /* RequestConverterTests.swift in Sources */, 655 | 769999DD1F3F2FAB00EC035C /* ParametersTests.swift in Sources */, 656 | ); 657 | runOnlyForDeploymentPostprocessing = 0; 658 | }; 659 | /* End PBXSourcesBuildPhase section */ 660 | 661 | /* Begin PBXTargetDependency section */ 662 | 769999541F3F293A00EC035C /* PBXTargetDependency */ = { 663 | isa = PBXTargetDependency; 664 | target = 769999481F3F293A00EC035C /* Dratini-iOS */; 665 | targetProxy = 769999531F3F293A00EC035C /* PBXContainerItemProxy */; 666 | }; 667 | 7699997F1F3F2D1100EC035C /* PBXTargetDependency */ = { 668 | isa = PBXTargetDependency; 669 | target = 769999731F3F2D1100EC035C /* Dratini-macOS */; 670 | targetProxy = 7699997E1F3F2D1100EC035C /* PBXContainerItemProxy */; 671 | }; 672 | 769999C51F3F2F3300EC035C /* PBXTargetDependency */ = { 673 | isa = PBXTargetDependency; 674 | target = 769999B91F3F2F3200EC035C /* Dratini-tvOS */; 675 | targetProxy = 769999C41F3F2F3300EC035C /* PBXContainerItemProxy */; 676 | }; 677 | /* End PBXTargetDependency section */ 678 | 679 | /* Begin XCBuildConfiguration section */ 680 | 7699995B1F3F293A00EC035C /* Debug */ = { 681 | isa = XCBuildConfiguration; 682 | buildSettings = { 683 | ALWAYS_SEARCH_USER_PATHS = NO; 684 | CLANG_ANALYZER_NONNULL = YES; 685 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 686 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 687 | CLANG_CXX_LIBRARY = "libc++"; 688 | CLANG_ENABLE_MODULES = YES; 689 | CLANG_WARN_BOOL_CONVERSION = YES; 690 | CLANG_WARN_CONSTANT_CONVERSION = YES; 691 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 692 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 693 | CLANG_WARN_EMPTY_BODY = YES; 694 | CLANG_WARN_ENUM_CONVERSION = YES; 695 | CLANG_WARN_INFINITE_RECURSION = YES; 696 | CLANG_WARN_INT_CONVERSION = YES; 697 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 698 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 699 | CLANG_WARN_UNREACHABLE_CODE = YES; 700 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 701 | CODE_SIGN_IDENTITY = ""; 702 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 703 | CURRENT_PROJECT_VERSION = 1; 704 | DEFINES_MODULE = YES; 705 | DYLIB_COMPATIBILITY_VERSION = 1; 706 | DYLIB_CURRENT_VERSION = 1; 707 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 708 | ENABLE_STRICT_OBJC_MSGSEND = YES; 709 | ENABLE_TESTABILITY = YES; 710 | FRAMEWORK_SEARCH_PATHS = ( 711 | "$(inherited)", 712 | "$(PROJECT_DIR)/Carthage/Build/iOS", 713 | ); 714 | GCC_C_LANGUAGE_STANDARD = gnu99; 715 | GCC_DYNAMIC_NO_PIC = NO; 716 | GCC_NO_COMMON_BLOCKS = YES; 717 | GCC_PREPROCESSOR_DEFINITIONS = ( 718 | "DEBUG=1", 719 | "$(inherited)", 720 | ); 721 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 722 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 723 | GCC_WARN_UNDECLARED_SELECTOR = YES; 724 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 725 | GCC_WARN_UNUSED_FUNCTION = YES; 726 | GCC_WARN_UNUSED_VARIABLE = YES; 727 | INFOPLIST_FILE = Sources/Info.plist; 728 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 729 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 730 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 731 | MTL_ENABLE_DEBUG_INFO = YES; 732 | PRODUCT_BUNDLE_IDENTIFIER = Dratini; 733 | PRODUCT_NAME = Dratini; 734 | SDKROOT = iphoneos; 735 | SKIP_INSTALL = YES; 736 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; 737 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 738 | SWIFT_VERSION = 3.0; 739 | TARGETED_DEVICE_FAMILY = "1,2"; 740 | VERSIONING_SYSTEM = "apple-generic"; 741 | VERSION_INFO_PREFIX = ""; 742 | }; 743 | name = Debug; 744 | }; 745 | 7699995C1F3F293A00EC035C /* Release */ = { 746 | isa = XCBuildConfiguration; 747 | buildSettings = { 748 | ALWAYS_SEARCH_USER_PATHS = NO; 749 | CLANG_ANALYZER_NONNULL = YES; 750 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 751 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 752 | CLANG_CXX_LIBRARY = "libc++"; 753 | CLANG_ENABLE_MODULES = YES; 754 | CLANG_WARN_BOOL_CONVERSION = YES; 755 | CLANG_WARN_CONSTANT_CONVERSION = YES; 756 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 757 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 758 | CLANG_WARN_EMPTY_BODY = YES; 759 | CLANG_WARN_ENUM_CONVERSION = YES; 760 | CLANG_WARN_INFINITE_RECURSION = YES; 761 | CLANG_WARN_INT_CONVERSION = YES; 762 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 763 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 764 | CLANG_WARN_UNREACHABLE_CODE = YES; 765 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 766 | CODE_SIGN_IDENTITY = ""; 767 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 768 | COPY_PHASE_STRIP = NO; 769 | CURRENT_PROJECT_VERSION = 1; 770 | DEFINES_MODULE = YES; 771 | DYLIB_COMPATIBILITY_VERSION = 1; 772 | DYLIB_CURRENT_VERSION = 1; 773 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 774 | ENABLE_NS_ASSERTIONS = NO; 775 | ENABLE_STRICT_OBJC_MSGSEND = YES; 776 | FRAMEWORK_SEARCH_PATHS = ( 777 | "$(inherited)", 778 | "$(PROJECT_DIR)/Carthage/Build/iOS", 779 | ); 780 | GCC_C_LANGUAGE_STANDARD = gnu99; 781 | GCC_NO_COMMON_BLOCKS = YES; 782 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 783 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 784 | GCC_WARN_UNDECLARED_SELECTOR = YES; 785 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 786 | GCC_WARN_UNUSED_FUNCTION = YES; 787 | GCC_WARN_UNUSED_VARIABLE = YES; 788 | INFOPLIST_FILE = Sources/Info.plist; 789 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 790 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 791 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 792 | MTL_ENABLE_DEBUG_INFO = NO; 793 | PRODUCT_BUNDLE_IDENTIFIER = Dratini; 794 | PRODUCT_NAME = Dratini; 795 | SDKROOT = iphoneos; 796 | SKIP_INSTALL = YES; 797 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; 798 | SWIFT_VERSION = 3.0; 799 | TARGETED_DEVICE_FAMILY = "1,2"; 800 | VALIDATE_PRODUCT = YES; 801 | VERSIONING_SYSTEM = "apple-generic"; 802 | VERSION_INFO_PREFIX = ""; 803 | }; 804 | name = Release; 805 | }; 806 | 7699995E1F3F293A00EC035C /* Debug */ = { 807 | isa = XCBuildConfiguration; 808 | buildSettings = { 809 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 810 | ALWAYS_SEARCH_USER_PATHS = NO; 811 | CLANG_ANALYZER_NONNULL = YES; 812 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 813 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 814 | CLANG_CXX_LIBRARY = "libc++"; 815 | CLANG_ENABLE_MODULES = YES; 816 | CLANG_WARN_BOOL_CONVERSION = YES; 817 | CLANG_WARN_CONSTANT_CONVERSION = YES; 818 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 819 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 820 | CLANG_WARN_EMPTY_BODY = YES; 821 | CLANG_WARN_ENUM_CONVERSION = YES; 822 | CLANG_WARN_INFINITE_RECURSION = YES; 823 | CLANG_WARN_INT_CONVERSION = YES; 824 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 825 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 826 | CLANG_WARN_UNREACHABLE_CODE = YES; 827 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 828 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 829 | ENABLE_STRICT_OBJC_MSGSEND = YES; 830 | ENABLE_TESTABILITY = YES; 831 | FRAMEWORK_SEARCH_PATHS = ( 832 | "$(inherited)", 833 | "$(PROJECT_DIR)/Carthage/Build/iOS", 834 | ); 835 | GCC_C_LANGUAGE_STANDARD = gnu99; 836 | GCC_DYNAMIC_NO_PIC = NO; 837 | GCC_NO_COMMON_BLOCKS = YES; 838 | GCC_PREPROCESSOR_DEFINITIONS = ( 839 | "DEBUG=1", 840 | "$(inherited)", 841 | ); 842 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 843 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 844 | GCC_WARN_UNDECLARED_SELECTOR = YES; 845 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 846 | GCC_WARN_UNUSED_FUNCTION = YES; 847 | GCC_WARN_UNUSED_VARIABLE = YES; 848 | INFOPLIST_FILE = Tests/Info.plist; 849 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 850 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 851 | MTL_ENABLE_DEBUG_INFO = YES; 852 | PRODUCT_BUNDLE_IDENTIFIER = "Dratini-Tests"; 853 | PRODUCT_NAME = "Dratini-Tests"; 854 | SDKROOT = iphoneos; 855 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 856 | SWIFT_VERSION = 3.0; 857 | }; 858 | name = Debug; 859 | }; 860 | 7699995F1F3F293A00EC035C /* Release */ = { 861 | isa = XCBuildConfiguration; 862 | buildSettings = { 863 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 864 | ALWAYS_SEARCH_USER_PATHS = NO; 865 | CLANG_ANALYZER_NONNULL = YES; 866 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 867 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 868 | CLANG_CXX_LIBRARY = "libc++"; 869 | CLANG_ENABLE_MODULES = YES; 870 | CLANG_WARN_BOOL_CONVERSION = YES; 871 | CLANG_WARN_CONSTANT_CONVERSION = YES; 872 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 873 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 874 | CLANG_WARN_EMPTY_BODY = YES; 875 | CLANG_WARN_ENUM_CONVERSION = YES; 876 | CLANG_WARN_INFINITE_RECURSION = YES; 877 | CLANG_WARN_INT_CONVERSION = YES; 878 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 879 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 880 | CLANG_WARN_UNREACHABLE_CODE = YES; 881 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 882 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 883 | COPY_PHASE_STRIP = NO; 884 | ENABLE_NS_ASSERTIONS = NO; 885 | ENABLE_STRICT_OBJC_MSGSEND = YES; 886 | FRAMEWORK_SEARCH_PATHS = ( 887 | "$(inherited)", 888 | "$(PROJECT_DIR)/Carthage/Build/iOS", 889 | ); 890 | GCC_C_LANGUAGE_STANDARD = gnu99; 891 | GCC_NO_COMMON_BLOCKS = YES; 892 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 893 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 894 | GCC_WARN_UNDECLARED_SELECTOR = YES; 895 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 896 | GCC_WARN_UNUSED_FUNCTION = YES; 897 | GCC_WARN_UNUSED_VARIABLE = YES; 898 | INFOPLIST_FILE = Tests/Info.plist; 899 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 900 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 901 | MTL_ENABLE_DEBUG_INFO = NO; 902 | PRODUCT_BUNDLE_IDENTIFIER = "Dratini-Tests"; 903 | PRODUCT_NAME = "Dratini-Tests"; 904 | SDKROOT = iphoneos; 905 | SWIFT_VERSION = 3.0; 906 | VALIDATE_PRODUCT = YES; 907 | }; 908 | name = Release; 909 | }; 910 | 769999861F3F2D1100EC035C /* Debug */ = { 911 | isa = XCBuildConfiguration; 912 | buildSettings = { 913 | ALWAYS_SEARCH_USER_PATHS = NO; 914 | CLANG_ANALYZER_NONNULL = YES; 915 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 916 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 917 | CLANG_CXX_LIBRARY = "libc++"; 918 | CLANG_ENABLE_MODULES = YES; 919 | CLANG_WARN_BOOL_CONVERSION = YES; 920 | CLANG_WARN_CONSTANT_CONVERSION = YES; 921 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 922 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 923 | CLANG_WARN_EMPTY_BODY = YES; 924 | CLANG_WARN_ENUM_CONVERSION = YES; 925 | CLANG_WARN_INFINITE_RECURSION = YES; 926 | CLANG_WARN_INT_CONVERSION = YES; 927 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 928 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 929 | CLANG_WARN_UNREACHABLE_CODE = YES; 930 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 931 | CODE_SIGN_IDENTITY = "-"; 932 | COMBINE_HIDPI_IMAGES = YES; 933 | CURRENT_PROJECT_VERSION = 1; 934 | DEFINES_MODULE = YES; 935 | DYLIB_COMPATIBILITY_VERSION = 1; 936 | DYLIB_CURRENT_VERSION = 1; 937 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 938 | ENABLE_STRICT_OBJC_MSGSEND = YES; 939 | ENABLE_TESTABILITY = YES; 940 | FRAMEWORK_SEARCH_PATHS = ( 941 | "$(inherited)", 942 | "$(PROJECT_DIR)/Carthage/Build/Mac", 943 | ); 944 | FRAMEWORK_VERSION = A; 945 | GCC_C_LANGUAGE_STANDARD = gnu99; 946 | GCC_DYNAMIC_NO_PIC = NO; 947 | GCC_NO_COMMON_BLOCKS = YES; 948 | GCC_PREPROCESSOR_DEFINITIONS = ( 949 | "DEBUG=1", 950 | "$(inherited)", 951 | ); 952 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 953 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 954 | GCC_WARN_UNDECLARED_SELECTOR = YES; 955 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 956 | GCC_WARN_UNUSED_FUNCTION = YES; 957 | GCC_WARN_UNUSED_VARIABLE = YES; 958 | INFOPLIST_FILE = Sources/Info.plist; 959 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 960 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 961 | MACOSX_DEPLOYMENT_TARGET = 10.10; 962 | MTL_ENABLE_DEBUG_INFO = YES; 963 | PRODUCT_BUNDLE_IDENTIFIER = Dratini; 964 | PRODUCT_NAME = Dratini; 965 | SKIP_INSTALL = YES; 966 | SUPPORTED_PLATFORMS = macosx; 967 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 968 | SWIFT_VERSION = 3.0; 969 | VERSIONING_SYSTEM = "apple-generic"; 970 | VERSION_INFO_PREFIX = ""; 971 | }; 972 | name = Debug; 973 | }; 974 | 769999871F3F2D1100EC035C /* Release */ = { 975 | isa = XCBuildConfiguration; 976 | buildSettings = { 977 | ALWAYS_SEARCH_USER_PATHS = NO; 978 | CLANG_ANALYZER_NONNULL = YES; 979 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 980 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 981 | CLANG_CXX_LIBRARY = "libc++"; 982 | CLANG_ENABLE_MODULES = YES; 983 | CLANG_WARN_BOOL_CONVERSION = YES; 984 | CLANG_WARN_CONSTANT_CONVERSION = YES; 985 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 986 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 987 | CLANG_WARN_EMPTY_BODY = YES; 988 | CLANG_WARN_ENUM_CONVERSION = YES; 989 | CLANG_WARN_INFINITE_RECURSION = YES; 990 | CLANG_WARN_INT_CONVERSION = YES; 991 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 992 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 993 | CLANG_WARN_UNREACHABLE_CODE = YES; 994 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 995 | CODE_SIGN_IDENTITY = "-"; 996 | COMBINE_HIDPI_IMAGES = YES; 997 | COPY_PHASE_STRIP = NO; 998 | CURRENT_PROJECT_VERSION = 1; 999 | DEFINES_MODULE = YES; 1000 | DYLIB_COMPATIBILITY_VERSION = 1; 1001 | DYLIB_CURRENT_VERSION = 1; 1002 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1003 | ENABLE_NS_ASSERTIONS = NO; 1004 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1005 | FRAMEWORK_SEARCH_PATHS = ( 1006 | "$(inherited)", 1007 | "$(PROJECT_DIR)/Carthage/Build/Mac", 1008 | ); 1009 | FRAMEWORK_VERSION = A; 1010 | GCC_C_LANGUAGE_STANDARD = gnu99; 1011 | GCC_NO_COMMON_BLOCKS = YES; 1012 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1013 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1014 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1015 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1016 | GCC_WARN_UNUSED_FUNCTION = YES; 1017 | GCC_WARN_UNUSED_VARIABLE = YES; 1018 | INFOPLIST_FILE = Sources/Info.plist; 1019 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1020 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 1021 | MACOSX_DEPLOYMENT_TARGET = 10.10; 1022 | MTL_ENABLE_DEBUG_INFO = NO; 1023 | PRODUCT_BUNDLE_IDENTIFIER = Dratini; 1024 | PRODUCT_NAME = Dratini; 1025 | SKIP_INSTALL = YES; 1026 | SUPPORTED_PLATFORMS = macosx; 1027 | SWIFT_VERSION = 3.0; 1028 | VERSIONING_SYSTEM = "apple-generic"; 1029 | VERSION_INFO_PREFIX = ""; 1030 | }; 1031 | name = Release; 1032 | }; 1033 | 769999891F3F2D1100EC035C /* Debug */ = { 1034 | isa = XCBuildConfiguration; 1035 | buildSettings = { 1036 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1037 | ALWAYS_SEARCH_USER_PATHS = NO; 1038 | CLANG_ANALYZER_NONNULL = YES; 1039 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 1040 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1041 | CLANG_CXX_LIBRARY = "libc++"; 1042 | CLANG_ENABLE_MODULES = YES; 1043 | CLANG_WARN_BOOL_CONVERSION = YES; 1044 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1045 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1046 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1047 | CLANG_WARN_EMPTY_BODY = YES; 1048 | CLANG_WARN_ENUM_CONVERSION = YES; 1049 | CLANG_WARN_INFINITE_RECURSION = YES; 1050 | CLANG_WARN_INT_CONVERSION = YES; 1051 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1052 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1053 | CLANG_WARN_UNREACHABLE_CODE = YES; 1054 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1055 | CODE_SIGN_IDENTITY = "-"; 1056 | COMBINE_HIDPI_IMAGES = YES; 1057 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1058 | ENABLE_TESTABILITY = YES; 1059 | FRAMEWORK_SEARCH_PATHS = ( 1060 | "$(inherited)", 1061 | "$(PROJECT_DIR)/Carthage/Build/Mac", 1062 | ); 1063 | GCC_C_LANGUAGE_STANDARD = gnu99; 1064 | GCC_DYNAMIC_NO_PIC = NO; 1065 | GCC_NO_COMMON_BLOCKS = YES; 1066 | GCC_PREPROCESSOR_DEFINITIONS = ( 1067 | "DEBUG=1", 1068 | "$(inherited)", 1069 | ); 1070 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1071 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1072 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1073 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1074 | GCC_WARN_UNUSED_FUNCTION = YES; 1075 | GCC_WARN_UNUSED_VARIABLE = YES; 1076 | INFOPLIST_FILE = Tests/Info.plist; 1077 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1078 | MACOSX_DEPLOYMENT_TARGET = 10.12; 1079 | MTL_ENABLE_DEBUG_INFO = YES; 1080 | PRODUCT_BUNDLE_IDENTIFIER = "Dratini-Tests"; 1081 | PRODUCT_NAME = "Dratini-Tests"; 1082 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 1083 | SWIFT_VERSION = 3.0; 1084 | }; 1085 | name = Debug; 1086 | }; 1087 | 7699998A1F3F2D1100EC035C /* Release */ = { 1088 | isa = XCBuildConfiguration; 1089 | buildSettings = { 1090 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1091 | ALWAYS_SEARCH_USER_PATHS = NO; 1092 | CLANG_ANALYZER_NONNULL = YES; 1093 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 1094 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1095 | CLANG_CXX_LIBRARY = "libc++"; 1096 | CLANG_ENABLE_MODULES = YES; 1097 | CLANG_WARN_BOOL_CONVERSION = YES; 1098 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1099 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1100 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1101 | CLANG_WARN_EMPTY_BODY = YES; 1102 | CLANG_WARN_ENUM_CONVERSION = YES; 1103 | CLANG_WARN_INFINITE_RECURSION = YES; 1104 | CLANG_WARN_INT_CONVERSION = YES; 1105 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1106 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1107 | CLANG_WARN_UNREACHABLE_CODE = YES; 1108 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1109 | CODE_SIGN_IDENTITY = "-"; 1110 | COMBINE_HIDPI_IMAGES = YES; 1111 | COPY_PHASE_STRIP = NO; 1112 | ENABLE_NS_ASSERTIONS = NO; 1113 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1114 | FRAMEWORK_SEARCH_PATHS = ( 1115 | "$(inherited)", 1116 | "$(PROJECT_DIR)/Carthage/Build/Mac", 1117 | ); 1118 | GCC_C_LANGUAGE_STANDARD = gnu99; 1119 | GCC_NO_COMMON_BLOCKS = YES; 1120 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1121 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1122 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1123 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1124 | GCC_WARN_UNUSED_FUNCTION = YES; 1125 | GCC_WARN_UNUSED_VARIABLE = YES; 1126 | INFOPLIST_FILE = Tests/Info.plist; 1127 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1128 | MACOSX_DEPLOYMENT_TARGET = 10.12; 1129 | MTL_ENABLE_DEBUG_INFO = NO; 1130 | PRODUCT_BUNDLE_IDENTIFIER = "Dratini-Tests"; 1131 | PRODUCT_NAME = "Dratini-Tests"; 1132 | SWIFT_VERSION = 3.0; 1133 | }; 1134 | name = Release; 1135 | }; 1136 | 769999AB1F3F2EBB00EC035C /* Debug */ = { 1137 | isa = XCBuildConfiguration; 1138 | buildSettings = { 1139 | ALWAYS_SEARCH_USER_PATHS = NO; 1140 | APPLICATION_EXTENSION_API_ONLY = YES; 1141 | CLANG_ANALYZER_NONNULL = YES; 1142 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 1143 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1144 | CLANG_CXX_LIBRARY = "libc++"; 1145 | CLANG_ENABLE_MODULES = YES; 1146 | CLANG_WARN_BOOL_CONVERSION = YES; 1147 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1148 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1149 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1150 | CLANG_WARN_EMPTY_BODY = YES; 1151 | CLANG_WARN_ENUM_CONVERSION = YES; 1152 | CLANG_WARN_INFINITE_RECURSION = YES; 1153 | CLANG_WARN_INT_CONVERSION = YES; 1154 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1155 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1156 | CLANG_WARN_UNREACHABLE_CODE = YES; 1157 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1158 | CODE_SIGN_IDENTITY = ""; 1159 | CURRENT_PROJECT_VERSION = 1; 1160 | DEFINES_MODULE = YES; 1161 | DYLIB_COMPATIBILITY_VERSION = 1; 1162 | DYLIB_CURRENT_VERSION = 1; 1163 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1164 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1165 | ENABLE_TESTABILITY = YES; 1166 | FRAMEWORK_SEARCH_PATHS = ( 1167 | "$(inherited)", 1168 | "$(PROJECT_DIR)/Carthage/Build/watchOS", 1169 | ); 1170 | GCC_C_LANGUAGE_STANDARD = gnu99; 1171 | GCC_DYNAMIC_NO_PIC = NO; 1172 | GCC_NO_COMMON_BLOCKS = YES; 1173 | GCC_PREPROCESSOR_DEFINITIONS = ( 1174 | "DEBUG=1", 1175 | "$(inherited)", 1176 | ); 1177 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1178 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1179 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1180 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1181 | GCC_WARN_UNUSED_FUNCTION = YES; 1182 | GCC_WARN_UNUSED_VARIABLE = YES; 1183 | INFOPLIST_FILE = Sources/Info.plist; 1184 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1185 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1186 | MTL_ENABLE_DEBUG_INFO = YES; 1187 | PRODUCT_BUNDLE_IDENTIFIER = Dratini; 1188 | PRODUCT_NAME = Dratini; 1189 | SDKROOT = watchos; 1190 | SKIP_INSTALL = YES; 1191 | SUPPORTED_PLATFORMS = "watchsimulator watchos"; 1192 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 1193 | SWIFT_VERSION = 3.0; 1194 | TARGETED_DEVICE_FAMILY = 4; 1195 | VERSIONING_SYSTEM = "apple-generic"; 1196 | VERSION_INFO_PREFIX = ""; 1197 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 1198 | }; 1199 | name = Debug; 1200 | }; 1201 | 769999AC1F3F2EBB00EC035C /* Release */ = { 1202 | isa = XCBuildConfiguration; 1203 | buildSettings = { 1204 | ALWAYS_SEARCH_USER_PATHS = NO; 1205 | APPLICATION_EXTENSION_API_ONLY = YES; 1206 | CLANG_ANALYZER_NONNULL = YES; 1207 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 1208 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1209 | CLANG_CXX_LIBRARY = "libc++"; 1210 | CLANG_ENABLE_MODULES = YES; 1211 | CLANG_WARN_BOOL_CONVERSION = YES; 1212 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1213 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1214 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1215 | CLANG_WARN_EMPTY_BODY = YES; 1216 | CLANG_WARN_ENUM_CONVERSION = YES; 1217 | CLANG_WARN_INFINITE_RECURSION = YES; 1218 | CLANG_WARN_INT_CONVERSION = YES; 1219 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1220 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1221 | CLANG_WARN_UNREACHABLE_CODE = YES; 1222 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1223 | CODE_SIGN_IDENTITY = ""; 1224 | COPY_PHASE_STRIP = NO; 1225 | CURRENT_PROJECT_VERSION = 1; 1226 | DEFINES_MODULE = YES; 1227 | DYLIB_COMPATIBILITY_VERSION = 1; 1228 | DYLIB_CURRENT_VERSION = 1; 1229 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1230 | ENABLE_NS_ASSERTIONS = NO; 1231 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1232 | FRAMEWORK_SEARCH_PATHS = ( 1233 | "$(inherited)", 1234 | "$(PROJECT_DIR)/Carthage/Build/watchOS", 1235 | ); 1236 | GCC_C_LANGUAGE_STANDARD = gnu99; 1237 | GCC_NO_COMMON_BLOCKS = YES; 1238 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1239 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1240 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1241 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1242 | GCC_WARN_UNUSED_FUNCTION = YES; 1243 | GCC_WARN_UNUSED_VARIABLE = YES; 1244 | INFOPLIST_FILE = Sources/Info.plist; 1245 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1246 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1247 | MTL_ENABLE_DEBUG_INFO = NO; 1248 | PRODUCT_BUNDLE_IDENTIFIER = Dratini; 1249 | PRODUCT_NAME = Dratini; 1250 | SDKROOT = watchos; 1251 | SKIP_INSTALL = YES; 1252 | SUPPORTED_PLATFORMS = "watchsimulator watchos"; 1253 | SWIFT_VERSION = 3.0; 1254 | TARGETED_DEVICE_FAMILY = 4; 1255 | VALIDATE_PRODUCT = YES; 1256 | VERSIONING_SYSTEM = "apple-generic"; 1257 | VERSION_INFO_PREFIX = ""; 1258 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 1259 | }; 1260 | name = Release; 1261 | }; 1262 | 769999CC1F3F2F3300EC035C /* Debug */ = { 1263 | isa = XCBuildConfiguration; 1264 | buildSettings = { 1265 | ALWAYS_SEARCH_USER_PATHS = NO; 1266 | CLANG_ANALYZER_NONNULL = YES; 1267 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 1268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1269 | CLANG_CXX_LIBRARY = "libc++"; 1270 | CLANG_ENABLE_MODULES = YES; 1271 | CLANG_WARN_BOOL_CONVERSION = YES; 1272 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1275 | CLANG_WARN_EMPTY_BODY = YES; 1276 | CLANG_WARN_ENUM_CONVERSION = YES; 1277 | CLANG_WARN_INFINITE_RECURSION = YES; 1278 | CLANG_WARN_INT_CONVERSION = YES; 1279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1280 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1281 | CLANG_WARN_UNREACHABLE_CODE = YES; 1282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1283 | CODE_SIGN_IDENTITY = ""; 1284 | CURRENT_PROJECT_VERSION = 1; 1285 | DEFINES_MODULE = YES; 1286 | DYLIB_COMPATIBILITY_VERSION = 1; 1287 | DYLIB_CURRENT_VERSION = 1; 1288 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1290 | ENABLE_TESTABILITY = YES; 1291 | FRAMEWORK_SEARCH_PATHS = ( 1292 | "$(inherited)", 1293 | "$(PROJECT_DIR)/Carthage/Build/tvOS", 1294 | ); 1295 | GCC_C_LANGUAGE_STANDARD = gnu99; 1296 | GCC_DYNAMIC_NO_PIC = NO; 1297 | GCC_NO_COMMON_BLOCKS = YES; 1298 | GCC_PREPROCESSOR_DEFINITIONS = ( 1299 | "DEBUG=1", 1300 | "$(inherited)", 1301 | ); 1302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1303 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1304 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1305 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1306 | GCC_WARN_UNUSED_FUNCTION = YES; 1307 | GCC_WARN_UNUSED_VARIABLE = YES; 1308 | INFOPLIST_FILE = Sources/Info.plist; 1309 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1310 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1311 | MTL_ENABLE_DEBUG_INFO = YES; 1312 | PRODUCT_BUNDLE_IDENTIFIER = Dratini; 1313 | PRODUCT_NAME = Dratini; 1314 | SDKROOT = appletvos; 1315 | SKIP_INSTALL = YES; 1316 | SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; 1317 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 1318 | SWIFT_VERSION = 3.0; 1319 | TARGETED_DEVICE_FAMILY = 3; 1320 | TVOS_DEPLOYMENT_TARGET = 9.0; 1321 | VERSIONING_SYSTEM = "apple-generic"; 1322 | VERSION_INFO_PREFIX = ""; 1323 | }; 1324 | name = Debug; 1325 | }; 1326 | 769999CD1F3F2F3300EC035C /* Release */ = { 1327 | isa = XCBuildConfiguration; 1328 | buildSettings = { 1329 | ALWAYS_SEARCH_USER_PATHS = NO; 1330 | CLANG_ANALYZER_NONNULL = YES; 1331 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 1332 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1333 | CLANG_CXX_LIBRARY = "libc++"; 1334 | CLANG_ENABLE_MODULES = YES; 1335 | CLANG_WARN_BOOL_CONVERSION = YES; 1336 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1337 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1338 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1339 | CLANG_WARN_EMPTY_BODY = YES; 1340 | CLANG_WARN_ENUM_CONVERSION = YES; 1341 | CLANG_WARN_INFINITE_RECURSION = YES; 1342 | CLANG_WARN_INT_CONVERSION = YES; 1343 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1344 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1345 | CLANG_WARN_UNREACHABLE_CODE = YES; 1346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1347 | CODE_SIGN_IDENTITY = ""; 1348 | COPY_PHASE_STRIP = NO; 1349 | CURRENT_PROJECT_VERSION = 1; 1350 | DEFINES_MODULE = YES; 1351 | DYLIB_COMPATIBILITY_VERSION = 1; 1352 | DYLIB_CURRENT_VERSION = 1; 1353 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1354 | ENABLE_NS_ASSERTIONS = NO; 1355 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1356 | FRAMEWORK_SEARCH_PATHS = ( 1357 | "$(inherited)", 1358 | "$(PROJECT_DIR)/Carthage/Build/tvOS", 1359 | ); 1360 | GCC_C_LANGUAGE_STANDARD = gnu99; 1361 | GCC_NO_COMMON_BLOCKS = YES; 1362 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1363 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1364 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1365 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1366 | GCC_WARN_UNUSED_FUNCTION = YES; 1367 | GCC_WARN_UNUSED_VARIABLE = YES; 1368 | INFOPLIST_FILE = Sources/Info.plist; 1369 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1370 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1371 | MTL_ENABLE_DEBUG_INFO = NO; 1372 | PRODUCT_BUNDLE_IDENTIFIER = Dratini; 1373 | PRODUCT_NAME = Dratini; 1374 | SDKROOT = appletvos; 1375 | SKIP_INSTALL = YES; 1376 | SUPPORTED_PLATFORMS = "appletvsimulator appletvos"; 1377 | SWIFT_VERSION = 3.0; 1378 | TARGETED_DEVICE_FAMILY = 3; 1379 | TVOS_DEPLOYMENT_TARGET = 9.0; 1380 | VALIDATE_PRODUCT = YES; 1381 | VERSIONING_SYSTEM = "apple-generic"; 1382 | VERSION_INFO_PREFIX = ""; 1383 | }; 1384 | name = Release; 1385 | }; 1386 | 769999CF1F3F2F3300EC035C /* Debug */ = { 1387 | isa = XCBuildConfiguration; 1388 | buildSettings = { 1389 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1390 | ALWAYS_SEARCH_USER_PATHS = NO; 1391 | CLANG_ANALYZER_NONNULL = YES; 1392 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 1393 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1394 | CLANG_CXX_LIBRARY = "libc++"; 1395 | CLANG_ENABLE_MODULES = YES; 1396 | CLANG_WARN_BOOL_CONVERSION = YES; 1397 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1398 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1399 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1400 | CLANG_WARN_EMPTY_BODY = YES; 1401 | CLANG_WARN_ENUM_CONVERSION = YES; 1402 | CLANG_WARN_INFINITE_RECURSION = YES; 1403 | CLANG_WARN_INT_CONVERSION = YES; 1404 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1405 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1406 | CLANG_WARN_UNREACHABLE_CODE = YES; 1407 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1408 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1409 | ENABLE_TESTABILITY = YES; 1410 | FRAMEWORK_SEARCH_PATHS = ( 1411 | "$(inherited)", 1412 | "$(PROJECT_DIR)/Carthage/Build/tvOS", 1413 | ); 1414 | GCC_C_LANGUAGE_STANDARD = gnu99; 1415 | GCC_DYNAMIC_NO_PIC = NO; 1416 | GCC_NO_COMMON_BLOCKS = YES; 1417 | GCC_PREPROCESSOR_DEFINITIONS = ( 1418 | "DEBUG=1", 1419 | "$(inherited)", 1420 | ); 1421 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1422 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1423 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1424 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1425 | GCC_WARN_UNUSED_FUNCTION = YES; 1426 | GCC_WARN_UNUSED_VARIABLE = YES; 1427 | INFOPLIST_FILE = Tests/Info.plist; 1428 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1429 | MTL_ENABLE_DEBUG_INFO = YES; 1430 | PRODUCT_BUNDLE_IDENTIFIER = "Dratini-Tests"; 1431 | PRODUCT_NAME = "Dratini-Tests"; 1432 | SDKROOT = appletvos; 1433 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 1434 | SWIFT_VERSION = 3.0; 1435 | TVOS_DEPLOYMENT_TARGET = 10.2; 1436 | }; 1437 | name = Debug; 1438 | }; 1439 | 769999D01F3F2F3300EC035C /* Release */ = { 1440 | isa = XCBuildConfiguration; 1441 | buildSettings = { 1442 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 1443 | ALWAYS_SEARCH_USER_PATHS = NO; 1444 | CLANG_ANALYZER_NONNULL = YES; 1445 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 1446 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1447 | CLANG_CXX_LIBRARY = "libc++"; 1448 | CLANG_ENABLE_MODULES = YES; 1449 | CLANG_WARN_BOOL_CONVERSION = YES; 1450 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1451 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1452 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 1453 | CLANG_WARN_EMPTY_BODY = YES; 1454 | CLANG_WARN_ENUM_CONVERSION = YES; 1455 | CLANG_WARN_INFINITE_RECURSION = YES; 1456 | CLANG_WARN_INT_CONVERSION = YES; 1457 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1458 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 1459 | CLANG_WARN_UNREACHABLE_CODE = YES; 1460 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1461 | COPY_PHASE_STRIP = NO; 1462 | ENABLE_NS_ASSERTIONS = NO; 1463 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1464 | FRAMEWORK_SEARCH_PATHS = ( 1465 | "$(inherited)", 1466 | "$(PROJECT_DIR)/Carthage/Build/tvOS", 1467 | ); 1468 | GCC_C_LANGUAGE_STANDARD = gnu99; 1469 | GCC_NO_COMMON_BLOCKS = YES; 1470 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1471 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1472 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1473 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1474 | GCC_WARN_UNUSED_FUNCTION = YES; 1475 | GCC_WARN_UNUSED_VARIABLE = YES; 1476 | INFOPLIST_FILE = Tests/Info.plist; 1477 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1478 | MTL_ENABLE_DEBUG_INFO = NO; 1479 | PRODUCT_BUNDLE_IDENTIFIER = "Dratini-Tests"; 1480 | PRODUCT_NAME = "Dratini-Tests"; 1481 | SDKROOT = appletvos; 1482 | SWIFT_VERSION = 3.0; 1483 | TVOS_DEPLOYMENT_TARGET = 10.2; 1484 | VALIDATE_PRODUCT = YES; 1485 | }; 1486 | name = Release; 1487 | }; 1488 | OBJ_3 /* Debug */ = { 1489 | isa = XCBuildConfiguration; 1490 | buildSettings = { 1491 | CLANG_ENABLE_OBJC_ARC = YES; 1492 | COMBINE_HIDPI_IMAGES = YES; 1493 | COPY_PHASE_STRIP = NO; 1494 | DEBUG_INFORMATION_FORMAT = dwarf; 1495 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1496 | ENABLE_NS_ASSERTIONS = YES; 1497 | GCC_OPTIMIZATION_LEVEL = 0; 1498 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 1499 | MACOSX_DEPLOYMENT_TARGET = 10.10; 1500 | ONLY_ACTIVE_ARCH = YES; 1501 | OTHER_SWIFT_FLAGS = "-DXcode"; 1502 | PRODUCT_NAME = "$(TARGET_NAME)"; 1503 | SDKROOT = macosx; 1504 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 1505 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 1506 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 1507 | SWIFT_VERSION = 3.0; 1508 | USE_HEADERMAP = NO; 1509 | }; 1510 | name = Debug; 1511 | }; 1512 | OBJ_4 /* Release */ = { 1513 | isa = XCBuildConfiguration; 1514 | buildSettings = { 1515 | CLANG_ENABLE_OBJC_ARC = YES; 1516 | COMBINE_HIDPI_IMAGES = YES; 1517 | COPY_PHASE_STRIP = YES; 1518 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 1519 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1520 | GCC_OPTIMIZATION_LEVEL = s; 1521 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 1522 | MACOSX_DEPLOYMENT_TARGET = 10.10; 1523 | OTHER_SWIFT_FLAGS = "-DXcode"; 1524 | PRODUCT_NAME = "$(TARGET_NAME)"; 1525 | SDKROOT = macosx; 1526 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 1527 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 1528 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1529 | SWIFT_VERSION = 3.0; 1530 | USE_HEADERMAP = NO; 1531 | }; 1532 | name = Release; 1533 | }; 1534 | /* End XCBuildConfiguration section */ 1535 | 1536 | /* Begin XCConfigurationList section */ 1537 | 7699995A1F3F293A00EC035C /* Build configuration list for PBXNativeTarget "Dratini-iOS" */ = { 1538 | isa = XCConfigurationList; 1539 | buildConfigurations = ( 1540 | 7699995B1F3F293A00EC035C /* Debug */, 1541 | 7699995C1F3F293A00EC035C /* Release */, 1542 | ); 1543 | defaultConfigurationIsVisible = 0; 1544 | }; 1545 | 7699995D1F3F293A00EC035C /* Build configuration list for PBXNativeTarget "Dratini-iOSTests" */ = { 1546 | isa = XCConfigurationList; 1547 | buildConfigurations = ( 1548 | 7699995E1F3F293A00EC035C /* Debug */, 1549 | 7699995F1F3F293A00EC035C /* Release */, 1550 | ); 1551 | defaultConfigurationIsVisible = 0; 1552 | }; 1553 | 769999851F3F2D1100EC035C /* Build configuration list for PBXNativeTarget "Dratini-macOS" */ = { 1554 | isa = XCConfigurationList; 1555 | buildConfigurations = ( 1556 | 769999861F3F2D1100EC035C /* Debug */, 1557 | 769999871F3F2D1100EC035C /* Release */, 1558 | ); 1559 | defaultConfigurationIsVisible = 0; 1560 | }; 1561 | 769999881F3F2D1100EC035C /* Build configuration list for PBXNativeTarget "Dratini-macOSTests" */ = { 1562 | isa = XCConfigurationList; 1563 | buildConfigurations = ( 1564 | 769999891F3F2D1100EC035C /* Debug */, 1565 | 7699998A1F3F2D1100EC035C /* Release */, 1566 | ); 1567 | defaultConfigurationIsVisible = 0; 1568 | }; 1569 | 769999AA1F3F2EBB00EC035C /* Build configuration list for PBXNativeTarget "Dratini-watchOS" */ = { 1570 | isa = XCConfigurationList; 1571 | buildConfigurations = ( 1572 | 769999AB1F3F2EBB00EC035C /* Debug */, 1573 | 769999AC1F3F2EBB00EC035C /* Release */, 1574 | ); 1575 | defaultConfigurationIsVisible = 0; 1576 | }; 1577 | 769999CB1F3F2F3300EC035C /* Build configuration list for PBXNativeTarget "Dratini-tvOS" */ = { 1578 | isa = XCConfigurationList; 1579 | buildConfigurations = ( 1580 | 769999CC1F3F2F3300EC035C /* Debug */, 1581 | 769999CD1F3F2F3300EC035C /* Release */, 1582 | ); 1583 | defaultConfigurationIsVisible = 0; 1584 | }; 1585 | 769999CE1F3F2F3300EC035C /* Build configuration list for PBXNativeTarget "Dratini-tvOSTests" */ = { 1586 | isa = XCConfigurationList; 1587 | buildConfigurations = ( 1588 | 769999CF1F3F2F3300EC035C /* Debug */, 1589 | 769999D01F3F2F3300EC035C /* Release */, 1590 | ); 1591 | defaultConfigurationIsVisible = 0; 1592 | }; 1593 | OBJ_2 /* Build configuration list for PBXProject "Dratini" */ = { 1594 | isa = XCConfigurationList; 1595 | buildConfigurations = ( 1596 | OBJ_3 /* Debug */, 1597 | OBJ_4 /* Release */, 1598 | ); 1599 | defaultConfigurationIsVisible = 0; 1600 | defaultConfigurationName = Debug; 1601 | }; 1602 | /* End XCConfigurationList section */ 1603 | }; 1604 | rootObject = OBJ_1 /* Project object */; 1605 | } 1606 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/project.xcworkspace/xcshareddata/Dratini.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "0D32BF1893ED9A31A02C803033FC3BB411A29377", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "8B123162C394A0A0A138779108E4C59DD771865A" : 9223372036854775807, 8 | "0D32BF1893ED9A31A02C803033FC3BB411A29377" : 9223372036854775807, 9 | "8F8D523A113DCD3A9D56182E3C82A270279DADC6" : 9223372036854775807 10 | }, 11 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "B0FEB3A0-E98F-4A1E-A593-3E98C94C0A61", 12 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 13 | "8B123162C394A0A0A138779108E4C59DD771865A" : "Dratini\/RxSwift\/", 14 | "0D32BF1893ED9A31A02C803033FC3BB411A29377" : "Dratini\/", 15 | "8F8D523A113DCD3A9D56182E3C82A270279DADC6" : "Dratini\/.build\/checkouts\/Ditto.git--15477487491508354\/" 16 | }, 17 | "DVTSourceControlWorkspaceBlueprintNameKey" : "Dratini", 18 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 19 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Dratini.xcodeproj", 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 21 | { 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/kevin0571\/Dratini.git", 23 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 24 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "0D32BF1893ED9A31A02C803033FC3BB411A29377" 25 | }, 26 | { 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/ReactiveX\/RxSwift", 28 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 29 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8B123162C394A0A0A138779108E4C59DD771865A" 30 | }, 31 | { 32 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/kevin0571\/Ditto", 33 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 34 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8F8D523A113DCD3A9D56182E3C82A270279DADC6" 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /Dratini.xcodeproj/xcshareddata/xcschemes/Dratini-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/xcshareddata/xcschemes/Dratini-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/xcshareddata/xcschemes/Dratini-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/xcshareddata/xcschemes/Dratini-watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Dratini.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | Dratini.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Kevin Lin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.pins: -------------------------------------------------------------------------------- 1 | { 2 | "autoPin": true, 3 | "pins": [ 4 | { 5 | "package": "Ditto", 6 | "reason": null, 7 | "repositoryURL": "https://github.com/kevin0571/Ditto.git", 8 | "version": "1.0.4" 9 | } 10 | ], 11 | "version": 1 12 | } -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:3.1 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Dratini", 7 | dependencies: [ 8 | .Package(url: "https://github.com/kevin0571/Ditto.git", majorVersion: 1) 9 | ] 10 | ) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dratini ![CI Status](https://travis-ci.org/kevin0571/Dratini.svg?branch=master) [![codecov](https://codecov.io/gh/kevin0571/Dratini/branch/master/graph/badge.svg)](https://codecov.io/gh/kevin0571/Dratini) ![CocoaPods](http://img.shields.io/cocoapods/v/Dratini.svg?style=flag) ![Carthage](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg) ![Swift Pacakge Manager](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg) ![License](https://img.shields.io/cocoapods/l/Dratini.svg?style=flag) 2 | Dratini is a neat network abstraction layer. 3 | If you are looking for a solution to make your network layer neat, Dratini is your choice. 4 | Dratini uses protocols to define network request, parameters and response, which makes your network layer more readable and testable. 5 | 6 | ## Features 7 | - Protocol base design. 8 | - Auto serialization for parameters. 9 | - Response is observable by request id or response type. 10 | - UI non-blocking since request and response handling happen in background thread. 11 | - Request and response are interceptable by using delegate. 12 | - RxSwift extension is available: [RxDratini](https://github.com/kevin0571/RxDratini) 13 | 14 | ## Requirements 15 | - Xcode 8.0+ 16 | - Swift 3.0 17 | 18 | ## Dependencies 19 | - [Ditto](https://github.com/kevin0571/Ditto): it's used for serializing Swift object into JSON compatible dictionary, mainly used for impelmenting DefaultQueryString, URLEncodedBodyData and JSONBodyData. 20 | 21 | ## Usage 22 | 23 | **CocoaPods** 24 | ```ruby 25 | pod 'Dratini' 26 | ``` 27 | 28 | **Carthage** 29 | ```ruby 30 | github "kevin0571/Dratini" 31 | ``` 32 | 33 | **Swift Package Manager** 34 | ```ruby 35 | dependencies: [ 36 | .Package(url: "https://github.com/kevin0571/Dratini.git", majorVersion: 1) 37 | ] 38 | ``` 39 | 40 | ### Overview 41 | Here are some basic steps to send out a request and observe for its response. 42 | 43 | Setup RequestQueue: 44 | ```swift 45 | let requestQueue = RequestQueue(baseURL: URL(string: "http://example.com")) 46 | // Delegate and configuration are not required. 47 | // Set the delegate(RequestQueueDelegate) if you wish to get callbacks for each step. 48 | // RequestQueueConfiguration.default is used if configuration is not specified. 49 | ``` 50 | 51 | Keep a shared RequestQueue is recommended: 52 | ```swift 53 | extension RequestQueue { 54 | static let shared = RequestQueue(baseURL: URL(string: "http://example.com")) 55 | } 56 | ``` 57 | 58 | Describe your request, parameters and response: 59 | ```swift 60 | struct LogInRequest: Request { 61 | typealias ParametersType = LogInParameters 62 | typealias ResponseType = LogInResponse 63 | 64 | var parameters: LogInParameters 65 | 66 | func path() -> String { 67 | return "/login" 68 | } 69 | 70 | func method() -> HTTPMethod { 71 | return .post 72 | } 73 | } 74 | 75 | // There are several built-in Parameters types: 76 | // - DefaultQueryString for query string, it will mostly be used in GET request. 77 | // - URLEncodedBodyData for URL encoded body data. 78 | // - JSONBodyData for JSON format body data. 79 | // - MultipartFormData for multipart form data, it will mostly be used for uploading file. 80 | // 81 | // In order to allow you to keep the naming convention of different platforms, 82 | // property name of DefaultQueryString, URLEncodedBodyData and JSONBodyData will be mapped to other naming convention. 83 | // By default property will be converted to lowercase name separated by underscore, 84 | // e.g. accessToken will be converted to access_token. 85 | // You can set the mapping by overriding "serializableMapping" function. 86 | // See more details in Ditto project's README. 87 | struct LogInParameters: URLEncodedBodyData { 88 | let username: String 89 | let password: String 90 | } 91 | 92 | struct LogInResponse: Response { 93 | let username: String 94 | let name: String 95 | init?(data: ResponseData, response: URLResponse) { 96 | // - Use data.data to access raw response data. 97 | // - Use data.jsonObject to access JSON format dictionary. 98 | // - Use data.jsonArray to access JSON format array. 99 | // - Use data.string to access UTF8 string. 100 | guard let username = data.jsonObject["username"] as? String, 101 | let name = data.jsonObject["name"] as? String else { 102 | return nil 103 | } 104 | self.username = username 105 | self.name = name 106 | } 107 | } 108 | ``` 109 | 110 | Send the request and observe for response: 111 | ```swift 112 | let request = LogInRequest(parameters: LogInParameters(username: username, 113 | password: password)) 114 | let requestID = RequestQueue.shared.add(request) 115 | // Observe by using requestID. 116 | // The observer will be removed by RequestQueue after the request is finished. 117 | requestQueue.addObserver(for: requestID) { (result: Result) in 118 | guard let response = result.response else { 119 | // Show error message 120 | return 121 | } 122 | // Update UI by using response.username and response.name 123 | } 124 | // Observe a specific response type. 125 | // The observer is owned by an owner. The owner is held weakly by RequestQueue, 126 | // thus the observer will be removed if owner is released. 127 | requestQueue.addObserver(ownedBy: self) { [weak self] (result: Result) in 128 | // ... 129 | } 130 | // NOTE: observer callback is called in main thread. 131 | ``` 132 | 133 | ### Do More with Dratini 134 | Sometimes you need to do more with Dratini, here are some features you might need, e.g. upload file, intercept different states of request and response. 135 | 136 | Upload file: 137 | ```swift 138 | let data = MultipartFormData() 139 | // Append file with fileURL 140 | data.append(fileURL: fileURL, withName: name, fileName: fileName, mimeType: "application/x-plist") 141 | // Append raw file data 142 | data.append(data: fileData, withName: name, fileName: fileName, mimeType: "application/x-plist") 143 | 144 | // Assume we've created UploadFileRequest 145 | let request = UploadFileRequest(parameters: data) 146 | // Send out request 147 | // ... 148 | ``` 149 | 150 | Intercept states of request: 151 | ```swift 152 | // Conform to Request with RequestDelegate to get callbacks of different states. 153 | struct LogInRequest: Request, RequestDelegate { 154 | // ... 155 | 156 | func requestWillSend(_ urlRequest: inout URLRequest) { 157 | // Called before request is sent out. 158 | // You are able to modify the URLRequest: update HTTP header for example. 159 | } 160 | 161 | func requestDidSend(_ urlRequest: URLRequest) { 162 | // Called after request is sent out. 163 | } 164 | 165 | func request(_ urlRequest: URLRequest, didFailWith error: DRError) { 166 | // Called when request is failed to be sent out or response is failed to be created. 167 | } 168 | } 169 | ``` 170 | 171 | Validate response before creating response and intercept states of response: 172 | ```swift 173 | struct LogInResponse: Response, ResponseDelegate { 174 | // ... 175 | 176 | // Validate the response before it's created. 177 | static func validate(_ response: URLResponse) -> Bool { 178 | guard let httpResponse = response as? HTTPURLResponse else { 179 | return true 180 | } 181 | return httpResponse.statusCode >= 200 && 182 | httpResponse.statusCode < 300 && 183 | httpResponse.allHeaderFields["Token"] != nil 184 | } 185 | 186 | // Called after response is created. 187 | func responseDidReceive(_ response: URLResponse) { 188 | guard let httpResponse = response as? HTTPURLResponse, 189 | let token = httpResponse.allHeaderFields["Token"] else { 190 | return nil 191 | } 192 | // Save your token 193 | } 194 | } 195 | ``` 196 | 197 | Having common logic for all requests and response are sometimes necessary, RequestQueueDelegate is here for you: 198 | ```swift 199 | class MyRequestQueueDelegate: RequestQueueDelegate { 200 | public func requestQueue(_ requestQueue: RequestQueue, willSend request: inout URLRequest) { 201 | // Called before each request is sent out. 202 | } 203 | 204 | public func requestQueue(_ requestQueue: RequestQueue, didSend request: URLRequest) { 205 | // Called after each request is sent out. 206 | } 207 | 208 | public func requestQueue(_ requestQueue: RequestQueue, didFailWith request: URLRequest, error: DRError) { 209 | // Called when request is failed to be sent out or response is failed to be created. 210 | } 211 | 212 | public func requestQueue(_ requestQueue: RequestQueue, didReceive response: URLResponse) { 213 | // Called after response is created. 214 | } 215 | } 216 | 217 | extension RequestQueue { 218 | // Set delegate when creating RequestQueue. 219 | static let shared = RequestQueue(delegate: MyRequestQueueDelegate(), baseURL: URL(string: "http://example.com")!) 220 | } 221 | ``` 222 | 223 | Check if request is finished and cancel it: 224 | ```swift 225 | let isFinished = RequestQueue.shared.isFinished(requestID) 226 | RequestQueue.shared.cancel(requestID) 227 | ``` 228 | 229 | ### Helpers 230 | 231 | When you don't really need a Parameters or Response, you can use: 232 | ```swift 233 | EmptyParameters 234 | EmptyResponse 235 | ``` 236 | 237 | ### Customization 238 | If you wish to customize query string or body data encoding, you can implement your own by adpoting QueryString or BodyData protocol. 239 | ```swift 240 | struct MyBodyData: BodyData { 241 | let string: String 242 | 243 | var contentType: String { 244 | return "my-content-type" 245 | } 246 | 247 | func encode() throws -> Data { 248 | return string.data(using: .utf8)! 249 | } 250 | } 251 | ``` -------------------------------------------------------------------------------- /Sources/DRError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DRError.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 10/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Dratini error type. 12 | /// - invalidParameters(String): something went wrong during parameters encoding. 13 | /// - invalidResponse(Error): error thrown by URLSession. 14 | /// - responseValidationFailed(URLResponse): validation failed before serializing response. 15 | /// - responseSerializationFailed: nil is returned by Response constructor. 16 | /// - unknown: everything else unexpected. 17 | public enum DRError: Error { 18 | case invalidParameters(String) 19 | case invalidResponse(Error) 20 | case responseValidationFailed(Data, URLResponse) 21 | case responseSerializationFailed 22 | case unknown 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1.2 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Parameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parameters.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 10/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Ditto 11 | 12 | /// This protocol is only used internally to eliminate the difference between QueryString and BodyData. 13 | /// So don't adopt this protocol. 14 | public protocol Parameters {} 15 | 16 | /// Implement your custom query string encoding by conforming to QueryString protocol. 17 | /// In most cases you should just use the default implementation DefaultQueryString. 18 | public protocol QueryString: Parameters { 19 | func encode() throws -> String 20 | } 21 | 22 | public protocol DefaultQueryString: QueryString, Serializable {} 23 | 24 | /// Default query string implementation. 25 | /// Since there is no specification for array and dictionary serialization, 26 | /// DefaultQueryString is following a generally accepted solution for array and dictionary serialization. 27 | /// If a different behavior of array and dictionary serialization is expected, 28 | /// you should implement your own query string serialization by adopting QueryString protocol. 29 | /// 30 | /// Array parameter - key: [1,2,3] will be converted to: 31 | /// key[]=1&key[]=2&key[]=3 32 | /// 33 | /// Dictionary parameter - key: { "nested1": 1, "nested2": 2 } will be converted to: 34 | /// key[nested1]=1&key[nested2]=2 35 | public extension DefaultQueryString { 36 | func encode() throws -> String { 37 | let jsonObject: JSONObject = serialize() 38 | var pairs = [String]() 39 | for case (let key, let value?) in jsonObject { 40 | pairs.append(contentsOf: convert(key: key, value: value, escaped: false)) 41 | } 42 | return pairs.joined(separator: "&") 43 | } 44 | 45 | func serializableMapping() -> Mapping { 46 | return AutoMapping.mapping(for: self, style: .lowercaseSeparatedByUnderscore) 47 | } 48 | } 49 | 50 | /// Basic body data encoding implementations are provided: 51 | /// URLEncodedBodyData, JSONBodyData and MultipartFormData. 52 | /// If you wish to have other form of body data encoding, 53 | /// you should implement your own by conforming to BodyData protocol. 54 | public protocol BodyData: Parameters { 55 | var contentType: String { get } 56 | func encode() throws -> Data 57 | } 58 | 59 | public protocol URLEncodedBodyData: BodyData, Serializable {} 60 | 61 | /// See DefaultQueryString for array and dictionary encoding rules. 62 | public extension URLEncodedBodyData { 63 | var contentType: String { 64 | return "application/x-www-form-urlencoded; charset=utf-8" 65 | } 66 | 67 | func encode() throws -> Data { 68 | let jsonObject: JSONObject = serialize() 69 | var pairs = [String]() 70 | for case (let key, let value?) in jsonObject { 71 | pairs.append(contentsOf: convert(key: key, value: value, escaped: true)) 72 | } 73 | return pairs.joined(separator: "&").data(using: .utf8) ?? Data() 74 | } 75 | 76 | func serializableMapping() -> Mapping { 77 | return AutoMapping.mapping(for: self, style: .lowercaseSeparatedByUnderscore) 78 | } 79 | } 80 | 81 | public protocol JSONBodyData: URLEncodedBodyData, Serializable {} 82 | 83 | /// JSON object body data. 84 | public extension JSONBodyData { 85 | var contentType: String { 86 | return "application/json" 87 | } 88 | 89 | func encode() throws -> Data { 90 | guard let data: Data = serialize() else { 91 | throw DRError.invalidParameters("Invalid JSON body") 92 | } 93 | return data 94 | } 95 | 96 | func serializableMapping() -> Mapping { 97 | return AutoMapping.mapping(for: self, style: .lowercaseSeparatedByUnderscore) 98 | } 99 | } 100 | 101 | private protocol MultipartEntry { 102 | var name: String { get } 103 | var fileName: String? { get } 104 | var mimeType: String? { get } 105 | func encode() throws -> Data 106 | } 107 | 108 | private struct MultipartFileEntry: MultipartEntry { 109 | private let fileURL: URL 110 | let name: String 111 | let fileName: String? 112 | let mimeType: String? 113 | 114 | init(fileURL: URL, name: String, fileName: String?, mimeType: String?) { 115 | self.fileURL = fileURL 116 | self.name = name 117 | self.fileName = fileName 118 | self.mimeType = mimeType 119 | } 120 | 121 | func encode() throws -> Data { 122 | guard fileURL.isFileURL else { 123 | throw DRError.invalidParameters("Invalid file URL") 124 | } 125 | guard let reachable = try? fileURL.checkPromisedItemIsReachable(), reachable else { 126 | throw DRError.invalidParameters("File URL is not reachable") 127 | } 128 | 129 | var isDirectory: ObjCBool = false 130 | guard FileManager.default.fileExists(atPath: fileURL.path, isDirectory: &isDirectory) && !isDirectory.boolValue else { 131 | throw DRError.invalidParameters("File is directory") 132 | } 133 | 134 | guard let data = try? Data(contentsOf: fileURL) else { 135 | throw DRError.invalidParameters("Unable to read from file URL") 136 | } 137 | return data 138 | } 139 | } 140 | 141 | private struct MultipartDataEntry: MultipartEntry { 142 | private let data: Data 143 | let name: String 144 | let fileName: String? 145 | let mimeType: String? 146 | 147 | init(data: Data, name: String, fileName: String?, mimeType: String?) { 148 | self.data = data 149 | self.name = name 150 | self.fileName = fileName 151 | self.mimeType = mimeType 152 | } 153 | 154 | func encode() throws -> Data { 155 | return self.data 156 | } 157 | } 158 | 159 | private struct MultipartKeyValueEntry: MultipartEntry { 160 | private let value: Any 161 | let name: String 162 | let fileName: String? 163 | let mimeType: String? 164 | 165 | init(value: Any, name: String) { 166 | self.value = value 167 | self.name = name 168 | self.fileName = nil 169 | self.mimeType = nil 170 | } 171 | 172 | func encode() throws -> Data { 173 | guard let data = "\(value)".data(using: .utf8) else { 174 | throw DRError.invalidParameters("Invalid multipart key-value entry") 175 | } 176 | return data 177 | } 178 | } 179 | 180 | /// Multipart body data. 181 | /// Raw data, fileURL and key value entry can be added. 182 | /// Raw data: data will be simply appended to body data. 183 | /// File URL: will try to access and read data from the file, 184 | /// DRError.invalidParameters will be thrown if file is not accessible. 185 | open class MultipartFormData: BodyData { 186 | private let boundary = "dratini.boundary.\(UUID().uuidString).\(arc4random())" 187 | private let crlf = "\r\n" 188 | private var entries = [MultipartEntry]() 189 | 190 | public init() {} 191 | 192 | public var contentType: String { 193 | return "multipart/form-data; boundary=\(boundary)" 194 | } 195 | 196 | public func encode() throws -> Data { 197 | var data = Data() 198 | let boundaryData = "--\(boundary)\(crlf)".data(using: .utf8)! 199 | let crlfData = crlf.data(using: .utf8)! 200 | for entry in entries { 201 | data.append(boundaryData) 202 | 203 | var disposition = "Content-Disposition: form-data; name=\"\(entry.name)\"" 204 | if let fileName = entry.fileName { 205 | disposition += "; filename=\"\(fileName)\"" 206 | } 207 | data.append(disposition.data(using: .utf8)!) 208 | data.append(crlfData) 209 | 210 | if let mimeType = entry.mimeType { 211 | let contentType = "Content-Type: \(mimeType)" 212 | data.append(contentType.data(using: .utf8)!) 213 | data.append(crlfData) 214 | } 215 | 216 | data.append(crlfData) 217 | data.append(try entry.encode()) 218 | data.append(crlfData) 219 | } 220 | data.append("--\(boundary)--\(crlf)".data(using: .utf8)!) 221 | return data 222 | } 223 | 224 | public func append(fileURL: URL, withName name: String) { 225 | append(fileURL: fileURL, withName: name, fileName: name, mimeType: nil) 226 | } 227 | 228 | public func append(fileURL: URL, withName name: String, fileName: String?, mimeType: String?) { 229 | entries.append(MultipartFileEntry(fileURL: fileURL, 230 | name: name, 231 | fileName: fileName, 232 | mimeType: mimeType)) 233 | } 234 | 235 | public func append(data: Data, withName name: String) { 236 | append(data: data, withName: name, fileName: name, mimeType: nil) 237 | } 238 | 239 | public func append(data: Data, withName name: String, fileName: String?, mimeType: String?) { 240 | entries.append(MultipartDataEntry(data: data, 241 | name: name, 242 | fileName: fileName, 243 | mimeType: mimeType)) 244 | } 245 | 246 | public func append(value: Any, withName name: String) { 247 | entries.append(MultipartKeyValueEntry(value: value, name: name)) 248 | } 249 | } 250 | 251 | /// Helper struct which could be used when parameters is not required. 252 | public struct EmptyParameters: Parameters { 253 | public init() {} 254 | } 255 | 256 | // MARK: Helpers 257 | 258 | // From Alamofire ParameterEncoding.swift 259 | private let urlQueryAllowedCharacterSet: CharacterSet = { 260 | let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 261 | let subDelimitersToEncode = "!$&'()*+,;=" 262 | 263 | var allowedCharacterSet = CharacterSet.urlQueryAllowed 264 | allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") 265 | return allowedCharacterSet 266 | }() 267 | 268 | private func escape(_ string: String) -> String { 269 | return string.addingPercentEncoding(withAllowedCharacters: urlQueryAllowedCharacterSet) ?? "" 270 | } 271 | 272 | // Convert to query string 273 | private func convert(key: String, value: Any, escaped: Bool) -> [String] { 274 | if let array = value as? JSONArray { 275 | var pairs = [String]() 276 | for case let item? in array { 277 | pairs.append(contentsOf: convert(key: key.appending("[]"), value: item, escaped: escaped)) 278 | } 279 | return pairs 280 | } else if let object = value as? JSONObject { 281 | var pairs = [String]() 282 | for case (let objectKey, let objectValue?) in object { 283 | pairs.append(contentsOf: convert(key: key.appending("[\(objectKey)]"), value: objectValue, escaped: escaped)) 284 | } 285 | return pairs 286 | } else { 287 | if escaped { 288 | return ["\(escape(key))=\(escape("\(value)"))"] 289 | } else { 290 | return ["\(key)=\(value)"] 291 | } 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /Sources/Request.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Request.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 1/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum HTTPMethod: String { 12 | case options, get, head, post, put, patch, delete, trace, connect 13 | } 14 | 15 | /// Protocol that all requests should conform to. 16 | /// Specifying ParametersType and ResponseType are required. 17 | /// Checkout Parameters.swift and Response.swift for more details. 18 | /// If there is no parameters required, please set ParametersType to EmptyParameters. 19 | /// The same for response, EmptyResponse will be used if response isn't needed. 20 | public protocol Request { 21 | /// Default implementations for query string and body data are provided. 22 | /// DefaultQueryString is the default implementation for query string. 23 | /// URLEncodedBodyData is the implementation for URL encoded body data. 24 | /// JSONBodyData is the implementation for JSON body data. 25 | /// MultipartFormData is the implementation for multipart form data. 26 | associatedtype ParametersType: Parameters 27 | /// The response type which will be passed into the observer callback. 28 | associatedtype ResponseType: Response 29 | 30 | var parameters: ParametersType { get set } 31 | 32 | func path() -> String 33 | func method() -> HTTPMethod 34 | func responseType() -> ResponseType.Type 35 | } 36 | 37 | public extension Request { 38 | func responseType() -> ResponseType.Type { 39 | return ResponseType.self 40 | } 41 | } 42 | 43 | /// Delegate that could be used with Request if you wish to get callback: 44 | /// - before request is sent 45 | /// - after request is sent 46 | /// - when request is failed 47 | /// NOTE: RequestDelegate will be called after RequestQueueDelegate. 48 | public protocol RequestDelegate { 49 | /// It's called before the actual URLRequest is sent out. 50 | /// Giving a chance to overwrite attributes of URLRequest. 51 | func requestWillSend(_ urlRequest: inout URLRequest) 52 | 53 | /// It's called right after URLRequest is sent out. 54 | func requestDidSend(_ urlRequest: URLRequest) 55 | 56 | /// It's called when request is failed. 57 | /// Any failure before response is generated will fall into this function. 58 | /// See DRError for error types. 59 | func request(_ urlRequest: URLRequest, didFailWith error: DRError) 60 | } 61 | -------------------------------------------------------------------------------- /Sources/RequestConverter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestConverter.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 3/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct RequestConverter { 12 | private init() {} 13 | 14 | static func convert(_ request: T, 15 | withBaseURL baseURL: URL, 16 | cachePolicy: URLRequest.CachePolicy, 17 | timeoutInterval: TimeInterval) throws -> URLRequest { 18 | var url = baseURL 19 | url.appendPathComponent(request.path()) 20 | if let queryString = request.parameters as? QueryString, 21 | var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) { 22 | urlComponents.query = try queryString.encode() 23 | url = urlComponents.url ?? url 24 | } 25 | 26 | var urlRequest = URLRequest(url: url, cachePolicy: cachePolicy, timeoutInterval: timeoutInterval) 27 | urlRequest.httpMethod = request.method().rawValue.uppercased() 28 | 29 | switch request.method() { 30 | case .options, .post, .put, .patch, .trace, .connect: 31 | guard let bodyData = request.parameters as? BodyData else { 32 | break 33 | } 34 | urlRequest.httpBody = try bodyData.encode() 35 | urlRequest.setValue(bodyData.contentType, forHTTPHeaderField: "Content-Type") 36 | default: 37 | break 38 | } 39 | 40 | return urlRequest 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/RequestQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestQueue.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 3/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias RequestID = UInt64 12 | 13 | /// Configuration for requst queue. 14 | /// RequestQueueConfiguration.default is provied with: 15 | /// - sessionConfiguration = URLSessionConfiguration.default 16 | /// - cachePolicy = .useProtocolCachePolicy 17 | /// - timeoutInterval = 60 18 | public struct RequestQueueConfiguration { 19 | public let sessionConfiguration: URLSessionConfiguration 20 | public let cachePolicy: URLRequest.CachePolicy 21 | public let timeoutInterval: TimeInterval 22 | static var `default`: RequestQueueConfiguration { 23 | return RequestQueueConfiguration(sessionConfiguration: URLSessionConfiguration.default, 24 | cachePolicy: .useProtocolCachePolicy, 25 | timeoutInterval: 60) 26 | } 27 | } 28 | 29 | /// Result for representing 'success' or 'failure'. 30 | /// - success(T) is used when response is successfully received and serialized. 31 | /// - failure(DRError) is used when any DRError is thrown. 32 | public enum Result { 33 | case success(T) 34 | case failure(DRError) 35 | } 36 | 37 | /// Helper functions for Result 38 | public extension Result { 39 | public var isSuccess: Bool { 40 | switch self { 41 | case .success: 42 | return true 43 | case .failure: 44 | return false 45 | } 46 | } 47 | 48 | public var isFailure: Bool { 49 | return !isSuccess 50 | } 51 | 52 | public var response: T? { 53 | switch self { 54 | case .success(let response): 55 | return response 56 | case .failure: 57 | return nil 58 | } 59 | } 60 | 61 | public var error: DRError? { 62 | switch self { 63 | case .success: 64 | return nil 65 | case .failure(let error): 66 | return error 67 | } 68 | } 69 | } 70 | 71 | /// Delegate that could be used for request queue if you wish to get callback: 72 | /// - before each request is sent 73 | /// - after each request is sent 74 | /// - when each request is failed 75 | /// - when response is received 76 | /// NOTE: RequestQueueDelegate will be called before RequestDelegate and ResponseDelegate. 77 | public protocol RequestQueueDelegate { 78 | func requestQueue(_ requestQueue: RequestQueue, willSend request: inout URLRequest) 79 | func requestQueue(_ requestQueue: RequestQueue, didSend request: URLRequest) 80 | func requestQueue(_ requestQueue: RequestQueue, didFailWith request: URLRequest, error: DRError) 81 | func requestQueue(_ requestQueue: RequestQueue, didReceive response: URLResponse) 82 | } 83 | 84 | private let dispatchQueue = DispatchQueue(label: String(describing: RequestQueue.self), 85 | qos: .userInteractive, 86 | attributes: .concurrent) 87 | 88 | /// RequestQueue maintains requests and observers. 89 | /// Request can be added, removed and tracked in queue. 90 | /// Observer is used to observe the corresponding response type of a request. 91 | public class RequestQueue { 92 | // Type-erased observer block 93 | fileprivate struct AnyObserverBlock { 94 | private let base: Any 95 | private(set) weak var owner: AnyObject? 96 | 97 | init(_ base: @escaping (Result) -> Void, owner: AnyObject?) { 98 | self.base = base 99 | self.owner = owner 100 | } 101 | 102 | func call(with result: Result) { 103 | guard let block = base as? (Result) -> Void else { 104 | return 105 | } 106 | block(result) 107 | } 108 | } 109 | 110 | fileprivate enum ObserverKey: Hashable { 111 | case responseType(String) 112 | case requestID(RequestID) 113 | 114 | var hashValue: Int { 115 | switch self { 116 | case .responseType(let responseType): 117 | return responseType.hashValue 118 | case .requestID(let requestID): 119 | return requestID.hashValue 120 | } 121 | } 122 | 123 | static func ==(lhs: ObserverKey, rhs: ObserverKey) -> Bool { 124 | if case .responseType(let responseType1) = lhs, case .responseType(let responseType2) = rhs { 125 | return responseType1 == responseType2 126 | } else if case .requestID(let requestID1) = lhs, case .requestID(let requestID2) = rhs { 127 | return requestID1 == requestID2 128 | } else { 129 | return false 130 | } 131 | } 132 | } 133 | 134 | public let configuration: RequestQueueConfiguration 135 | public let baseURL: URL 136 | fileprivate let delegate: RequestQueueDelegate? 137 | fileprivate let session: URLSession 138 | fileprivate var observers = [ObserverKey: [AnyObserverBlock]]() 139 | fileprivate let observersLock = NSLock() 140 | fileprivate var tasks = [RequestID: URLSessionTask]() 141 | fileprivate let tasksLock = NSLock() 142 | 143 | public init(configuration: RequestQueueConfiguration = RequestQueueConfiguration.default, 144 | delegate: RequestQueueDelegate? = nil, 145 | baseURL: URL) { 146 | self.configuration = configuration 147 | self.delegate = delegate 148 | self.baseURL = baseURL 149 | session = URLSession(configuration: configuration.sessionConfiguration) 150 | } 151 | } 152 | 153 | // MARK: Request ID 154 | 155 | extension RequestQueue { 156 | private static var requestID: RequestID = 0 157 | private static let requestIDLock = NSLock() 158 | 159 | fileprivate func generateRequestID() -> RequestID { 160 | RequestQueue.requestIDLock.lock() 161 | defer { 162 | RequestQueue.requestIDLock.unlock() 163 | } 164 | RequestQueue.requestID += 1 165 | return RequestQueue.requestID 166 | } 167 | } 168 | 169 | // MARK: Add request to & remove request from queue 170 | 171 | extension RequestQueue { 172 | 173 | /// Add a request to queue. 174 | /// Requests will be sent out in the order they are added. 175 | /// RequestID will be returned for the usage of adding observer, 176 | /// removing request from queue and tracking finish stauts. 177 | /// RequestID is unique in all request queues. 178 | /// 179 | /// - parameter request: Request implemenation 180 | /// 181 | /// - returns: RequestID which is unique in all request queues. 182 | @discardableResult 183 | public func add(_ request: T) -> RequestID { 184 | let requestID = generateRequestID() 185 | dispatchQueue.async { 186 | let responseType = request.responseType() 187 | var urlRequest: URLRequest 188 | do { 189 | urlRequest = try RequestConverter.convert(request, 190 | withBaseURL: self.baseURL, 191 | cachePolicy: self.configuration.cachePolicy, 192 | timeoutInterval: self.configuration.timeoutInterval) 193 | } catch { 194 | guard let error = (error as? DRError), case .invalidParameters = error else { 195 | self.notify(for: requestID, responseType, with: .failure(.invalidParameters("Unknown reason"))) 196 | return 197 | } 198 | self.notify(for: requestID, responseType, with: .failure(error)) 199 | return 200 | } 201 | 202 | self.delegate?.requestQueue(self, willSend: &urlRequest) 203 | if let requestDelegate = request as? RequestDelegate { 204 | requestDelegate.requestWillSend(&urlRequest) 205 | } 206 | 207 | let dataTask = self.session.dataTask(with: urlRequest) { (data, urlResponse, error) in 208 | self.tasksLock.lock() 209 | self.tasks.removeValue(forKey: requestID) 210 | self.tasksLock.unlock() 211 | do { 212 | guard error == nil else { 213 | throw DRError.invalidResponse(error!) 214 | } 215 | guard let data = data, let urlResponse = urlResponse else { 216 | throw DRError.unknown 217 | } 218 | guard responseType.validate(urlResponse) else { 219 | throw DRError.responseValidationFailed(data, urlResponse) 220 | } 221 | guard let response = responseType.init(data: ResponseData(data), response: urlResponse) else { 222 | throw DRError.responseSerializationFailed 223 | } 224 | 225 | self.delegate?.requestQueue(self, didReceive: urlResponse) 226 | if let responseDelegate = response as? ResponseDelegate { 227 | responseDelegate.responseDidReceive(urlResponse) 228 | } 229 | self.notify(for: requestID, request.responseType(), with: .success(response)) 230 | } catch { 231 | guard let error = error as? DRError else { 232 | self.delegate?.requestQueue(self, didFailWith: urlRequest, error: .unknown) 233 | if let requestDelegate = request as? RequestDelegate { 234 | requestDelegate.request(urlRequest, didFailWith: .unknown) 235 | } 236 | self.notify(for: requestID, responseType, with: Result.failure(.unknown)) 237 | return 238 | } 239 | self.delegate?.requestQueue(self, didFailWith: urlRequest, error: error) 240 | if let requestDelegate = request as? RequestDelegate { 241 | requestDelegate.request(urlRequest, didFailWith: error) 242 | } 243 | self.notify(for: requestID, responseType, with: Result.failure(error)) 244 | } 245 | } 246 | dataTask.resume() 247 | 248 | self.tasksLock.lock() 249 | self.tasks[requestID] = dataTask 250 | self.tasksLock.unlock() 251 | 252 | self.delegate?.requestQueue(self, didSend: urlRequest) 253 | if let requestDelegate = request as? RequestDelegate { 254 | requestDelegate.requestDidSend(urlRequest) 255 | } 256 | } 257 | return requestID 258 | } 259 | 260 | /// Cancel request from queue according to the given RequestID. 261 | /// 262 | /// - parameter requestID: RequestID returned by "add" function. 263 | public func cancel(_ requestID: RequestID) { 264 | dispatchQueue.async(flags: .barrier) { 265 | self.tasksLock.lock() 266 | if let task = self.tasks.removeValue(forKey: requestID) { 267 | task.cancel() 268 | } 269 | self.tasksLock.unlock() 270 | 271 | self.observersLock.lock() 272 | self.observers.removeValue(forKey: .requestID(requestID)) 273 | self.observersLock.unlock() 274 | } 275 | } 276 | 277 | /// Check if a request is finished. 278 | /// A request is considered as finished: 279 | /// - Result.success or Result.failure is returned in observer. 280 | /// - request is removed from queue. 281 | /// 282 | /// - parameter requestID: RequestID returned by "add" function. 283 | public func isFinished(_ requestID: RequestID) -> Bool { 284 | var isFinished = false 285 | dispatchQueue.sync(flags: .barrier) { 286 | tasksLock.lock() 287 | defer { 288 | tasksLock.unlock() 289 | } 290 | isFinished = tasks[requestID] == nil 291 | } 292 | return isFinished 293 | } 294 | 295 | private func notify(for requestID: RequestID, _ responseType: T.Type, with result: Result) { 296 | let responseTypeKey = ObserverKey.responseType(String(describing: responseType)) 297 | let requestIDKey = ObserverKey.requestID(requestID) 298 | 299 | self.observersLock.lock() 300 | // Only notify observers with owner 301 | let blocksForResponseType = (self.observers[responseTypeKey] ?? []).filter { $0.owner != nil } 302 | let blocksForRequestID = self.observers[requestIDKey] ?? [] 303 | self.observers[responseTypeKey] = blocksForResponseType 304 | self.observers.removeValue(forKey: requestIDKey) 305 | self.observersLock.unlock() 306 | 307 | let blocks = blocksForResponseType + blocksForRequestID 308 | 309 | DispatchQueue.main.async { 310 | for block in blocks { 311 | block.call(with: result) 312 | } 313 | } 314 | } 315 | } 316 | 317 | // MARK: Observer 318 | 319 | extension RequestQueue { 320 | 321 | /// Add observer for a specific response type. 322 | /// The observer will not be notified if it's owner is released. 323 | /// NOTE: block will be called in main thread. 324 | /// 325 | /// - parameter owner: owner that owns the observer. It will be weakly held in the queue. 326 | /// - parameter block: observer block which will be called when response or error is received. 327 | public func addObserver(ownedBy owner: AnyObject, using block: @escaping (Result) -> Void) { 328 | observersLock.lock() 329 | defer { 330 | observersLock.unlock() 331 | } 332 | 333 | let key = ObserverKey.responseType(String(describing: T.self)) 334 | if var blocks = observers[key] { 335 | blocks.append(AnyObserverBlock(block, owner: owner)) 336 | observers[key] = blocks 337 | } else { 338 | observers[key] = [AnyObserverBlock(block, owner: owner)] 339 | } 340 | } 341 | 342 | /// Add observer for a specific requestID. 343 | /// The observer will be removed from the queue when the request is finished or removed. 344 | /// NOTE: block will be called in main thread. 345 | /// 346 | /// - parameter requestID: RequestID returned by "add" function. 347 | /// - parameter block: observer block which will be called when response or error is received. 348 | public func addObserver(for requestID: RequestID, using block: @escaping (Result) -> Void) { 349 | observersLock.lock() 350 | defer { 351 | observersLock.unlock() 352 | } 353 | 354 | let key = ObserverKey.requestID(requestID) 355 | if var blocks = observers[key] { 356 | blocks.append(AnyObserverBlock(block, owner: nil)) 357 | observers[key] = blocks 358 | } else { 359 | observers[key] = [AnyObserverBlock(block, owner: nil)] 360 | } 361 | } 362 | 363 | /// Remove observers for a specific response type which are owned by "owner". 364 | /// NOTE: block will be called in main thread. 365 | /// 366 | /// - parameter type: response type 367 | /// - parameter owner: owner that owns observers. 368 | public func removeObservers(forType type: T.Type, ownedBy owner: AnyObject) { 369 | observersLock.lock() 370 | defer { 371 | observersLock.unlock() 372 | } 373 | 374 | let key = ObserverKey.responseType(String(describing: type)) 375 | guard let blocks = observers[key] else { 376 | return 377 | } 378 | observers[key] = blocks.filter { $0.owner !== owner } 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /Sources/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 2/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Wrapper of raw response data. 12 | /// It also provides handy functions for accessing the data in string, JSON object or JSON array. 13 | open class ResponseData { 14 | public let data: Data 15 | public lazy var string: String = String(data: self.data, encoding: .utf8) ?? "" 16 | public lazy var jsonObject: [String: Any] = ((try? JSONSerialization.jsonObject(with: self.data, options: [])) as? [String: Any]) ?? [String: Any]() 17 | public lazy var jsonArray: [[String: Any]] = ((try? JSONSerialization.jsonObject(with: self.data, options: [])) as? [[String: Any]]) ?? [[String: Any]]() 18 | 19 | init(_ data: Data) { 20 | self.data = data 21 | } 22 | } 23 | 24 | /// A protocol that all responses should conform to. 25 | public protocol Response { 26 | init?(data: ResponseData, response: URLResponse) 27 | 28 | /// Validation before response is being created. 29 | /// Default implementation is provided to accept responses with status code between 200 and 299. 30 | static func validate(_ response: URLResponse) -> Bool 31 | } 32 | 33 | public extension Response { 34 | static func validate(_ response: URLResponse) -> Bool { 35 | if let httpResponse = response as? HTTPURLResponse { 36 | return httpResponse.statusCode >= 200 && httpResponse.statusCode < 300 37 | } else { 38 | return true 39 | } 40 | } 41 | } 42 | 43 | /// Helper struct which could be used when response is not required. 44 | public struct EmptyResponse: Response { 45 | public init?(data: ResponseData, response: URLResponse) {} 46 | } 47 | 48 | /// Delegate that could be used with Response if you wish to get callback: 49 | /// - when response is received 50 | /// NOTE: ResponseDelegate will be called after RequestQueueDelegate 51 | public protocol ResponseDelegate { 52 | func responseDidReceive(_ response: URLResponse) 53 | } 54 | -------------------------------------------------------------------------------- /Tests/DratiniTests/ParametersTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParametersTests.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 11/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Dratini 11 | 12 | private struct TestDefaultQueryString: DefaultQueryString { 13 | let stringValue = "string" 14 | let intValue = 10 15 | let floatValue = 10.1 16 | let arrayValue = [1,2,3,4,5] 17 | let dictionaryValue = ["key_1": "value_1"] 18 | } 19 | 20 | private struct TestURLEncodedBodyData: URLEncodedBodyData { 21 | let stringValue = "string" 22 | let intValue = 10 23 | let floatValue = 10.1 24 | let arrayValue = [1,2,3,4,5] 25 | let dictionaryValue = ["key_1": "value_1"] 26 | } 27 | 28 | private struct TestJSONBodyData: JSONBodyData { 29 | let stringValue = "string" 30 | let intValue = 10 31 | let floatValue = 10.1 32 | let arrayValue = [1,2,3,4,5] 33 | let dictionaryValue = ["key_1": "value_1"] 34 | } 35 | 36 | class ParametersTests: XCTestCase { 37 | func testDefaultQueryString() { 38 | let testDefaultQueryString = TestDefaultQueryString() 39 | guard let queryString = try? testDefaultQueryString.encode() else { 40 | XCTFail("Encode failed") 41 | return 42 | } 43 | XCTAssert(queryString == "int_value=10&float_value=10.1&array_value[]=1&array_value[]=2&array_value[]=3&array_value[]=4&array_value[]=5&string_value=string&dictionary_value[key_1]=value_1") 44 | } 45 | 46 | func testURLEncodedBodyData() { 47 | let testURLEncodedBodyData = TestURLEncodedBodyData() 48 | guard let bodyData = try? testURLEncodedBodyData.encode() else { 49 | XCTFail("Encode failed") 50 | return 51 | } 52 | XCTAssert(bodyData == "int_value=10&float_value=10.1&array_value%5B%5D=1&array_value%5B%5D=2&array_value%5B%5D=3&array_value%5B%5D=4&array_value%5B%5D=5&string_value=string&dictionary_value%5Bkey_1%5D=value_1".data(using: .utf8)!) 53 | } 54 | 55 | func testJSONBodyData() { 56 | let testJSONBodyData = TestJSONBodyData() 57 | guard let bodyData = try? testJSONBodyData.encode() else { 58 | XCTFail("Encode failed") 59 | return 60 | } 61 | XCTAssert(bodyData == "{\"int_value\":10,\"float_value\":10.1,\"array_value\":[1,2,3,4,5],\"string_value\":\"string\",\"dictionary_value\":{\"key_1\":\"value_1\"}}".data(using: .utf8)!) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tests/DratiniTests/RequestConverterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestConverterTests.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 13/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Dratini 11 | 12 | private struct TestQueryString: DefaultQueryString { 13 | let string = "string" 14 | let int = 1 15 | } 16 | 17 | private struct TestBodyData: URLEncodedBodyData { 18 | let string = "string" 19 | let int = 1 20 | } 21 | 22 | private struct TestQueryStringRequest: Request { 23 | typealias ParametersType = TestQueryString 24 | typealias ResponseType = EmptyResponse 25 | 26 | var parameters = TestQueryString() 27 | 28 | func path() -> String { 29 | return "/get" 30 | } 31 | 32 | func method() -> HTTPMethod { 33 | return .get 34 | } 35 | } 36 | 37 | private struct TestBodyDataRequest: Request { 38 | typealias ParametersType = TestBodyData 39 | typealias ResponseType = EmptyResponse 40 | 41 | var parameters = TestBodyData() 42 | 43 | func path() -> String { 44 | return "/post" 45 | } 46 | 47 | func method() -> HTTPMethod { 48 | return .post 49 | } 50 | } 51 | 52 | class RequestConverterTests: XCTestCase { 53 | func testRequestWithQueryString() { 54 | let request = TestQueryStringRequest() 55 | guard let urlRequest = try? RequestConverter.convert(request, withBaseURL: URL(string: "http://httpbin.org")!, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 300) else { 56 | XCTFail("Convert request failed") 57 | return 58 | } 59 | XCTAssert(urlRequest.url!.absoluteString.hasPrefix("http://httpbin.org/get")) 60 | for item in URLComponents(url: urlRequest.url!, resolvingAgainstBaseURL: false)!.queryItems! { 61 | XCTAssert(item.name == "string" || item.name == "int") 62 | XCTAssert(item.value == "string" || item.value == "1") 63 | } 64 | XCTAssert(urlRequest.httpMethod == "GET") 65 | XCTAssert((urlRequest.httpBody?.count ?? 0) == 0) 66 | XCTAssert(urlRequest.cachePolicy == .reloadIgnoringLocalCacheData) 67 | XCTAssert(urlRequest.timeoutInterval == 300) 68 | } 69 | 70 | func testRequestWithBodyData() { 71 | let request = TestBodyDataRequest() 72 | guard let urlRequest = try? RequestConverter.convert(request, withBaseURL: URL(string: "http://httpbin.org")!, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 300) else { 73 | XCTFail("Convert request failed") 74 | return 75 | } 76 | XCTAssert(urlRequest.url == URL(string: "http://httpbin.org/post")) 77 | XCTAssert(urlRequest.httpMethod == "POST") 78 | XCTAssert((urlRequest.httpBody?.count ?? 0) > 0) 79 | XCTAssert(urlRequest.cachePolicy == .reloadIgnoringLocalCacheData) 80 | XCTAssert(urlRequest.timeoutInterval == 300) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Tests/DratiniTests/RequestQueueTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestQueueTests.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 4/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Dratini 11 | 12 | private struct TestGetResponse: Response { 13 | let stringParam: String 14 | let chineseParam: String 15 | let intParam: Int 16 | let optionalParam: String? 17 | 18 | init?(data: ResponseData, response: URLResponse) { 19 | guard let args = data.jsonObject["args"] as? [String: Any], 20 | let stringParam = args["string_param"] as? String, 21 | let chineseParam = args["chinese_param"] as? String, 22 | let intParamString = args["int_param"] as? String, 23 | let intParam = Int(intParamString) else { 24 | return nil 25 | } 26 | self.stringParam = stringParam 27 | self.chineseParam = chineseParam 28 | self.intParam = intParam 29 | self.optionalParam = args["optional_param"] as? String 30 | } 31 | } 32 | 33 | private struct TestGetQueryString: DefaultQueryString { 34 | let stringParam: String 35 | let chineseParam: String 36 | let intParam: Int 37 | let optionalParam: String? 38 | } 39 | 40 | private struct TestGetRequest: Request { 41 | typealias ParametersType = TestGetQueryString 42 | typealias ResponseType = TestGetResponse 43 | 44 | var parameters: TestGetQueryString 45 | 46 | func path() -> String { 47 | return "/get" 48 | } 49 | 50 | func method() -> HTTPMethod { 51 | return .get 52 | } 53 | } 54 | 55 | private struct TestPostResponse: Response { 56 | let stringParam: String 57 | let chineseParam: String 58 | let intParam: Int 59 | let optionalParam: String? 60 | 61 | init?(data: ResponseData, response: URLResponse) { 62 | guard let form = data.jsonObject["form"] as? [String: Any], 63 | let stringParam = form["string_param"] as? String, 64 | let chineseParam = form["chinese_param"] as? String, 65 | let intParamString = form["int_param"] as? String, 66 | let intParam = Int(intParamString) else { 67 | return nil 68 | } 69 | self.stringParam = stringParam 70 | self.chineseParam = chineseParam 71 | self.intParam = intParam 72 | self.optionalParam = form["optional_param"] as? String 73 | } 74 | } 75 | 76 | private struct TestPostBodyData: URLEncodedBodyData { 77 | let stringParam: String 78 | let chineseParam: String 79 | let intParam: Int 80 | let optionalParam: String? 81 | } 82 | 83 | private struct TestPostRequest: Request { 84 | typealias ParametersType = TestPostBodyData 85 | typealias ResponseType = TestPostResponse 86 | 87 | var parameters: TestPostBodyData 88 | 89 | func path() -> String { 90 | return "/post" 91 | } 92 | 93 | func method() -> HTTPMethod { 94 | return .post 95 | } 96 | } 97 | 98 | private struct TestPostMultipartResponse: Response { 99 | let fileContent: String 100 | let stringContent: String 101 | let stringValue: String 102 | let chineseValue: String 103 | 104 | init?(data: ResponseData, response: URLResponse) { 105 | guard let files = data.jsonObject["files"] as? [String: Any], 106 | let form = data.jsonObject["form"] as? [String: Any], 107 | let fileContent = files["file_content"] as? String, 108 | let stringContent = files["string_content"] as? String, 109 | let stringValue = form["string_value"] as? String, 110 | let chineseValue = form["chinese_value"] as? String else { 111 | return nil 112 | } 113 | self.fileContent = fileContent 114 | self.stringContent = stringContent 115 | self.stringValue = stringValue 116 | self.chineseValue = chineseValue 117 | } 118 | } 119 | 120 | private struct TestPostMultipartRequest: Request { 121 | typealias ParametersType = MultipartFormData 122 | typealias ResponseType = TestPostMultipartResponse 123 | 124 | var parameters: MultipartFormData 125 | 126 | func path() -> String { 127 | return "/post" 128 | } 129 | 130 | func method() -> HTTPMethod { 131 | return .post 132 | } 133 | } 134 | 135 | private class TestRequestQueueDelegate: RequestQueueDelegate { 136 | var willSend = false 137 | var didSend = false 138 | var didReceive = false 139 | var didFail = false 140 | 141 | func requestQueue(_ requestQueue: RequestQueue, willSend request: inout URLRequest) { 142 | willSend = true 143 | } 144 | 145 | func requestQueue(_ requestQueue: RequestQueue, didSend request: URLRequest) { 146 | didSend = true 147 | } 148 | 149 | func requestQueue(_ requestQueue: RequestQueue, didFailWith request: URLRequest, error: DRError) { 150 | didFail = true 151 | } 152 | 153 | func requestQueue(_ requestQueue: RequestQueue, didReceive response: URLResponse) { 154 | didReceive = true 155 | } 156 | } 157 | 158 | class RequestQueueTests: XCTestCase { 159 | private let delegate = TestRequestQueueDelegate() 160 | private lazy var requestQueue: RequestQueue = { 161 | return RequestQueue(delegate: self.delegate, baseURL: URL(string: "http://httpbin.org")!) 162 | }() 163 | 164 | func testGetRequest() { 165 | let expectation = self.expectation(description: #function) 166 | let request = TestGetRequest(parameters: TestGetQueryString(stringParam: "string", 167 | chineseParam: "中文", 168 | intParam: 1, 169 | optionalParam: nil)) 170 | requestQueue.add(request) 171 | requestQueue.addObserver(ownedBy: self) { (result: Result) in 172 | guard let response = result.response else { 173 | XCTFail("Invalid response") 174 | return 175 | } 176 | XCTAssert(response.stringParam == request.parameters.stringParam) 177 | XCTAssert(response.chineseParam == request.parameters.chineseParam) 178 | XCTAssert(response.intParam == request.parameters.intParam) 179 | XCTAssert(response.optionalParam == request.parameters.optionalParam) 180 | expectation.fulfill() 181 | } 182 | waitForExpectations(timeout: 10, handler: nil) 183 | } 184 | 185 | func testPostRequest() { 186 | let expectation = self.expectation(description: #function) 187 | let request = TestPostRequest(parameters: TestPostBodyData(stringParam: "string", 188 | chineseParam: "中文", 189 | intParam: 1, 190 | optionalParam: nil)) 191 | requestQueue.add(request) 192 | requestQueue.addObserver(ownedBy: self) { (result: Result) in 193 | guard let response = result.response else { 194 | XCTFail("Invalid response") 195 | return 196 | } 197 | XCTAssert(response.stringParam == request.parameters.stringParam) 198 | XCTAssert(response.chineseParam == request.parameters.chineseParam) 199 | XCTAssert(response.intParam == request.parameters.intParam) 200 | XCTAssert(response.optionalParam == request.parameters.optionalParam) 201 | expectation.fulfill() 202 | } 203 | waitForExpectations(timeout: 10, handler: nil) 204 | } 205 | 206 | func testMultipartRequest() { 207 | let expectation = self.expectation(description: #function) 208 | 209 | let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("test_file") 210 | let fileContent = "Test file content" 211 | try! fileContent.data(using: .utf8)?.write(to: fileURL) 212 | 213 | let data = MultipartFormData() 214 | data.append(fileURL: fileURL, withName: "file_content", fileName: "test_file", mimeType: "text/plain") 215 | let stringContent = "Test file data" 216 | data.append(data: stringContent.data(using: .utf8)!, withName: "string_content") 217 | let stringValue = "string" 218 | data.append(value: stringValue, withName: "string_value") 219 | let chineseValue = "中文" 220 | data.append(value: chineseValue, withName: "chinese_value") 221 | 222 | let request = TestPostMultipartRequest(parameters: data) 223 | requestQueue.add(request) 224 | requestQueue.addObserver(ownedBy: self) { (result: Result) in 225 | guard let response = result.response else { 226 | XCTFail("Invalid response") 227 | return 228 | } 229 | XCTAssert(response.fileContent == fileContent) 230 | XCTAssert(response.stringContent == stringContent) 231 | XCTAssert(response.stringValue == stringValue) 232 | XCTAssert(response.chineseValue == chineseValue) 233 | expectation.fulfill() 234 | } 235 | waitForExpectations(timeout: 10, handler: nil) 236 | } 237 | 238 | func testCancel() { 239 | let expectation = self.expectation(description: #function) 240 | let request = TestGetRequest(parameters: TestGetQueryString(stringParam: "string", 241 | chineseParam: "中文", 242 | intParam: 1, 243 | optionalParam: nil)) 244 | let requestID = requestQueue.add(request) 245 | requestQueue.addObserver(for: requestID) { (result: Result) in 246 | XCTFail("Request is not cancelled") 247 | } 248 | requestQueue.cancel(requestID) 249 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 250 | expectation.fulfill() 251 | } 252 | waitForExpectations(timeout: 10, handler: nil) 253 | } 254 | 255 | func testIsFinished() { 256 | let expectation = self.expectation(description: #function) 257 | let request = TestGetRequest(parameters: TestGetQueryString(stringParam: "string", 258 | chineseParam: "中文", 259 | intParam: 1, 260 | optionalParam: nil)) 261 | let requestID = requestQueue.add(request) 262 | requestQueue.addObserver(for: requestID) { (result: Result) in 263 | XCTAssert(self.requestQueue.isFinished(requestID)) 264 | expectation.fulfill() 265 | } 266 | XCTAssert(!requestQueue.isFinished(requestID)) 267 | waitForExpectations(timeout: 10, handler: nil) 268 | } 269 | 270 | func testRemoveObservers() { 271 | let expectation = self.expectation(description: #function) 272 | let request = TestGetRequest(parameters: TestGetQueryString(stringParam: "string", 273 | chineseParam: "中文", 274 | intParam: 1, 275 | optionalParam: nil)) 276 | let requestID = requestQueue.add(request) 277 | requestQueue.addObserver(for: requestID) { (result: Result) in 278 | expectation.fulfill() 279 | } 280 | requestQueue.addObserver(ownedBy: self) { (result: Result) in 281 | XCTFail("Observer is not removed") 282 | } 283 | requestQueue.removeObservers(forType: TestGetResponse.self, ownedBy: self) 284 | waitForExpectations(timeout: 10, handler: nil) 285 | } 286 | 287 | func testRequestQueueDelegate() { 288 | let expectation = self.expectation(description: #function) 289 | let request = TestGetRequest(parameters: TestGetQueryString(stringParam: "string", 290 | chineseParam: "中文", 291 | intParam: 1, 292 | optionalParam: nil)) 293 | requestQueue.add(request) 294 | requestQueue.addObserver(ownedBy: self) { (result: Result) in 295 | XCTAssert(self.delegate.willSend) 296 | XCTAssert(self.delegate.didSend) 297 | XCTAssert(self.delegate.didReceive) 298 | XCTAssert(!self.delegate.didFail) 299 | expectation.fulfill() 300 | } 301 | waitForExpectations(timeout: 10, handler: nil) 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /Tests/DratiniTests/RequestTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestTests.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 11/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Dratini 11 | 12 | private struct TestResponse: Response { 13 | let headerValue: String 14 | init?(data: ResponseData, response: URLResponse) { 15 | guard let headers = data.jsonObject["headers"] as? [String: Any], 16 | let headerValue = headers["Test-Header"] as? String else { 17 | return nil 18 | } 19 | self.headerValue = headerValue 20 | } 21 | } 22 | 23 | private class TestRequest: Request, RequestDelegate { 24 | typealias ParametersType = EmptyParameters 25 | typealias ResponseType = TestResponse 26 | 27 | var parameters = EmptyParameters() 28 | let shouldFail: Bool 29 | private(set) var sent = false 30 | private(set) var errored = false 31 | 32 | init(shouldFail: Bool) { 33 | self.shouldFail = shouldFail 34 | } 35 | 36 | func path() -> String { 37 | return shouldFail ? "/404" : "/get" 38 | } 39 | 40 | func method() -> HTTPMethod { 41 | return .get 42 | } 43 | 44 | func requestWillSend(_ urlRequest: inout URLRequest) { 45 | urlRequest.addValue("Test header value", forHTTPHeaderField: "Test-Header") 46 | } 47 | 48 | func requestDidSend(_ urlRequest: URLRequest) { 49 | sent = true 50 | } 51 | 52 | func request(_ urlRequest: URLRequest, didFailWith error: DRError) { 53 | errored = true 54 | } 55 | } 56 | 57 | class RequestTests: XCTestCase { 58 | private let requestQueue = RequestQueue(baseURL: URL(string: "http://httpbin.org")!) 59 | 60 | func testRequestDelegate() { 61 | let expectation = self.expectation(description: #function) 62 | let request = TestRequest(shouldFail: false) 63 | let requestID = requestQueue.add(request) 64 | requestQueue.addObserver(for: requestID) { (result: Result) in 65 | guard let response = result.response else { 66 | XCTFail("Invalid response") 67 | return 68 | } 69 | XCTAssert(response.headerValue == "Test header value") 70 | XCTAssert(request.sent) 71 | XCTAssert(!request.errored) 72 | expectation.fulfill() 73 | } 74 | waitForExpectations(timeout: 10, handler: nil) 75 | } 76 | 77 | func testRequestDelegateWithFailure() { 78 | let expectation = self.expectation(description: #function) 79 | let request = TestRequest(shouldFail: true) 80 | let requestID = requestQueue.add(request) 81 | requestQueue.addObserver(for: requestID) { (result: Result) in 82 | guard result.isFailure else { 83 | XCTFail("Request should fail") 84 | return 85 | } 86 | XCTAssert(request.sent) 87 | XCTAssert(request.errored) 88 | expectation.fulfill() 89 | } 90 | waitForExpectations(timeout: 10, handler: nil) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Tests/DratiniTests/ResponseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseTests.swift 3 | // Dratini 4 | // 5 | // Created by Kevin Lin on 13/1/17. 6 | // Copyright © 2017 Kevin. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Dratini 11 | 12 | private struct TestRequest: Request { 13 | typealias ParametersType = EmptyParameters 14 | typealias ResponseType = TestResponse 15 | 16 | var parameters = EmptyParameters() 17 | 18 | func path() -> String { 19 | return "/404" 20 | } 21 | 22 | func method() -> HTTPMethod { 23 | return .get 24 | } 25 | } 26 | 27 | private class TestResponse: Response, ResponseDelegate { 28 | private(set) var received = false 29 | 30 | required init?(data: ResponseData, response: URLResponse) { 31 | 32 | } 33 | 34 | static func validate(_ response: URLResponse) -> Bool { 35 | guard let httpURLResponse = response as? HTTPURLResponse else { 36 | return false 37 | } 38 | return httpURLResponse.statusCode == 404 39 | } 40 | 41 | func responseDidReceive(_ response: URLResponse) { 42 | received = true 43 | } 44 | } 45 | 46 | class ResponseTests: XCTestCase { 47 | private let requestQueue = RequestQueue(baseURL: URL(string: "http://httpbin.org")!) 48 | 49 | func testResponseData() { 50 | let jsonObjectString = "{\"int_value\":10,\"float_value\":10.1,\"array_value\":[1,2,3,4,5],\"string_value\":\"string\",\"dictionary_value\":{\"key_1\":\"value_1\"}}" 51 | let jsonObjectData = jsonObjectString.data(using: .utf8)! 52 | let jsonObjectResponseData = ResponseData(jsonObjectData) 53 | XCTAssert(jsonObjectResponseData.string == jsonObjectString) 54 | XCTAssert(jsonObjectResponseData.jsonArray.isEmpty) 55 | if let data = try? JSONSerialization.data(withJSONObject: jsonObjectResponseData.jsonObject, options: []) { 56 | XCTAssert(data == jsonObjectData) 57 | } else { 58 | XCTFail("Invalid JSON object") 59 | } 60 | 61 | let jsonArrayString = "[{\"int_value\":10,\"float_value\":10.1,\"array_value\":[1,2,3,4,5],\"string_value\":\"string\",\"dictionary_value\":{\"key_1\":\"value_1\"}}]" 62 | let jsonArrayData = jsonArrayString.data(using: .utf8)! 63 | let jsonArrayResponseData = ResponseData(jsonArrayData) 64 | XCTAssert(jsonArrayResponseData.string == jsonArrayString) 65 | XCTAssert(jsonArrayResponseData.jsonObject.isEmpty) 66 | if let data = try? JSONSerialization.data(withJSONObject: jsonArrayResponseData.jsonArray, options: []) { 67 | XCTAssert(data == jsonArrayData) 68 | } else { 69 | XCTFail("Invalid JSON array") 70 | } 71 | } 72 | 73 | func testResponse() { 74 | let expectation = self.expectation(description: #function) 75 | let request = TestRequest() 76 | let requestID = requestQueue.add(request) 77 | requestQueue.addObserver(for: requestID) { (result: Result) in 78 | guard let response = result.response else { 79 | XCTFail("Invalid response") 80 | return 81 | } 82 | XCTAssert(response.received) 83 | expectation.fulfill() 84 | } 85 | waitForExpectations(timeout: 10, handler: nil) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | ignore: 3 | - "Tests/*" 4 | --------------------------------------------------------------------------------