├── .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  [](https://codecov.io/gh/kevin0571/Dratini)    
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 |
--------------------------------------------------------------------------------