├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── STNetTaskQueue.podspec ├── STNetTaskQueue.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── STNetTaskQueue.xcscheme ├── STNetTaskQueue ├── Info.plist ├── STHTTPNetTask.h ├── STHTTPNetTask.m ├── STHTTPNetTaskParametersPacker.h ├── STHTTPNetTaskParametersPacker.m ├── STHTTPNetTaskQueueHandler.h ├── STHTTPNetTaskQueueHandler.m ├── STNetTask.h ├── STNetTask.m ├── STNetTaskChain.h ├── STNetTaskChain.m ├── STNetTaskGroup.h ├── STNetTaskGroup.m ├── STNetTaskQueue.h ├── STNetTaskQueue.m ├── STNetTaskQueueLog.h └── STNetTaskQueueLog.m └── STNetTaskQueueTest ├── STNetTaskQueueTest.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── STNetTaskQueueTest.xcscheme └── STNetTaskQueueTest ├── Info.plist ├── STNetTaskQueueTestHTTP.m ├── STTestDeleteNetTask.h ├── STTestDeleteNetTask.m ├── STTestGetNetTask.h ├── STTestGetNetTask.m ├── STTestMaxConcurrentTasksCountNetTask.h ├── STTestMaxConcurrentTasksCountNetTask.m ├── STTestPackerNetTask.h ├── STTestPackerNetTask.m ├── STTestPatchNetTask.h ├── STTestPatchNetTask.m ├── STTestPostNetTask.h ├── STTestPostNetTask.m ├── STTestPutNetTask.h ├── STTestPutNetTask.m ├── STTestRetryNetTask.h └── STTestRetryNetTask.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | Pods/ 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8 3 | script: 4 | - xcodebuild test -project STNetTaskQueueTest/STNetTaskQueueTest.xcodeproj -scheme STNetTaskQueueTest -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6,OS=9.3' 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kevin 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STNetTaskQueue ![CI Status](https://img.shields.io/travis/kevin0571/STNetTaskQueue.svg?style=flat) ![Version](http://img.shields.io/cocoapods/v/STNetTaskQueue.svg?style=flag) ![License](https://img.shields.io/cocoapods/l/STNetTaskQueue.svg?style=flag) 2 | STNetTaskQueue is a networking queue library for iOS and OS X. It's abstract and can be implemented in different protocols. 3 | 4 | STNetTaskQueue avoid you from directly dealing with "url", "request packing" and "response parsing". All networking tasks are described and processed by subclassing STNetTask, which provides you a clean code style in UI layer when handling networking. 5 | 6 | ## Features 7 | - Auto packing parameters for HTTP net task. 8 | - Max concurrent tasks count in each STNetTaskQueue. 9 | - Max retry count for each STNetTask. 10 | - Net task is cancelable after added to STNetTaskQueue. 11 | - Multiple delegates for same net task. 12 | - Works with ReactiveCocoa, subscribeCompleted for net task result. 13 | 14 | ## STHTTPNetTaskQueueHandler 15 | 16 | STHTTPNetTaskQueueHandler is a HTTP based implementation of STNetTaskQueueHandler. It provides different ways to pack request and parse response, e.g. STHTTPNetTaskRequestJSON is for JSON format request body, STHTTPNetTaskResponseJSON is for JSON format response data and STHTTPNetTaskRequestFormData is for form data format request body which is mostly used for uploading file. 17 | 18 | ## STNetTask 19 | 20 | STNetTask is abstract, it provides basic properties and callbacks for subclassing. 21 | 22 | ## STNetTaskDelegate 23 | 24 | STNetTaskDelegate is the delegate protocol for observing result of STNetTask, mostly it is used in view controller. 25 | 26 | ## ~~STNetTaskChain~~ (Deprecated. Use STNetTaskGroup instead) 27 | 28 | ~~STNetTaskChain is a chain which processes an array of STNetTask serially. A net task chain is considered as successful only if all net tasks in the chain are end without error.~~ 29 | 30 | ## STNetTaskGroup 31 | 32 | A net task group for executing net tasks serially or concurrently. 33 | 34 | ## Get Started 35 | 36 | ### Podfile 37 | 38 | ```ruby 39 | platform :ios, '7.0' 40 | pod 'STNetTaskQueue' 41 | ``` 42 | 43 | ### Carthage 44 | ```ruby 45 | github "kevin0571/STNetTaskQueue" 46 | ``` 47 | 48 | ### Use STNetTaskQueue in your project 49 | #### Step 1: Setup STNetTaskQueue after your app launch 50 | ```objc 51 | NSURL *baseUrl = [NSURL URLWithString:@"http://jsonplaceholder.typicode.com"]; 52 | STHTTPNetTaskQueueHandler *httpHandler = [[STHTTPNetTaskQueueHandler alloc] initWithBaseURL:baseUrl]; 53 | [STNetTaskQueue sharedQueue].handler = httpHandler; 54 | ``` 55 | 56 | #### Step 2: Create your net task 57 | ```objc 58 | @interface STTestPostNetTask : STHTTPNetTask 59 | 60 | @property (nonatomic, strong) NSString *title; 61 | @property (nonatomic, strong) NSString *body; 62 | @property (nonatomic, strong) NSDate *date; 63 | @property (nonatomic, assign) int userId; 64 | @property (nonatomic, strong) NSString *ignored; // This property is ignored when packing the request. 65 | @property (nonatomic, strong, readonly) NSDictionary *post; 66 | 67 | @end 68 | ``` 69 | 70 | ```objc 71 | @implementation STTestPostNetTask 72 | 73 | - (STHTTPNetTaskMethod)method 74 | { 75 | return STHTTPNetTaskPost; 76 | } 77 | 78 | - (NSString *)uri 79 | { 80 | return @"posts"; 81 | } 82 | 83 | // Optional. Retry 3 times after error occurs. 84 | - (NSUInteger)maxRetryCount 85 | { 86 | return 3; 87 | } 88 | 89 | // Optional. Retry for all types of errors 90 | - (BOOL)shouldRetryForError:(NSError *)error 91 | { 92 | return YES; 93 | } 94 | 95 | // Optional. Retry after 5 seconds. 96 | - (NSTimeInterval)retryInterval 97 | { 98 | return 5; 99 | } 100 | 101 | // Optional. Custom headers. 102 | - (NSDictionary *)headers 103 | { 104 | return @{ @"custom_header": @"value" }; 105 | } 106 | 107 | // Optional. Add parameters which are not inclued in requestObject and net task properties. 108 | - (NSDictionary *)parameters 109 | { 110 | return @{ @"other_parameter": @"value" }; 111 | } 112 | 113 | // Optional. Transform value to a format you want. 114 | - (id)transformValue:(id)value 115 | { 116 | if ([value isKindOfClass:[NSDate class]]) { 117 | return @([value timeIntervalSince1970]); 118 | } 119 | return value; 120 | } 121 | 122 | - (void)didResponseDictionary:(NSDictionary *)dictionary 123 | { 124 | _post = dictionary; 125 | } 126 | 127 | @end 128 | ``` 129 | 130 | #### Step 3: Send net task and delegate for the result 131 | ```objc 132 | STTestPostNetTask *testPostTask = [STTestPostNetTask new]; 133 | testPostTask.title = @"Test Post Net Task Title"; 134 | testPostTask.body = @"Test Post Net Task Body"; 135 | testPostTask.userId = 1; 136 | testPostTask.date = [NSDate new]; 137 | testPostTask.ignored = @"test"; 138 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self uri:testPostTask.uri]; 139 | [[STNetTaskQueue sharedQueue] addTask:testPostTask]; 140 | 141 | // The net task will be sent as described below. 142 | /* 143 | URI: posts 144 | Method: POST 145 | Request Type: Key-Value String 146 | Response Type: JSON 147 | Custom Headers: 148 | { 149 | "custom_header" = value; 150 | } 151 | Parameters: 152 | { 153 | body = "Test Post Net Task Body"; 154 | date = "1452239110.829915"; 155 | "other_parameter" = value; 156 | title = "Test Post Net Task Title"; 157 | "user_id" = 1; 158 | } 159 | */ 160 | ``` 161 | 162 | #### Use subscription block 163 | ```objc 164 | [testPostTask subscribeState:STNetTaskStateFinished usingBlock:^{ 165 | if (testPostTask.error) { 166 | // Handle error cases 167 | return; 168 | } 169 | // Access result from net task 170 | }]; 171 | ``` 172 | 173 | #### Use STNetTaskDelegate 174 | 175 | ```objc 176 | - (void)netTaskDidEnd:(STNetTask *)task 177 | { 178 | if (task.error) { 179 | // Handle error cases 180 | return; 181 | } 182 | // Access result from net task 183 | } 184 | ``` 185 | 186 | #### Work with ReactiveCocoa for getting net task result 187 | 188 | ```objc 189 | [STNetTaskObserve(testPostTask) subscribeCompleted:^( 190 | if (testPostTask.error) { 191 | // Handle error cases 192 | return; 193 | } 194 | // Access result from net task 195 | }]; 196 | ``` 197 | 198 | For more details, check out unit tests. 199 | 200 | ### Set max concurrent tasks count of STNetTaskQueue 201 | Sometimes we need to set the concurrent image download tasks to avoid too much data coming at the same time. 202 | 203 | ```objc 204 | STNetTaskQueue *downloadQueue = [STNetTaskQueue new]; 205 | downloadQueue.handler = [[STHTTPNetTaskQueueHandler alloc] initWithBaseURL:[NSURL URLWithString:@"http://example.com"]]; 206 | downloadQueue.maxConcurrentTasksCount = 2; 207 | /* 208 | [downloadQueue addTask:task1]; 209 | [downloadQueue addTask:task2]; 210 | [downloadQueue addTask:task3]; // task3 will be sent after task1 or task2 is finished. 211 | */ 212 | ``` 213 | 214 | ### Use STNetTaskGroup to execute multiple net tasks 215 | STNetTaskGroup supports two modes: STNetTaskGroupModeSerial and STNetTaskGroupConcurrent. 216 | STNetTaskGroupModeSerial will execute a net task after the previous net task is finished. 217 | STNetTaskGroupModeConcurrent will execute all net tasks concurrently. 218 | ```objc 219 | STTestGetNetTask *task1 = [STTestGetNetTask new]; 220 | task1.id = 1; 221 | 222 | STTestGetNetTask *task2 = [STTestGetNetTask new]; 223 | task2.id = 2; 224 | 225 | STNetTaskGroup *group = [[STNetTaskGroup alloc] initWithTasks:@[ task1, task2 ] mode:STNetTaskGroupModeSerial]; 226 | [group subscribeState:STNetTaskGroupStateFinished usingBlock:^(STNetTaskGroup *group, NSError *error) { 227 | if (error) { 228 | // One of the net task is failed. 229 | return; 230 | } 231 | // All net tasks are finished without error. 232 | }]; 233 | [group start]; 234 | ``` 235 | 236 | Or a handy way: 237 | ```objc 238 | STTestGetNetTask *task1 = [STTestGetNetTask new]; 239 | task1.id = 1; 240 | 241 | STTestGetNetTask *task2 = [STTestGetNetTask new]; 242 | task2.id = 2; 243 | 244 | [[[@[ task1, task2 ] serialNetTaskGroup] subscribeState:STNetTaskGroupStateFinished usingBlock:^(STNetTaskGroup *group, NSError *error) { 245 | if (error) { 246 | // One of the net task is failed. 247 | return; 248 | } 249 | // All net tasks are finished without error. 250 | }] start]; 251 | ``` -------------------------------------------------------------------------------- /STNetTaskQueue.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "STNetTaskQueue" 3 | s.version = "0.0.22" 4 | s.summary = "STNetTaskQueue is a networking queue library for iOS and OS X. It's abstract and can be implemented in different protocols." 5 | 6 | s.description = <<-DESC 7 | STNetTaskQueue avoid you from directly dealing with "url", "request packing" and "response parsing". All networking tasks are described and processed by subclassing STNetTask, which provides you a clean code style in UI layer when handling networking. 8 | DESC 9 | 10 | s.homepage = "https://github.com/kevin0571/STNetTaskQueue" 11 | s.license = { :type => "MIT", :file => "LICENSE" } 12 | s.author = { "Kevin Lin" => "kevin_lyn@outlook.com" } 13 | 14 | s.platform = :ios, "7.0" 15 | s.source = { :git => "https://github.com/kevin0571/STNetTaskQueue.git", :tag => s.version } 16 | 17 | s.source_files = "STNetTaskQueue/*.{h,m}" 18 | s.public_header_files = "STNetTaskQueue/*.h" 19 | end 20 | -------------------------------------------------------------------------------- /STNetTaskQueue.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 76012C6E1C265EC50090BE94 /* STHTTPNetTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 76012C601C265EC50090BE94 /* STHTTPNetTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 76012C6F1C265EC50090BE94 /* STHTTPNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 76012C611C265EC50090BE94 /* STHTTPNetTask.m */; }; 12 | 76012C701C265EC50090BE94 /* STHTTPNetTaskParametersPacker.h in Headers */ = {isa = PBXBuildFile; fileRef = 76012C621C265EC50090BE94 /* STHTTPNetTaskParametersPacker.h */; }; 13 | 76012C711C265EC50090BE94 /* STHTTPNetTaskParametersPacker.m in Sources */ = {isa = PBXBuildFile; fileRef = 76012C631C265EC50090BE94 /* STHTTPNetTaskParametersPacker.m */; }; 14 | 76012C721C265EC50090BE94 /* STHTTPNetTaskQueueHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 76012C641C265EC50090BE94 /* STHTTPNetTaskQueueHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 76012C731C265EC50090BE94 /* STHTTPNetTaskQueueHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 76012C651C265EC50090BE94 /* STHTTPNetTaskQueueHandler.m */; }; 16 | 76012C741C265EC50090BE94 /* STNetTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 76012C661C265EC50090BE94 /* STNetTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 76012C751C265EC50090BE94 /* STNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 76012C671C265EC50090BE94 /* STNetTask.m */; }; 18 | 76012C761C265EC50090BE94 /* STNetTaskChain.h in Headers */ = {isa = PBXBuildFile; fileRef = 76012C681C265EC50090BE94 /* STNetTaskChain.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | 76012C771C265EC50090BE94 /* STNetTaskChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 76012C691C265EC50090BE94 /* STNetTaskChain.m */; }; 20 | 76012C781C265EC50090BE94 /* STNetTaskQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 76012C6A1C265EC50090BE94 /* STNetTaskQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; 21 | 76012C791C265EC50090BE94 /* STNetTaskQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 76012C6B1C265EC50090BE94 /* STNetTaskQueue.m */; }; 22 | 76012C7A1C265EC50090BE94 /* STNetTaskQueueLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 76012C6C1C265EC50090BE94 /* STNetTaskQueueLog.h */; }; 23 | 76012C7B1C265EC50090BE94 /* STNetTaskQueueLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 76012C6D1C265EC50090BE94 /* STNetTaskQueueLog.m */; }; 24 | 7645D0FF1CDF66D5000ED54A /* STNetTaskGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 7645D0FD1CDF66D5000ED54A /* STNetTaskGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25 | 7645D1001CDF66D5000ED54A /* STNetTaskGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 7645D0FE1CDF66D5000ED54A /* STNetTaskGroup.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 76012C551C265A160090BE94 /* STNetTaskQueue.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = STNetTaskQueue.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 76012C5A1C265A160090BE94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 76012C601C265EC50090BE94 /* STHTTPNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STHTTPNetTask.h; sourceTree = ""; }; 32 | 76012C611C265EC50090BE94 /* STHTTPNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STHTTPNetTask.m; sourceTree = ""; }; 33 | 76012C621C265EC50090BE94 /* STHTTPNetTaskParametersPacker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STHTTPNetTaskParametersPacker.h; sourceTree = ""; }; 34 | 76012C631C265EC50090BE94 /* STHTTPNetTaskParametersPacker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STHTTPNetTaskParametersPacker.m; sourceTree = ""; }; 35 | 76012C641C265EC50090BE94 /* STHTTPNetTaskQueueHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STHTTPNetTaskQueueHandler.h; sourceTree = ""; }; 36 | 76012C651C265EC50090BE94 /* STHTTPNetTaskQueueHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STHTTPNetTaskQueueHandler.m; sourceTree = ""; }; 37 | 76012C661C265EC50090BE94 /* STNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STNetTask.h; sourceTree = ""; }; 38 | 76012C671C265EC50090BE94 /* STNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STNetTask.m; sourceTree = ""; }; 39 | 76012C681C265EC50090BE94 /* STNetTaskChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STNetTaskChain.h; sourceTree = ""; }; 40 | 76012C691C265EC50090BE94 /* STNetTaskChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STNetTaskChain.m; sourceTree = ""; }; 41 | 76012C6A1C265EC50090BE94 /* STNetTaskQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STNetTaskQueue.h; sourceTree = ""; }; 42 | 76012C6B1C265EC50090BE94 /* STNetTaskQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STNetTaskQueue.m; sourceTree = ""; }; 43 | 76012C6C1C265EC50090BE94 /* STNetTaskQueueLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STNetTaskQueueLog.h; sourceTree = ""; }; 44 | 76012C6D1C265EC50090BE94 /* STNetTaskQueueLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STNetTaskQueueLog.m; sourceTree = ""; }; 45 | 7645D0FD1CDF66D5000ED54A /* STNetTaskGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STNetTaskGroup.h; sourceTree = ""; }; 46 | 7645D0FE1CDF66D5000ED54A /* STNetTaskGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STNetTaskGroup.m; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 76012C511C265A160090BE94 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | 76012C4B1C265A160090BE94 = { 61 | isa = PBXGroup; 62 | children = ( 63 | 76012C571C265A160090BE94 /* STNetTaskQueue */, 64 | 76012C561C265A160090BE94 /* Products */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | 76012C561C265A160090BE94 /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 76012C551C265A160090BE94 /* STNetTaskQueue.framework */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 76012C571C265A160090BE94 /* STNetTaskQueue */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 76012C7C1C265F6E0090BE94 /* HTTP */, 80 | 76012C661C265EC50090BE94 /* STNetTask.h */, 81 | 76012C671C265EC50090BE94 /* STNetTask.m */, 82 | 76012C681C265EC50090BE94 /* STNetTaskChain.h */, 83 | 76012C691C265EC50090BE94 /* STNetTaskChain.m */, 84 | 76012C6A1C265EC50090BE94 /* STNetTaskQueue.h */, 85 | 76012C6B1C265EC50090BE94 /* STNetTaskQueue.m */, 86 | 76012C6C1C265EC50090BE94 /* STNetTaskQueueLog.h */, 87 | 76012C6D1C265EC50090BE94 /* STNetTaskQueueLog.m */, 88 | 7645D0FD1CDF66D5000ED54A /* STNetTaskGroup.h */, 89 | 7645D0FE1CDF66D5000ED54A /* STNetTaskGroup.m */, 90 | 76012C5A1C265A160090BE94 /* Info.plist */, 91 | ); 92 | path = STNetTaskQueue; 93 | sourceTree = ""; 94 | }; 95 | 76012C7C1C265F6E0090BE94 /* HTTP */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 76012C601C265EC50090BE94 /* STHTTPNetTask.h */, 99 | 76012C611C265EC50090BE94 /* STHTTPNetTask.m */, 100 | 76012C621C265EC50090BE94 /* STHTTPNetTaskParametersPacker.h */, 101 | 76012C631C265EC50090BE94 /* STHTTPNetTaskParametersPacker.m */, 102 | 76012C641C265EC50090BE94 /* STHTTPNetTaskQueueHandler.h */, 103 | 76012C651C265EC50090BE94 /* STHTTPNetTaskQueueHandler.m */, 104 | ); 105 | name = HTTP; 106 | sourceTree = ""; 107 | }; 108 | /* End PBXGroup section */ 109 | 110 | /* Begin PBXHeadersBuildPhase section */ 111 | 76012C521C265A160090BE94 /* Headers */ = { 112 | isa = PBXHeadersBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | 76012C6E1C265EC50090BE94 /* STHTTPNetTask.h in Headers */, 116 | 76012C761C265EC50090BE94 /* STNetTaskChain.h in Headers */, 117 | 76012C741C265EC50090BE94 /* STNetTask.h in Headers */, 118 | 76012C781C265EC50090BE94 /* STNetTaskQueue.h in Headers */, 119 | 7645D0FF1CDF66D5000ED54A /* STNetTaskGroup.h in Headers */, 120 | 76012C721C265EC50090BE94 /* STHTTPNetTaskQueueHandler.h in Headers */, 121 | 76012C7A1C265EC50090BE94 /* STNetTaskQueueLog.h in Headers */, 122 | 76012C701C265EC50090BE94 /* STHTTPNetTaskParametersPacker.h in Headers */, 123 | ); 124 | runOnlyForDeploymentPostprocessing = 0; 125 | }; 126 | /* End PBXHeadersBuildPhase section */ 127 | 128 | /* Begin PBXNativeTarget section */ 129 | 76012C541C265A160090BE94 /* STNetTaskQueue */ = { 130 | isa = PBXNativeTarget; 131 | buildConfigurationList = 76012C5D1C265A160090BE94 /* Build configuration list for PBXNativeTarget "STNetTaskQueue" */; 132 | buildPhases = ( 133 | 76012C501C265A160090BE94 /* Sources */, 134 | 76012C511C265A160090BE94 /* Frameworks */, 135 | 76012C521C265A160090BE94 /* Headers */, 136 | 76012C531C265A160090BE94 /* Resources */, 137 | ); 138 | buildRules = ( 139 | ); 140 | dependencies = ( 141 | ); 142 | name = STNetTaskQueue; 143 | productName = STNetTaskQueue; 144 | productReference = 76012C551C265A160090BE94 /* STNetTaskQueue.framework */; 145 | productType = "com.apple.product-type.framework"; 146 | }; 147 | /* End PBXNativeTarget section */ 148 | 149 | /* Begin PBXProject section */ 150 | 76012C4C1C265A160090BE94 /* Project object */ = { 151 | isa = PBXProject; 152 | attributes = { 153 | LastUpgradeCheck = 0800; 154 | ORGANIZATIONNAME = Sth4Me; 155 | TargetAttributes = { 156 | 76012C541C265A160090BE94 = { 157 | CreatedOnToolsVersion = 7.2; 158 | }; 159 | }; 160 | }; 161 | buildConfigurationList = 76012C4F1C265A160090BE94 /* Build configuration list for PBXProject "STNetTaskQueue" */; 162 | compatibilityVersion = "Xcode 3.2"; 163 | developmentRegion = English; 164 | hasScannedForEncodings = 0; 165 | knownRegions = ( 166 | en, 167 | ); 168 | mainGroup = 76012C4B1C265A160090BE94; 169 | productRefGroup = 76012C561C265A160090BE94 /* Products */; 170 | projectDirPath = ""; 171 | projectRoot = ""; 172 | targets = ( 173 | 76012C541C265A160090BE94 /* STNetTaskQueue */, 174 | ); 175 | }; 176 | /* End PBXProject section */ 177 | 178 | /* Begin PBXResourcesBuildPhase section */ 179 | 76012C531C265A160090BE94 /* Resources */ = { 180 | isa = PBXResourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXResourcesBuildPhase section */ 187 | 188 | /* Begin PBXSourcesBuildPhase section */ 189 | 76012C501C265A160090BE94 /* Sources */ = { 190 | isa = PBXSourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | 76012C6F1C265EC50090BE94 /* STHTTPNetTask.m in Sources */, 194 | 7645D1001CDF66D5000ED54A /* STNetTaskGroup.m in Sources */, 195 | 76012C7B1C265EC50090BE94 /* STNetTaskQueueLog.m in Sources */, 196 | 76012C711C265EC50090BE94 /* STHTTPNetTaskParametersPacker.m in Sources */, 197 | 76012C771C265EC50090BE94 /* STNetTaskChain.m in Sources */, 198 | 76012C731C265EC50090BE94 /* STHTTPNetTaskQueueHandler.m in Sources */, 199 | 76012C751C265EC50090BE94 /* STNetTask.m in Sources */, 200 | 76012C791C265EC50090BE94 /* STNetTaskQueue.m in Sources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXSourcesBuildPhase section */ 205 | 206 | /* Begin XCBuildConfiguration section */ 207 | 76012C5B1C265A160090BE94 /* Debug */ = { 208 | isa = XCBuildConfiguration; 209 | buildSettings = { 210 | ALWAYS_SEARCH_USER_PATHS = NO; 211 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 212 | CLANG_CXX_LIBRARY = "libc++"; 213 | CLANG_ENABLE_MODULES = YES; 214 | CLANG_ENABLE_OBJC_ARC = YES; 215 | CLANG_WARN_BOOL_CONVERSION = YES; 216 | CLANG_WARN_CONSTANT_CONVERSION = YES; 217 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 218 | CLANG_WARN_EMPTY_BODY = YES; 219 | CLANG_WARN_ENUM_CONVERSION = YES; 220 | CLANG_WARN_INFINITE_RECURSION = YES; 221 | CLANG_WARN_INT_CONVERSION = YES; 222 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 223 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 224 | CLANG_WARN_UNREACHABLE_CODE = YES; 225 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 226 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 227 | COPY_PHASE_STRIP = NO; 228 | CURRENT_PROJECT_VERSION = 1; 229 | DEBUG_INFORMATION_FORMAT = dwarf; 230 | ENABLE_STRICT_OBJC_MSGSEND = YES; 231 | ENABLE_TESTABILITY = YES; 232 | GCC_C_LANGUAGE_STANDARD = gnu99; 233 | GCC_DYNAMIC_NO_PIC = NO; 234 | GCC_NO_COMMON_BLOCKS = YES; 235 | GCC_OPTIMIZATION_LEVEL = 0; 236 | GCC_PREPROCESSOR_DEFINITIONS = ( 237 | "DEBUG=1", 238 | "$(inherited)", 239 | ); 240 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 241 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 242 | GCC_WARN_UNDECLARED_SELECTOR = YES; 243 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 244 | GCC_WARN_UNUSED_FUNCTION = YES; 245 | GCC_WARN_UNUSED_VARIABLE = YES; 246 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 247 | MTL_ENABLE_DEBUG_INFO = YES; 248 | ONLY_ACTIVE_ARCH = YES; 249 | SDKROOT = iphoneos; 250 | TARGETED_DEVICE_FAMILY = "1,2"; 251 | VERSIONING_SYSTEM = "apple-generic"; 252 | VERSION_INFO_PREFIX = ""; 253 | }; 254 | name = Debug; 255 | }; 256 | 76012C5C1C265A160090BE94 /* Release */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ALWAYS_SEARCH_USER_PATHS = NO; 260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 261 | CLANG_CXX_LIBRARY = "libc++"; 262 | CLANG_ENABLE_MODULES = YES; 263 | CLANG_ENABLE_OBJC_ARC = YES; 264 | CLANG_WARN_BOOL_CONVERSION = YES; 265 | CLANG_WARN_CONSTANT_CONVERSION = YES; 266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 267 | CLANG_WARN_EMPTY_BODY = YES; 268 | CLANG_WARN_ENUM_CONVERSION = YES; 269 | CLANG_WARN_INFINITE_RECURSION = YES; 270 | CLANG_WARN_INT_CONVERSION = YES; 271 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 272 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 273 | CLANG_WARN_UNREACHABLE_CODE = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 276 | COPY_PHASE_STRIP = NO; 277 | CURRENT_PROJECT_VERSION = 1; 278 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 279 | ENABLE_NS_ASSERTIONS = NO; 280 | ENABLE_STRICT_OBJC_MSGSEND = YES; 281 | GCC_C_LANGUAGE_STANDARD = gnu99; 282 | GCC_NO_COMMON_BLOCKS = YES; 283 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 284 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 285 | GCC_WARN_UNDECLARED_SELECTOR = YES; 286 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 287 | GCC_WARN_UNUSED_FUNCTION = YES; 288 | GCC_WARN_UNUSED_VARIABLE = YES; 289 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 290 | MTL_ENABLE_DEBUG_INFO = NO; 291 | SDKROOT = iphoneos; 292 | TARGETED_DEVICE_FAMILY = "1,2"; 293 | VALIDATE_PRODUCT = YES; 294 | VERSIONING_SYSTEM = "apple-generic"; 295 | VERSION_INFO_PREFIX = ""; 296 | }; 297 | name = Release; 298 | }; 299 | 76012C5E1C265A160090BE94 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 303 | DEFINES_MODULE = YES; 304 | DYLIB_COMPATIBILITY_VERSION = 1; 305 | DYLIB_CURRENT_VERSION = 1; 306 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 307 | INFOPLIST_FILE = STNetTaskQueue/Info.plist; 308 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 309 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 310 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 311 | PRODUCT_BUNDLE_IDENTIFIER = me.sth4.STNetTaskQueue; 312 | PRODUCT_NAME = "$(TARGET_NAME)"; 313 | SKIP_INSTALL = YES; 314 | }; 315 | name = Debug; 316 | }; 317 | 76012C5F1C265A160090BE94 /* Release */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 321 | DEFINES_MODULE = YES; 322 | DYLIB_COMPATIBILITY_VERSION = 1; 323 | DYLIB_CURRENT_VERSION = 1; 324 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 325 | INFOPLIST_FILE = STNetTaskQueue/Info.plist; 326 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 327 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 328 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 329 | PRODUCT_BUNDLE_IDENTIFIER = me.sth4.STNetTaskQueue; 330 | PRODUCT_NAME = "$(TARGET_NAME)"; 331 | SKIP_INSTALL = YES; 332 | }; 333 | name = Release; 334 | }; 335 | /* End XCBuildConfiguration section */ 336 | 337 | /* Begin XCConfigurationList section */ 338 | 76012C4F1C265A160090BE94 /* Build configuration list for PBXProject "STNetTaskQueue" */ = { 339 | isa = XCConfigurationList; 340 | buildConfigurations = ( 341 | 76012C5B1C265A160090BE94 /* Debug */, 342 | 76012C5C1C265A160090BE94 /* Release */, 343 | ); 344 | defaultConfigurationIsVisible = 0; 345 | defaultConfigurationName = Release; 346 | }; 347 | 76012C5D1C265A160090BE94 /* Build configuration list for PBXNativeTarget "STNetTaskQueue" */ = { 348 | isa = XCConfigurationList; 349 | buildConfigurations = ( 350 | 76012C5E1C265A160090BE94 /* Debug */, 351 | 76012C5F1C265A160090BE94 /* Release */, 352 | ); 353 | defaultConfigurationIsVisible = 0; 354 | defaultConfigurationName = Release; 355 | }; 356 | /* End XCConfigurationList section */ 357 | }; 358 | rootObject = 76012C4C1C265A160090BE94 /* Project object */; 359 | } 360 | -------------------------------------------------------------------------------- /STNetTaskQueue.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /STNetTaskQueue.xcodeproj/xcshareddata/xcschemes/STNetTaskQueue.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 | -------------------------------------------------------------------------------- /STNetTaskQueue/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 | 0.0.22 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /STNetTaskQueue/STHTTPNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STHTTPNetTask.h 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | // Error domains 14 | FOUNDATION_EXPORT NSString *const STHTTPNetTaskServerError; 15 | FOUNDATION_EXPORT NSString *const STHTTPNetTaskResponseParsedError; 16 | 17 | // Error "userInfo" keys 18 | FOUNDATION_EXPORT NSString *const STHTTPNetTaskErrorStatusCodeUserInfoKey DEPRECATED_MSG_ATTRIBUTE("Use STHTTPNetTask.statusCode instead"); 19 | FOUNDATION_EXPORT NSString *const STHTTPNetTaskErrorResponseDataUserInfoKey; 20 | 21 | FOUNDATION_EXPORT NSString *STHTTPNetTaskRequestObjectDefaultSeparator; 22 | 23 | #define STHTTPNetTaskIgnoreAllProperties @[ @"*" ] 24 | 25 | typedef NS_ENUM(NSUInteger, STHTTPNetTaskMethod) { 26 | STHTTPNetTaskGet, 27 | STHTTPNetTaskPost, 28 | STHTTPNetTaskPut, 29 | STHTTPNetTaskDelete, 30 | STHTTPNetTaskHead, 31 | STHTTPNetTaskPatch 32 | }; 33 | 34 | typedef NS_ENUM(NSUInteger, STHTTPNetTaskRequestType) { 35 | STHTTPNetTaskRequestJSON, 36 | STHTTPNetTaskRequestKeyValueString, 37 | STHTTPNetTaskRequestFormData 38 | }; 39 | 40 | typedef NS_ENUM(NSUInteger, STHTTPNetTaskResponseType) { 41 | STHTTPNetTaskResponseJSON, 42 | STHTTPNetTaskResponseString, 43 | STHTTPNetTaskResponseRawData 44 | }; 45 | 46 | /** 47 | STIgnore marks a property as "ignore" so that the property will be ignored when packing the request. 48 | STHTTPNetTaskRequestObject-ignoredProperties will do the same. 49 | 50 | @see STHTTPNetTaskRequestObject 51 | */ 52 | @protocol STIgnore 53 | 54 | @end 55 | 56 | /** 57 | To avoid complier warnings 58 | */ 59 | @interface NSObject (STHTTPNetTaskRequestObject) 60 | 61 | @end 62 | 63 | /** 64 | If a class conforms to this protocol, it means the instance of this class will be converted to a dictionary and passed as parameter in a HTTP request. 65 | */ 66 | @protocol STHTTPNetTaskRequestObject 67 | 68 | /** 69 | Properties which should be ignored when packing parameters for reqeust. 70 | 71 | @return NSArray An array of strings representing the name of properties to be ignored. 72 | */ 73 | - (NSArray *)ignoredProperties; 74 | 75 | @optional 76 | 77 | /** 78 | Transform a value to another. 79 | Use case: NSArray need to be transformed to comma separated string. 80 | 81 | @param value id Value to be transformed 82 | @return id The transformed value. Should return the same value if "value" is not supposed to be transformed. 83 | */ 84 | - (nullable id)transformValue:(id)value; 85 | 86 | /** 87 | Separator string which should be used when packing parameters. 88 | E.g. property schoolName will be converted to school_name. 89 | Default: @"_" 90 | 91 | @return NSString 92 | */ 93 | - (nullable NSString *)parameterNameSeparator; 94 | 95 | @end 96 | 97 | /** 98 | Net task which is designed for HTTP protocol. 99 | */ 100 | @interface STHTTPNetTask : STNetTask 101 | 102 | /** 103 | HTTP status code. 104 | */ 105 | @property (atomic, assign, readonly) NSInteger statusCode; 106 | 107 | /** 108 | HTTP headers of response. 109 | */ 110 | @property (atomic, strong, readonly) NSDictionary *responseHeaders; 111 | 112 | /** 113 | HTTP method which should be used for the HTTP net task. 114 | 115 | @return STHTTPNetTaskMethod 116 | */ 117 | - (STHTTPNetTaskMethod)method; 118 | 119 | /** 120 | Request parameters format. E.g JSON, key-value string(form param). 121 | 122 | @return STHTTPNetTaskRequestType 123 | */ 124 | - (STHTTPNetTaskRequestType)requestType; 125 | 126 | /** 127 | Response data format. E.g JSON, String, Raw data. 128 | 129 | @return STHTTPNetTaskResponseType 130 | */ 131 | - (STHTTPNetTaskResponseType)responseType; 132 | 133 | /** 134 | Custom headers which will be added into HTTP request headers. 135 | 136 | @return NSDictionary Custom headers, e.g. @{ @"User-Agent": @"STNetTaskQueue Client" } 137 | */ 138 | - (NSDictionary *)headers; 139 | 140 | /** 141 | Additional parameters which will be added as HTTP request parameters. 142 | 143 | @return NSDictionary 144 | */ 145 | - (NSDictionary *)parameters; 146 | 147 | /** 148 | NSDatas which will be added into multi-part form data body, 149 | requestType should be STHTTPNetTaskRequestFormData if you are going to return datas. 150 | 151 | @return NSDictionary 152 | */ 153 | - (NSDictionary *)datas; 154 | 155 | /** 156 | This method will be called if the response object is a dictionary. 157 | 158 | @param dictionary NSDictionary 159 | */ 160 | - (void)didResponseDictionary:(NSDictionary *)dictionary; 161 | 162 | /** 163 | This method will be called if the response object is an array. 164 | 165 | @param array NSArray 166 | */ 167 | - (void)didResponseArray:(NSArray *)array; 168 | 169 | /** 170 | This method will be called if the response obejct is a string. 171 | 172 | @param string NSString 173 | */ 174 | - (void)didResponseString:(NSString *)string; 175 | 176 | /** 177 | This method will be called if the response object is NSData 178 | 179 | @param data NSData 180 | */ 181 | - (void)didResponseData:(NSData *)data; 182 | 183 | @end 184 | 185 | NS_ASSUME_NONNULL_END 186 | -------------------------------------------------------------------------------- /STNetTaskQueue/STHTTPNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STHTTPNetTask.m 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STHTTPNetTask.h" 10 | #import "STHTTPNetTaskParametersPacker.h" 11 | 12 | NSString *const STHTTPNetTaskServerError = @"STHTTPNetTaskServerError"; 13 | NSString *const STHTTPNetTaskResponseParsedError = @"STHTTPNetTaskResponseParsedError"; 14 | NSString *const STHTTPNetTaskErrorStatusCodeUserInfoKey = @"statusCode"; 15 | NSString *const STHTTPNetTaskErrorResponseDataUserInfoKey = @"responseData"; 16 | NSString *STHTTPNetTaskRequestObjectDefaultSeparator = @"_"; 17 | 18 | @interface STHTTPNetTask () 19 | 20 | @property (atomic, assign) NSInteger statusCode; 21 | @property (atomic, strong) NSDictionary *responseHeaders; 22 | 23 | @end 24 | 25 | @implementation STHTTPNetTask 26 | 27 | - (STHTTPNetTaskMethod)method 28 | { 29 | return STHTTPNetTaskGet; 30 | } 31 | 32 | - (STHTTPNetTaskRequestType)requestType 33 | { 34 | return STHTTPNetTaskRequestKeyValueString; 35 | } 36 | 37 | - (STHTTPNetTaskResponseType)responseType 38 | { 39 | return STHTTPNetTaskResponseJSON; 40 | } 41 | 42 | - (NSDictionary *)headers 43 | { 44 | return nil; 45 | } 46 | 47 | - (NSDictionary *)parameters 48 | { 49 | return nil; 50 | } 51 | 52 | - (NSDictionary *)datas 53 | { 54 | return nil; 55 | } 56 | 57 | - (void)didResponse:(id)response 58 | { 59 | if ([response isKindOfClass:[NSDictionary class]]) { 60 | [self didResponseDictionary:response]; 61 | } 62 | else if ([response isKindOfClass:[NSArray class]]) { 63 | [self didResponseArray:response]; 64 | } 65 | else if ([response isKindOfClass:[NSString class]]) { 66 | [self didResponseString:response]; 67 | } 68 | else if ([response isKindOfClass:[NSData class]]) { 69 | [self didResponseData:response]; 70 | } 71 | else { 72 | NSAssert(NO, @"Invalid response"); 73 | } 74 | } 75 | 76 | - (void)didResponseDictionary:(NSDictionary *)dictionary 77 | { 78 | 79 | } 80 | 81 | - (void)didResponseArray:(NSArray *)array 82 | { 83 | 84 | } 85 | 86 | - (void)didResponseString:(NSString *)string 87 | { 88 | 89 | } 90 | 91 | - (void)didResponseData:(NSData *)data 92 | { 93 | 94 | } 95 | 96 | - (NSArray *)ignoredProperties 97 | { 98 | return nil; 99 | } 100 | 101 | - (NSString *)description 102 | { 103 | NSDictionary *methodMap = @{ @(STHTTPNetTaskGet): @"GET", 104 | @(STHTTPNetTaskDelete): @"DELETE", 105 | @(STHTTPNetTaskHead): @"HEAD", 106 | @(STHTTPNetTaskPatch): @"PATCH", 107 | @(STHTTPNetTaskPost): @"POST", 108 | @(STHTTPNetTaskPut): @"PUT" }; 109 | NSDictionary *requestTypeMap = @{ @(STHTTPNetTaskRequestJSON): @"JSON", 110 | @(STHTTPNetTaskRequestKeyValueString): @"Key-Value String", 111 | @(STHTTPNetTaskRequestFormData): @"Form Data" }; 112 | NSDictionary *responseTypeMap = @{ @(STHTTPNetTaskResponseJSON): @"JSON", 113 | @(STHTTPNetTaskResponseString): @"String", 114 | @(STHTTPNetTaskResponseRawData): @"Raw Data" }; 115 | 116 | NSMutableString *desc = [NSMutableString new]; 117 | [desc appendFormat:@"URI: %@\n", self.uri]; 118 | [desc appendFormat:@"Method: %@\n", methodMap[@(self.method)]]; 119 | [desc appendFormat:@"Request Type: %@\n", requestTypeMap[@(self.requestType)]]; 120 | [desc appendFormat:@"Response Type: %@\n", responseTypeMap[@(self.responseType)]]; 121 | 122 | NSDictionary *headers = self.headers; 123 | if (headers.count) { 124 | [desc appendFormat:@"Custom Headers:\n%@\n", headers]; 125 | } 126 | NSDictionary *datas = self.datas; 127 | if (datas.count) { 128 | [desc appendFormat:@"Form Datas:\n"]; 129 | for (NSString *name in datas) { 130 | NSData *data = datas[name]; 131 | [desc appendFormat:@"%@: %td bytes\n", name, data.length]; 132 | } 133 | } 134 | 135 | [desc appendFormat:@"Parameters:\n%@\n", [[[STHTTPNetTaskParametersPacker alloc] initWithNetTask:self] pack]]; 136 | return desc; 137 | } 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /STNetTaskQueue/STHTTPNetTaskParametersPacker.h: -------------------------------------------------------------------------------- 1 | // 2 | // STHTTPNetTaskParametersPacker.h 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 6/9/15. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class STHTTPNetTask; 14 | 15 | @interface STHTTPNetTaskParametersPacker : NSObject 16 | 17 | - (instancetype)initWithNetTask:(STHTTPNetTask *)netTask; 18 | - (NSDictionary *)pack; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /STNetTaskQueue/STHTTPNetTaskParametersPacker.m: -------------------------------------------------------------------------------- 1 | // 2 | // STHTTPNetTaskParametersPacker.m 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 6/9/15. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STHTTPNetTaskParametersPacker.h" 10 | #import "STHTTPNetTask.h" 11 | #import 12 | 13 | @implementation STHTTPNetTaskParametersPacker 14 | { 15 | STHTTPNetTask *_netTask; 16 | } 17 | 18 | - (instancetype)initWithNetTask:(STHTTPNetTask *)netTask 19 | { 20 | if (self = [super init]) { 21 | _netTask = netTask; 22 | } 23 | return self; 24 | } 25 | 26 | - (NSDictionary *)pack 27 | { 28 | NSMutableDictionary *parameters = [NSMutableDictionary new]; 29 | 30 | // Pack parameters from net task 31 | [parameters addEntriesFromDictionary:[self parametersFromRequestObject:_netTask]]; 32 | // Pack additional parameters 33 | [parameters addEntriesFromDictionary:_netTask.parameters]; 34 | 35 | return [NSDictionary dictionaryWithDictionary:parameters]; 36 | } 37 | 38 | - (NSDictionary *)parametersFromRequestObject:(id)requestObject 39 | { 40 | if (!requestObject || ![requestObject isKindOfClass:[NSObject class]]) { 41 | return nil; 42 | } 43 | 44 | NSSet *ignoredProperties = [NSSet setWithArray:[requestObject ignoredProperties]]; 45 | if (ignoredProperties.count == 1 && [ignoredProperties.anyObject isEqualToString:@"*"]) { 46 | return nil; 47 | } 48 | NSMutableDictionary *parameters = [NSMutableDictionary new]; 49 | 50 | unsigned int numberOfProperties; 51 | objc_property_t *properties = class_copyPropertyList([requestObject class], &numberOfProperties); 52 | for (unsigned int i = 0; i < numberOfProperties; i++) { 53 | objc_property_t property = properties[i]; 54 | NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)]; 55 | NSArray *propertyAttributes = [[NSString stringWithUTF8String:property_getAttributes(property)] componentsSeparatedByString:@","]; 56 | 57 | if ([ignoredProperties containsObject:propertyName]) { 58 | continue; 59 | } 60 | 61 | if (![self shouldPackPropertyWithAttributes:propertyAttributes]) { 62 | continue; 63 | } 64 | 65 | NSString *separator = [self separatorFromRequestObject:requestObject]; 66 | NSString *parameterName = [self parameterNameFromName:propertyName withSeparator:separator]; 67 | id parameterValue = [self parameterValueFromValue:[(NSObject *)requestObject valueForKey:propertyName] inRequestObject:requestObject]; 68 | if (parameterName && parameterValue) { 69 | parameters[parameterName] = parameterValue; 70 | } 71 | } 72 | 73 | free(properties); 74 | 75 | return parameters; 76 | } 77 | 78 | - (NSDictionary *)parametersFromDictionary:(NSDictionary *)dictionary inRequestObject:(id)requestObject 79 | { 80 | NSString *separator = [self separatorFromRequestObject:requestObject]; 81 | NSMutableDictionary *parameters = [NSMutableDictionary new]; 82 | for (NSString *key in dictionary) { 83 | NSString *parameterName = [self parameterNameFromName:key withSeparator:separator]; 84 | id parameterValue = [self parameterValueFromValue:dictionary[key] inRequestObject:requestObject]; 85 | if (parameterName && parameterValue) { 86 | parameters[parameterName] = parameterValue; 87 | } 88 | } 89 | return parameters; 90 | } 91 | 92 | - (id)parameterValueFromValue:(id)value inRequestObject:(id)requestObject 93 | { 94 | if ([requestObject respondsToSelector:@selector(transformValue:)]) { 95 | id transformedValue = [requestObject transformValue:value]; 96 | if (transformedValue != value) { 97 | return transformedValue; 98 | } 99 | } 100 | if ([value conformsToProtocol:@protocol(STHTTPNetTaskRequestObject)]) { 101 | return [self parametersFromRequestObject:value]; 102 | } 103 | else if ([value isKindOfClass:[NSDictionary class]]) { 104 | return [self parametersFromDictionary:value inRequestObject:requestObject]; 105 | } 106 | else if ([value isKindOfClass:[NSNumber class]] || 107 | [value isKindOfClass:[NSString class]] || 108 | [value isKindOfClass:[NSArray class]]) { 109 | return value; 110 | } 111 | return nil; 112 | } 113 | 114 | - (NSString *)parameterNameFromName:(NSString *)name withSeparator:(NSString *)separator 115 | { 116 | if (!separator) { 117 | return name; 118 | } 119 | 120 | NSMutableString *parameterName = [NSMutableString new]; 121 | const char *chars = name.UTF8String; 122 | for (NSUInteger i = 0; i < name.length; i++) { 123 | BOOL hasPrevious = i != 0; 124 | BOOL hasNext = i + 1 < name.length; 125 | BOOL prependUnderscore = NO; 126 | char ch = chars[i]; 127 | if (isupper(ch)) { 128 | if (hasPrevious) { 129 | if (!isupper(chars[i - 1])) { 130 | prependUnderscore = YES; 131 | } 132 | } 133 | if(hasNext && !prependUnderscore) { 134 | if (!isupper(chars[i + 1])) { 135 | prependUnderscore = YES; 136 | } 137 | } 138 | ch = tolower(ch); 139 | } 140 | if (prependUnderscore) { 141 | [parameterName appendFormat:@"_%c", ch]; 142 | } 143 | else { 144 | [parameterName appendFormat:@"%c", ch]; 145 | } 146 | } 147 | 148 | return [NSString stringWithString:parameterName]; 149 | } 150 | 151 | - (NSString *)separatorFromRequestObject:(id)requestObject 152 | { 153 | NSString *separator = STHTTPNetTaskRequestObjectDefaultSeparator; 154 | if ([requestObject respondsToSelector:@selector(parameterNameSeparator)]) { 155 | separator = [requestObject parameterNameSeparator]; 156 | } 157 | return separator; 158 | } 159 | 160 | - (BOOL)shouldPackPropertyWithAttributes:(NSArray *)attributes 161 | { 162 | // Only pack non-readonly property and property which is not ignored. 163 | return ![attributes containsObject:@"R"] && [attributes[0] rangeOfString:NSStringFromProtocol(@protocol(STIgnore))].location == NSNotFound; 164 | } 165 | 166 | @end 167 | -------------------------------------------------------------------------------- /STNetTaskQueue/STHTTPNetTaskQueueHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // STHTTPNetTaskQueueHandler.h 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface STHTTPNetTaskQueueHandler : NSObject 14 | 15 | /** 16 | Init the handler with base URL, a base URL will be used for constructing the whole url for HTTP net tasks. 17 | E.g HTTP net task returns uri "user/profile", handled by handler with baseURL "http://example.com", the whole url will be http://example.com/user/profile". 18 | 19 | @param baseURL NSURL 20 | */ 21 | - (instancetype)initWithBaseURL:(nullable NSURL *)baseURL; 22 | 23 | /** 24 | Init the handler with baseURL and NSURLSessionConfiguration. 25 | 26 | @param baseURL NSURL 27 | */ 28 | - (instancetype)initWithBaseURL:(nullable NSURL *)baseURL configuration:(NSURLSessionConfiguration *)configuration; 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /STNetTaskQueue/STHTTPNetTaskQueueHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // STHTTPNetTaskQueueHandler.m 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STHTTPNetTaskQueueHandler.h" 10 | #import "STHTTPNetTask.h" 11 | #import "STHTTPNetTaskParametersPacker.h" 12 | #import "STNetTaskQueueLog.h" 13 | #import 14 | 15 | @interface STHTTPNetTask (STInternal) 16 | 17 | @property (atomic, assign) NSInteger statusCode; 18 | @property (atomic, strong) NSDictionary *responseHeaders; 19 | 20 | @end 21 | 22 | @class STHTTPNetTaskQueueHandlerOperation; 23 | 24 | @interface NSURLSessionTask (STHTTPNetTaskQueueHandlerOperation) 25 | 26 | @property (nonatomic, strong) STHTTPNetTaskQueueHandlerOperation *operation; 27 | 28 | @end 29 | 30 | @implementation NSURLSessionTask (STHTTPNetTaskQueueHandlerOperation) 31 | 32 | @dynamic operation; 33 | 34 | - (void)setOperation:(STHTTPNetTaskQueueHandlerOperation *)operation 35 | { 36 | objc_setAssociatedObject(self, @selector(operation), operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 37 | } 38 | 39 | - (STHTTPNetTaskQueueHandlerOperation *)operation 40 | { 41 | return objc_getAssociatedObject(self, @selector(operation)); 42 | } 43 | 44 | @end 45 | 46 | static NSDictionary *STHTTPNetTaskMethodMap; 47 | static NSDictionary *STHTTPNetTaskContentTypeMap; 48 | static NSString *STHTTPNetTaskFormDataBoundary; 49 | static NSMapTable *STHTTPNetTaskToSessionTask; 50 | 51 | @interface STHTTPNetTaskQueueHandlerOperation : NSObject 52 | 53 | @property (nonatomic, strong) STNetTaskQueue *queue; 54 | @property (nonatomic, strong) STHTTPNetTask *task; 55 | @property (nonatomic, strong) NSURLSession *session; 56 | @property (nonatomic, strong) NSURL *baseURL; 57 | 58 | - (void)start; 59 | 60 | @end 61 | 62 | @implementation STHTTPNetTaskQueueHandlerOperation 63 | { 64 | NSMutableData *_data; 65 | } 66 | 67 | + (void)load 68 | { 69 | STHTTPNetTaskMethodMap = @{ @(STHTTPNetTaskGet): @"GET", 70 | @(STHTTPNetTaskDelete): @"DELETE", 71 | @(STHTTPNetTaskHead): @"HEAD", 72 | @(STHTTPNetTaskPatch): @"PATCH", 73 | @(STHTTPNetTaskPost): @"POST", 74 | @(STHTTPNetTaskPut): @"PUT" }; 75 | STHTTPNetTaskContentTypeMap = @{ @(STHTTPNetTaskRequestJSON): @"application/json; charset=utf-8", 76 | @(STHTTPNetTaskRequestKeyValueString): @"application/x-www-form-urlencoded", 77 | @(STHTTPNetTaskRequestFormData): @"multipart/form-data" }; 78 | STHTTPNetTaskFormDataBoundary = [NSString stringWithFormat:@"ST-Boundary-%@", [[NSUUID UUID] UUIDString]]; 79 | STHTTPNetTaskToSessionTask = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsWeakMemory capacity:50]; 80 | } 81 | 82 | - (void)start 83 | { 84 | _data = [NSMutableData new]; 85 | 86 | NSDictionary *headers = self.task.headers; 87 | NSDictionary *parameters = [[[STHTTPNetTaskParametersPacker alloc] initWithNetTask:_task] pack]; 88 | 89 | NSURLSessionTask *sessionTask = nil; 90 | NSMutableURLRequest *request = [NSMutableURLRequest new]; 91 | request.HTTPMethod = STHTTPNetTaskMethodMap[@(_task.method)]; 92 | 93 | if (_baseURL.user.length || _baseURL.password.length) { 94 | NSString *credentials = [NSString stringWithFormat:@"%@:%@", _baseURL.user, _baseURL.password]; 95 | credentials = [[credentials dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:kNilOptions]; 96 | [request setValue:[NSString stringWithFormat:@"Basic %@", credentials] forHTTPHeaderField:@"Authorization"]; 97 | } 98 | 99 | switch (_task.method) { 100 | case STHTTPNetTaskGet: 101 | case STHTTPNetTaskHead: 102 | case STHTTPNetTaskDelete: { 103 | NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:[self requestURL] 104 | resolvingAgainstBaseURL:NO]; 105 | if (parameters.count) { 106 | urlComponents.query = [self queryStringFromParameters:parameters]; 107 | } 108 | request.URL = urlComponents.URL; 109 | } 110 | break; 111 | case STHTTPNetTaskPost: 112 | case STHTTPNetTaskPut: 113 | case STHTTPNetTaskPatch: { 114 | request.URL = [self requestURL]; 115 | NSDictionary *datas = _task.datas; 116 | if (_task.requestType != STHTTPNetTaskRequestFormData) { 117 | request.HTTPBody = [self bodyDataFromParameters:parameters requestType:_task.requestType]; 118 | [request setValue:STHTTPNetTaskContentTypeMap[@(_task.requestType)] forHTTPHeaderField:@"Content-Type"]; 119 | } 120 | else { 121 | request.HTTPBody = [self formDataFromParameters:parameters datas:datas]; 122 | NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", STHTTPNetTaskFormDataBoundary]; 123 | [request setValue:contentType forHTTPHeaderField: @"Content-Type"]; 124 | } 125 | } 126 | break; 127 | default: { 128 | NSAssert(NO, @"Invalid STHTTPNetTaskMethod"); 129 | } 130 | break; 131 | } 132 | 133 | for (NSString *headerField in headers) { 134 | [request setValue:headers[headerField] forHTTPHeaderField:headerField]; 135 | } 136 | sessionTask = [_session dataTaskWithRequest:request]; 137 | 138 | [STHTTPNetTaskToSessionTask setObject:sessionTask forKey:_task]; 139 | 140 | sessionTask.operation = self; 141 | [sessionTask resume]; 142 | } 143 | 144 | - (NSURL *)requestURL 145 | { 146 | if (_baseURL) { 147 | return [_baseURL URLByAppendingPathComponent:_task.uri]; 148 | } 149 | return [NSURL URLWithString:_task.uri]; 150 | } 151 | 152 | #pragma mark - NSURLSessionDataDelegate 153 | 154 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data 155 | { 156 | [_data appendData:data]; 157 | } 158 | 159 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 160 | { 161 | if (error && error.code == NSURLErrorCancelled) { 162 | return; 163 | } 164 | 165 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response; 166 | NSData *data = [NSData dataWithData:_data]; 167 | 168 | _task.statusCode = httpResponse.statusCode; 169 | _task.responseHeaders = httpResponse.allHeaderFields; 170 | 171 | if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) { 172 | id responseObj = nil; 173 | NSError *error = nil; 174 | switch (_task.responseType) { 175 | case STHTTPNetTaskResponseRawData: 176 | responseObj = data; 177 | break; 178 | case STHTTPNetTaskResponseString: 179 | responseObj = [self stringFromData:data]; 180 | break; 181 | case STHTTPNetTaskResponseJSON: 182 | default: 183 | responseObj = [self JSONFromData:data]; 184 | break; 185 | } 186 | 187 | if (!responseObj) { 188 | error = [NSError errorWithDomain:STHTTPNetTaskResponseParsedError 189 | code:0 190 | userInfo:@{ @"url": httpResponse.URL.absoluteString }]; 191 | } 192 | 193 | if (error) { 194 | [_queue task:_task didFailWithError:error]; 195 | } 196 | else { 197 | [_queue task:_task didResponse:responseObj]; 198 | } 199 | } 200 | else { 201 | if (!error) { // Response status code is not 20x 202 | #pragma GCC diagnostic push 203 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 204 | error = [NSError errorWithDomain:STHTTPNetTaskServerError 205 | code:0 206 | userInfo:@{ STHTTPNetTaskErrorStatusCodeUserInfoKey: @(httpResponse.statusCode), 207 | STHTTPNetTaskErrorResponseDataUserInfoKey: data }]; 208 | #pragma GCC diagnostic pop 209 | [STNetTaskQueueLog log:@"\n%@", _task.description]; 210 | } 211 | [_queue task:_task didFailWithError:error]; 212 | } 213 | } 214 | 215 | #pragma mark - Response data parsing methods 216 | 217 | - (NSString *)stringFromData:(NSData *)data 218 | { 219 | @try { 220 | NSString *string = data.length ? [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] : @""; 221 | return string; 222 | } 223 | @catch (NSException *exception) { 224 | [STNetTaskQueueLog log:@"String parsed error: %@", exception.debugDescription]; 225 | return nil; 226 | } 227 | } 228 | 229 | - (id)JSONFromData:(NSData *)data 230 | { 231 | NSError *error; 232 | id JSON = data.length ? [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error] : @{}; 233 | if (error) { 234 | [STNetTaskQueueLog log:@"JSON parsed error: %@", error.debugDescription]; 235 | return nil; 236 | } 237 | return JSON; 238 | } 239 | 240 | #pragma mark - Request data constructing methods 241 | 242 | - (NSString *)queryStringFromParameters:(NSDictionary *)parameters 243 | { 244 | if (!parameters.count) { 245 | return @""; 246 | } 247 | 248 | NSMutableString *queryString = [NSMutableString string]; 249 | [parameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { 250 | if ([value isKindOfClass:[NSArray class]]) { 251 | for (id element in value) { 252 | [self appendKeyValueToString:queryString withKey:key value:[element description] percentEncoding:NO]; 253 | } 254 | } 255 | else { 256 | [self appendKeyValueToString:queryString withKey:key value:[value description] percentEncoding:NO]; 257 | } 258 | }]; 259 | [queryString deleteCharactersInRange:NSMakeRange(queryString.length - 1, 1)]; 260 | return queryString; 261 | } 262 | 263 | - (NSData *)bodyDataFromParameters:(NSDictionary *)parameters requestType:(STHTTPNetTaskRequestType)requestType 264 | { 265 | if (!parameters.count) { 266 | return nil; 267 | } 268 | 269 | NSData *bodyData = nil; 270 | 271 | switch (requestType) { 272 | case STHTTPNetTaskRequestJSON: { 273 | NSError *error = nil; 274 | bodyData = [NSJSONSerialization dataWithJSONObject:parameters options:kNilOptions error:&error]; 275 | NSAssert(!error, @"Request is not in JSON format"); 276 | } 277 | break; 278 | case STHTTPNetTaskRequestKeyValueString: 279 | default: { 280 | NSMutableString *bodyString = [NSMutableString string]; 281 | [parameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { 282 | if ([value isKindOfClass:[NSArray class]]) { 283 | for (id element in value) { 284 | [self appendKeyValueToString:bodyString withKey:key value:[element description] percentEncoding:YES]; 285 | } 286 | } 287 | else { 288 | [self appendKeyValueToString:bodyString withKey:key value:[value description] percentEncoding:YES]; 289 | } 290 | }]; 291 | [bodyString deleteCharactersInRange:NSMakeRange(bodyString.length - 1, 1)]; 292 | bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding]; 293 | } 294 | break; 295 | } 296 | 297 | return bodyData; 298 | } 299 | 300 | - (NSData *)formDataFromParameters:(NSDictionary *)parameters datas:(NSDictionary *)datas 301 | { 302 | NSMutableData *formData = [NSMutableData data]; 303 | 304 | [parameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { 305 | if ([value isKindOfClass:[NSArray class]]) { 306 | for (id element in value) { 307 | [self appendToFormData:formData withKey:key value:[element description]]; 308 | } 309 | } 310 | else { 311 | [self appendToFormData:formData withKey:key value:[value description]]; 312 | } 313 | }]; 314 | 315 | [datas enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSData *fileData, BOOL *stop) { 316 | [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", STHTTPNetTaskFormDataBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; 317 | [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, key] dataUsingEncoding:NSUTF8StringEncoding]]; 318 | [formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", @"*/*"] dataUsingEncoding:NSUTF8StringEncoding]]; 319 | [formData appendData:fileData]; 320 | [formData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 321 | }]; 322 | 323 | [formData appendData:[[NSString stringWithFormat:@"--%@--\r\n", STHTTPNetTaskFormDataBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; 324 | 325 | return formData; 326 | } 327 | 328 | - (void)appendKeyValueToString:(NSMutableString *)string withKey:(NSString *)key value:(NSString *)value percentEncoding:(BOOL)percentEncoding 329 | { 330 | if (percentEncoding) { 331 | key = [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 332 | value = [value stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 333 | } 334 | [string appendFormat:@"%@=%@&", key, value]; 335 | } 336 | 337 | - (void)appendToFormData:(NSMutableData *)formData withKey:(NSString *)key value:(NSString *)value 338 | { 339 | [formData appendData:[[NSString stringWithFormat:@"--%@\r\n", STHTTPNetTaskFormDataBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; 340 | [formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; 341 | [formData appendData:[[NSString stringWithFormat:@"%@\r\n", value] dataUsingEncoding:NSUTF8StringEncoding]]; 342 | } 343 | 344 | @end 345 | 346 | @interface STHTTPNetTaskQueueHandler () 347 | 348 | @end 349 | 350 | @implementation STHTTPNetTaskQueueHandler 351 | { 352 | NSURL *_baseURL; 353 | NSURLSession *_urlSession; 354 | } 355 | 356 | - (instancetype)initWithBaseURL:(NSURL *)baseURL 357 | { 358 | return [self initWithBaseURL:baseURL configuration:[NSURLSessionConfiguration defaultSessionConfiguration]]; 359 | } 360 | 361 | - (instancetype)initWithBaseURL:(NSURL *)baseURL configuration:(NSURLSessionConfiguration *)configuration 362 | { 363 | if (self = [super init]) { 364 | _baseURL = baseURL; 365 | _urlSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; 366 | } 367 | return self; 368 | } 369 | 370 | #pragma mark - STNetTaskQueueHandler 371 | 372 | - (void)netTaskQueue:(STNetTaskQueue *)netTaskQueue handleTask:(STNetTask *)task 373 | { 374 | NSAssert([task isKindOfClass:[STHTTPNetTask class]], @"Net task should be subclass of STHTTPNetTask"); 375 | 376 | STHTTPNetTaskQueueHandlerOperation *operation = [STHTTPNetTaskQueueHandlerOperation new]; 377 | operation.queue = netTaskQueue; 378 | operation.task = (STHTTPNetTask *)task; 379 | operation.baseURL = _baseURL; 380 | operation.session = _urlSession; 381 | 382 | [operation start]; 383 | } 384 | 385 | - (void)netTaskQueue:(STNetTaskQueue *)netTaskQueue didCancelTask:(STNetTask *)task 386 | { 387 | NSAssert([task isKindOfClass:[STHTTPNetTask class]], @"Net task should be subclass of STHTTPNetTask"); 388 | 389 | NSURLSessionTask *sessionTask = [STHTTPNetTaskToSessionTask objectForKey:task]; 390 | [sessionTask cancel]; 391 | } 392 | 393 | - (void)netTaskQueueDidBecomeInactive:(STNetTaskQueue *)netTaskQueue 394 | { 395 | [_urlSession invalidateAndCancel]; 396 | } 397 | 398 | #pragma mark - NSURLSessionDataDelegate 399 | 400 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data 401 | { 402 | [dataTask.operation URLSession:session dataTask:dataTask didReceiveData:data]; 403 | } 404 | 405 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 406 | { 407 | [task.operation URLSession:session task:task didCompleteWithError:error]; 408 | } 409 | 410 | @end 411 | -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTask.h 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | FOUNDATION_EXPORT NSString *const STNetTaskUnknownError; 14 | 15 | #ifdef RACObserve 16 | 17 | #define STNetTaskObserve(TASK) \ 18 | [RACSignal createSignal:^RACDisposable *(id subscriber) { \ 19 | [[[[RACObserve(TASK, finished) skip:1] ignore:@NO] deliverOnMainThread] subscribeNext:^(id x) { \ 20 | [subscriber sendNext:TASK];\ 21 | [subscriber sendCompleted]; \ 22 | }]; \ 23 | [[[[RACObserve(TASK, cancelled) skip:1] ignore:@NO] deliverOnMainThread] subscribeNext:^(id x) { \ 24 | [subscriber sendError:nil];\ 25 | }]; \ 26 | return nil; \ 27 | }] 28 | 29 | #endif 30 | 31 | @class STNetTask; 32 | 33 | @protocol STNetTaskDelegate 34 | 35 | /** 36 | This delegate method will be called when the net task is finished(no matter it's successful or failed). 37 | If the net task is failed, task.error will be non-nil. 38 | 39 | @param task STNetTask The finished net task. 40 | */ 41 | - (void)netTaskDidEnd:(__kindof STNetTask *)task; 42 | 43 | @end 44 | 45 | typedef NS_ENUM(NSUInteger, STNetTaskState) { 46 | STNetTaskStateCancalled, 47 | STNetTaskStateFinished, 48 | STNetTaskStateRetrying 49 | }; 50 | 51 | typedef void (^STNetTaskSubscriptionBlock)(); 52 | 53 | @interface STNetTask : NSObject 54 | 55 | /** 56 | Error object which contains error message when net task is failed. 57 | */ 58 | @property (nullable, atomic, strong) NSError *error; 59 | 60 | /** 61 | Indicates if the net task is waiting for executing or executing. 62 | This value will be set to "YES" immediately after the net task is added to net task queue. 63 | */ 64 | @property (atomic, assign, readonly) BOOL pending; 65 | 66 | /** 67 | Indicates if the net task is cancelled. 68 | This value would be "NO" by default after net task is created, even the net task is not added to queue. 69 | */ 70 | @property (atomic, assign, readonly) BOOL cancelled; 71 | 72 | /** 73 | Indicates if the net task is finished(no matter it's successful or failed). 74 | */ 75 | @property (atomic, assign, readonly) BOOL finished; 76 | 77 | /** 78 | The current retry time @see maxRetryCount 79 | */ 80 | @property (atomic, assign, readonly) NSUInteger retryCount; 81 | 82 | /** 83 | A unique string represents the net task. 84 | 85 | @return NSString The uri string. 86 | */ 87 | - (NSString *)uri; 88 | 89 | /** 90 | A callback method which is called when the net task is finished successfully. 91 | Note: this method will be called in thread of STNetTaskQueue. 92 | 93 | @param response id The response object. 94 | */ 95 | - (void)didResponse:(id)response; 96 | 97 | /** 98 | A callback method which is called when the net task is failed. 99 | Note: this method will be called in thread of STNetTaskQueue. 100 | */ 101 | - (void)didFail; 102 | 103 | /** 104 | A callback method which is called when the net task is retried. 105 | Note: this method will be called in thread of STNetTaskQueue. 106 | */ 107 | - (void)didRetry; 108 | 109 | /** 110 | Indicates how many times the net task should be retried after failed. 111 | Default 0. 112 | 113 | @return NSUInteger 114 | */ 115 | - (NSUInteger)maxRetryCount; 116 | 117 | /** 118 | If you are going to retry the net task only when specific error is returned, return NO in this method. 119 | Default YES. 120 | 121 | @param error NSError Error object. 122 | @return BOOL Should the net task be retried according to the error object. 123 | */ 124 | - (BOOL)shouldRetryForError:(NSError *)error; 125 | 126 | /** 127 | Indicates how many seconds should be delayed before retrying the net task. 128 | 129 | @return NSTimeInterval 130 | */ 131 | - (NSTimeInterval)retryInterval; 132 | 133 | /** 134 | Subscribe state of net task by using block 135 | 136 | @param state STNetTaskState state of net task 137 | @param block STNetTaskSubscriptionBlock block is called when net task is in subscribed state. 138 | NOTE: this block will be called in main thread. 139 | */ 140 | - (void)subscribeState:(STNetTaskState)state usingBlock:(STNetTaskSubscriptionBlock)block; 141 | 142 | @end 143 | 144 | NS_ASSUME_NONNULL_END 145 | -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTask.m 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STNetTask.h" 10 | 11 | NSString *const STNetTaskUnknownError = @"STNetTaskUnknownError"; 12 | 13 | @interface STNetTask () 14 | 15 | @property (atomic, assign) BOOL pending; 16 | @property (atomic, assign) BOOL cancelled; 17 | @property (atomic, assign) BOOL finished; 18 | @property (atomic, assign) NSUInteger retryCount; 19 | @property (nonatomic, strong) NSMutableDictionary *> *stateToBlock; 20 | 21 | @end 22 | 23 | @implementation STNetTask 24 | 25 | - (NSString *)uri 26 | { 27 | return @""; 28 | } 29 | 30 | - (void)didResponse:(id)response 31 | { 32 | 33 | } 34 | 35 | - (void)didFail 36 | { 37 | 38 | } 39 | 40 | - (void)didRetry 41 | { 42 | 43 | } 44 | 45 | - (NSUInteger)maxRetryCount 46 | { 47 | return 0; 48 | } 49 | 50 | - (BOOL)shouldRetryForError:(NSError *)error 51 | { 52 | return YES; 53 | } 54 | 55 | - (NSTimeInterval)retryInterval 56 | { 57 | return 0; 58 | } 59 | 60 | - (void)subscribeState:(STNetTaskState)state usingBlock:(STNetTaskSubscriptionBlock)block 61 | { 62 | if ([NSThread isMainThread]) { 63 | [self _subscribeState:state usingBlock:block]; 64 | } 65 | else { 66 | dispatch_async(dispatch_get_main_queue(), ^{ 67 | [self _subscribeState:state usingBlock:block]; 68 | }); 69 | } 70 | } 71 | 72 | - (void)_subscribeState:(STNetTaskState)state usingBlock:(STNetTaskSubscriptionBlock)block 73 | { 74 | if (!self.stateToBlock) { 75 | self.stateToBlock = [NSMutableDictionary new]; 76 | } 77 | NSMutableArray *blocks = self.stateToBlock[@(state)]; 78 | if (!blocks) { 79 | blocks = [NSMutableArray new]; 80 | self.stateToBlock[@(state)] = blocks; 81 | } 82 | [blocks addObject:[block copy]]; 83 | } 84 | 85 | - (void)notifyState:(STNetTaskState)state 86 | { 87 | dispatch_async(dispatch_get_main_queue(), ^{ 88 | NSArray *blocks = self.stateToBlock[@(state)]; 89 | for (STNetTaskSubscriptionBlock block in blocks) { 90 | block(); 91 | } 92 | switch (state) { 93 | case STNetTaskStateFinished: 94 | case STNetTaskStateCancalled: { 95 | self.stateToBlock = nil; 96 | } 97 | break; 98 | default: 99 | break; 100 | } 101 | }); 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTaskChain.h: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTaskChain.h 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class STNetTaskQueue; 14 | @class STNetTask; 15 | @class STNetTaskChain; 16 | 17 | DEPRECATED_MSG_ATTRIBUTE("Use STNetTaskGroup instead") 18 | @protocol STNetTaskChainDelegate 19 | 20 | - (void)netTaskChainDidEnd:(STNetTaskChain *)netTaskChain; 21 | 22 | @end 23 | 24 | DEPRECATED_MSG_ATTRIBUTE("Use STNetTaskGroup instead") 25 | @interface STNetTaskChain : NSObject 26 | 27 | @property (nullable, nonatomic, weak) id delegate; 28 | @property (nullable, nonatomic, strong) STNetTaskQueue *queue; 29 | @property (nullable, nonatomic, strong, readonly) NSError *error; 30 | @property (nonatomic, assign, readonly) BOOL started; 31 | 32 | - (void)setTasks:(STNetTask *)task, ...; 33 | // Return NO indicates this task should not be sent. 34 | - (BOOL)onNextRequest:(STNetTask *)task; 35 | - (void)onNextResponse:(STNetTask *)task; 36 | - (void)start; 37 | - (void)cancel; 38 | 39 | @end 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTaskChain.m: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTaskChain.m 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STNetTaskChain.h" 10 | #import "STNetTask.h" 11 | #import "STNetTaskQueue.h" 12 | 13 | @interface STNetTaskChain() 14 | 15 | @property (nonatomic, strong) NSArray *allTasks; 16 | @property (nonatomic, assign) int taskIndex; 17 | 18 | @end 19 | 20 | @implementation STNetTaskChain 21 | 22 | - (void)setTasks:(STNetTask *)task, ... 23 | { 24 | NSMutableArray *tasks = [NSMutableArray array]; 25 | va_list args; 26 | va_start(args, task); 27 | STNetTask *nextTask = nil; 28 | for (nextTask = task; nextTask != nil; nextTask = va_arg(args, STNetTask *)) { 29 | [tasks addObject:nextTask]; 30 | [self.queue addTaskDelegate:self uri:nextTask.uri]; 31 | } 32 | va_end(args); 33 | self.allTasks = tasks; 34 | } 35 | 36 | - (BOOL)onNextRequest:(STNetTask *)task 37 | { 38 | return YES; 39 | } 40 | 41 | - (void)onNextResponse:(STNetTask *)task 42 | { 43 | 44 | } 45 | 46 | - (void)start 47 | { 48 | if (_started) { 49 | return; 50 | } 51 | _started = YES; 52 | _error = nil; 53 | self.taskIndex = 0; 54 | [self nextRequest]; 55 | } 56 | 57 | - (void)cancel 58 | { 59 | if (!_started) { 60 | return; 61 | } 62 | _started = NO; 63 | for (STNetTask *task in self.allTasks) { 64 | [self.queue cancelTask:task]; 65 | } 66 | } 67 | 68 | - (void)nextRequest 69 | { 70 | while (_started) { 71 | if (self.taskIndex >= self.allTasks.count) { 72 | _started = NO; 73 | [self.delegate netTaskChainDidEnd:self]; 74 | return; 75 | } 76 | STNetTask *task = [self.allTasks objectAtIndex:self.taskIndex]; 77 | self.taskIndex++; 78 | if ([self onNextRequest:task]) { 79 | [self.queue addTask:task]; 80 | return; 81 | } 82 | } 83 | } 84 | 85 | - (void)netTaskDidEnd:(STNetTask *)task 86 | { 87 | if (![self.allTasks containsObject:task]) { 88 | return; 89 | } 90 | 91 | if (task.error) { 92 | _error = task.error; 93 | [self.delegate netTaskChainDidEnd:self]; 94 | } 95 | else { 96 | [self onNextResponse:task]; 97 | [self nextRequest]; 98 | } 99 | } 100 | 101 | @end 102 | 103 | -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTaskGroup.h: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTaskGroup.h 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 8/5/16. 6 | // Copyright © 2016 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class STNetTask; 14 | @class STNetTaskQueue; 15 | @class STNetTaskGroup; 16 | 17 | typedef NS_ENUM(NSUInteger, STNetTaskGroupMode) { 18 | STNetTaskGroupModeSerial, 19 | STNetTaskGroupModeConcurrent 20 | }; 21 | 22 | typedef NS_ENUM(NSUInteger, STNetTaskGroupState) { 23 | STNetTaskGroupStateCancelled, 24 | STNetTaskGroupStateFinished 25 | }; 26 | 27 | /** 28 | @param group STNetTaskGroup 29 | @param error NSError the first error was encountered in the group. 30 | */ 31 | typedef void (^STNetTaskGroupSubscriptionBlock)(STNetTaskGroup *group, NSError * _Nullable error); 32 | 33 | /** 34 | STNetTaskGroup is a group to execute STNetTasks in serial or concurrent mode. 35 | NOTE: STNetTaskGroup is currently not thread safe. 36 | */ 37 | @interface STNetTaskGroup : NSObject 38 | 39 | /** 40 | The executing task in the group when it is in STNetTaskGroupModeSerial. 41 | It will be always 'nil' when the group is in STNetTaskGroupModeConcurrent. 42 | */ 43 | @property (nullable, nonatomic, strong, readonly) STNetTask *executingTask; 44 | 45 | /** 46 | All tasks in this group. 47 | */ 48 | @property (nonatomic, strong, readonly) NSArray *tasks; 49 | 50 | /** 51 | The STNetTaskGroupMode is being used. 52 | */ 53 | @property (nonatomic, assign, readonly) STNetTaskGroupMode mode; 54 | 55 | /** 56 | Indicates if the group is executing tasks. 57 | */ 58 | @property (nonatomic, assign, readonly) BOOL pending; 59 | 60 | /** 61 | Init with an array of net tasks and mode. 62 | [STNetTaskQueue sharedQueue] will be used for executing tasks in the group. 63 | 64 | @param tasks NSArray 65 | @param mode STNetTaskGroupMode indicates the tasks in this group should be sent serially or concurrently. 66 | */ 67 | - (instancetype)initWithTasks:(NSArray *)tasks mode:(STNetTaskGroupMode)mode; 68 | 69 | /** 70 | Init with an array of net tasks, mode and the queue which will be used for executing tasks. 71 | 72 | @param tasks NSArray 73 | @param mode STNetTaskGroupMode indicates the tasks in this group should be sent serially or concurrently. 74 | @param queue STNetTaskQueue the queue which is used for executing tasks in the group. 75 | */ 76 | - (instancetype)initWithTasks:(NSArray *)tasks mode:(STNetTaskGroupMode)mode queue:(STNetTaskQueue *)queue NS_DESIGNATED_INITIALIZER; 77 | 78 | - (instancetype)init NS_UNAVAILABLE; 79 | 80 | /** 81 | Add a new task to this group. 82 | 83 | @param task STNetTask 84 | */ 85 | - (void)addTask:(STNetTask *)task; 86 | 87 | /** 88 | Subscribe state with STNetTaskGroupSubscriptionBlock. 89 | 90 | @param state STNetTaskGroupState 91 | @param block STNetTaskGroupSubscriptionBlock 92 | */ 93 | - (STNetTaskGroup *)subscribeState:(STNetTaskGroupState)state usingBlock:(STNetTaskGroupSubscriptionBlock)block; 94 | 95 | /** 96 | Start executing tasks in this group. 97 | */ 98 | - (void)start; 99 | 100 | /** 101 | Cancel all tasks in this group. 102 | */ 103 | - (void)cancel; 104 | 105 | @end 106 | 107 | /** 108 | Handy category for executing tasks in an array. 109 | */ 110 | @interface NSArray (STNetTaskGroup) 111 | 112 | - (STNetTaskGroup *)serialNetTaskGroup; 113 | - (STNetTaskGroup *)serialNetTaskGroupInQueue:(STNetTaskQueue *)queue; 114 | - (STNetTaskGroup *)concurrentNetTaskGroup; 115 | - (STNetTaskGroup *)concurrentNetTaskGroupInQueue:(STNetTaskQueue *)queue; 116 | 117 | @end 118 | 119 | NS_ASSUME_NONNULL_END 120 | -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTaskGroup.m: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTaskGroup.m 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 8/5/16. 6 | // Copyright © 2016 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STNetTaskGroup.h" 10 | #import "STNetTaskQueue.h" 11 | 12 | static NSMutableSet *_retainedGroups; // Retain group to make sure it won't be autoreleased. 13 | 14 | @interface STNetTaskGroup () 15 | 16 | @property (nonatomic, strong) STNetTask *executingTask; 17 | @property (nonatomic, strong) NSArray *tasks; 18 | @property (nonatomic, assign) BOOL pending; 19 | @property (nonatomic, assign) BOOL started; 20 | @property (nonatomic, strong) NSMutableDictionary *> *stateToBlock; 21 | @property (nonatomic, strong) STNetTaskSubscriptionBlock taskSubscriptionBlock; // For serial mode 22 | @property (nonatomic, strong, readonly) STNetTaskQueue *queue; 23 | 24 | @end 25 | 26 | @implementation STNetTaskGroup 27 | 28 | - (instancetype)initWithTasks:(NSArray *)tasks mode:(STNetTaskGroupMode)mode 29 | { 30 | return [self initWithTasks:tasks mode:mode queue:[STNetTaskQueue sharedQueue]]; 31 | } 32 | 33 | - (instancetype)initWithTasks:(NSArray *)tasks mode:(STNetTaskGroupMode)mode queue:(STNetTaskQueue *)queue 34 | { 35 | if (self = [super init]) { 36 | self.tasks = [NSArray arrayWithArray:tasks]; 37 | _mode = mode; 38 | _queue = queue; 39 | } 40 | return self; 41 | } 42 | 43 | - (void)addTask:(STNetTask *)task 44 | { 45 | NSMutableArray *tasks = [_tasks mutableCopy]; 46 | [tasks addObject:task]; 47 | self.tasks = [NSArray arrayWithArray:tasks]; 48 | } 49 | 50 | - (STNetTaskGroup *)subscribeState:(STNetTaskGroupState)state usingBlock:(STNetTaskGroupSubscriptionBlock)block 51 | { 52 | if (!self.stateToBlock) { 53 | self.stateToBlock = [NSMutableDictionary new]; 54 | } 55 | NSMutableArray *blocks = self.stateToBlock[@(state)]; 56 | if (!blocks) { 57 | blocks = [NSMutableArray new]; 58 | self.stateToBlock[@(state)] = blocks; 59 | } 60 | [blocks addObject:[block copy]]; 61 | return self; 62 | } 63 | 64 | - (void)notifyState:(STNetTaskGroupState)state withError:(NSError *)error 65 | { 66 | NSMutableArray *blocks = self.stateToBlock[@(state)]; 67 | for (STNetTaskGroupSubscriptionBlock block in blocks) { 68 | block(self, error); 69 | } 70 | self.stateToBlock = nil; 71 | self.taskSubscriptionBlock = nil; 72 | [_retainedGroups removeObject:self]; 73 | } 74 | 75 | - (void)start 76 | { 77 | NSAssert(!self.started, @"STNetTaskGroup can not be reused, please create a new instance."); 78 | if (self.pending) { 79 | return; 80 | } 81 | self.pending = YES; 82 | self.started = YES; 83 | if (!_retainedGroups) { 84 | _retainedGroups = [NSMutableSet new]; 85 | } 86 | [_retainedGroups addObject:self]; 87 | 88 | switch (self.mode) { 89 | case STNetTaskGroupModeSerial: { 90 | __block NSUInteger executingTaskIndex = 0; 91 | __weak STNetTaskGroup *weakSelf = self; 92 | self.taskSubscriptionBlock = ^{ 93 | if (weakSelf.executingTask.error) { 94 | [weakSelf notifyState:STNetTaskGroupStateFinished withError:weakSelf.executingTask.error]; 95 | return; 96 | } 97 | executingTaskIndex++; 98 | if (executingTaskIndex == weakSelf.tasks.count) { 99 | [weakSelf notifyState:STNetTaskGroupStateFinished withError:nil]; 100 | } 101 | else { 102 | weakSelf.executingTask = weakSelf.tasks[executingTaskIndex]; 103 | [weakSelf.queue addTask:weakSelf.executingTask]; 104 | [weakSelf.executingTask subscribeState:STNetTaskStateFinished usingBlock:weakSelf.taskSubscriptionBlock]; 105 | } 106 | }; 107 | self.executingTask = self.tasks[executingTaskIndex]; 108 | [self.queue addTask:self.executingTask]; 109 | [self.executingTask subscribeState:STNetTaskStateFinished usingBlock:self.taskSubscriptionBlock]; 110 | } 111 | break; 112 | case STNetTaskGroupModeConcurrent: { 113 | __block NSUInteger finishedTasksCount = 0; 114 | for (STNetTask *task in self.tasks) { 115 | [self.queue addTask:task]; 116 | [task subscribeState:STNetTaskStateFinished usingBlock:^{ 117 | if (task.error) { 118 | [self cancelTasks]; 119 | [self notifyState:STNetTaskGroupStateFinished withError:task.error]; 120 | return; 121 | } 122 | finishedTasksCount++; 123 | if (finishedTasksCount == self.tasks.count) { 124 | [self notifyState:STNetTaskGroupStateFinished withError:nil]; 125 | } 126 | }]; 127 | } 128 | } 129 | break; 130 | default: 131 | break; 132 | } 133 | } 134 | 135 | - (void)cancel 136 | { 137 | if (!self.pending) { 138 | return; 139 | } 140 | 141 | switch (self.mode) { 142 | case STNetTaskGroupModeSerial: { 143 | [self.queue cancelTask:self.executingTask]; 144 | self.executingTask = nil; 145 | } 146 | break; 147 | case STNetTaskGroupModeConcurrent: { 148 | [self cancelTasks]; 149 | } 150 | break; 151 | default: 152 | break; 153 | } 154 | [self notifyState:STNetTaskGroupStateCancelled withError:nil]; 155 | } 156 | 157 | - (void)cancelTasks 158 | { 159 | for (STNetTask *task in self.tasks) { 160 | if (task.pending) { 161 | [self.queue cancelTask:task]; 162 | } 163 | } 164 | } 165 | 166 | @end 167 | 168 | @implementation NSArray (STNetTaskGroup) 169 | 170 | - (STNetTaskGroup *)serialNetTaskGroup 171 | { 172 | return [[STNetTaskGroup alloc] initWithTasks:self mode:STNetTaskGroupModeSerial]; 173 | } 174 | 175 | - (STNetTaskGroup *)serialNetTaskGroupInQueue:(STNetTaskQueue *)queue 176 | { 177 | return [[STNetTaskGroup alloc] initWithTasks:self mode:STNetTaskGroupModeSerial queue:queue]; 178 | } 179 | 180 | - (STNetTaskGroup *)concurrentNetTaskGroup 181 | { 182 | return [[STNetTaskGroup alloc] initWithTasks:self mode:STNetTaskGroupModeConcurrent]; 183 | } 184 | 185 | - (STNetTaskGroup *)concurrentNetTaskGroupInQueue:(STNetTaskQueue *)queue 186 | { 187 | return [[STNetTaskGroup alloc] initWithTasks:self mode:STNetTaskGroupModeConcurrent queue:queue]; 188 | } 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTaskQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTaskQueue.h 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class STNetTask; 14 | @protocol STNetTaskDelegate; 15 | 16 | //! Project version number for STNetTaskQueue. 17 | FOUNDATION_EXPORT double STNetTaskQueueVersionNumber; 18 | 19 | //! Project version string for STNetTaskQueue. 20 | FOUNDATION_EXPORT const unsigned char STNetTaskQueueVersionString[]; 21 | 22 | @class STNetTaskQueue; 23 | 24 | @protocol STNetTaskQueueHandler 25 | 26 | /** 27 | STNetTaskQueue will call this method when a net task is added to queue and become ready to be excecuted. 28 | 29 | @param netTaskQueue STNetTaskQueue The net task queue which is holding the net task. 30 | @param task STNetTask The net task which is ready to be executed. 31 | */ 32 | - (void)netTaskQueue:(STNetTaskQueue *)netTaskQueue handleTask:(STNetTask *)task; 33 | 34 | /** 35 | STNetTaskQueue will call this method when a net task is cancelled and removed from net task queue. 36 | Giving a chance to the handler to do a clean up for the cancelled net task. 37 | 38 | @param netTaskQueue STNetTaskQueue The net task queue which is holding the cancelled net task. 39 | @param task STNetTask The net task which is cancelled and removed from net task queue. 40 | */ 41 | - (void)netTaskQueue:(STNetTaskQueue *)netTaskQueue didCancelTask:(STNetTask *)task; 42 | 43 | /** 44 | STNetTaskQueue will call this method when the net task queue is deallocated. 45 | 46 | @param netTaskQueue STNetTaskQueue The net task queue which is deallocated. 47 | */ 48 | - (void)netTaskQueueDidBecomeInactive:(STNetTaskQueue *)netTaskQueue; 49 | 50 | @end 51 | 52 | @interface STNetTaskQueue : NSObject 53 | 54 | /** 55 | The STNetTaskQueueHandler which is used for handling the net tasks in queue. 56 | */ 57 | @property (nullable, nonatomic, strong) id handler; 58 | 59 | /** 60 | Count of Max concurrent task in a queue. 61 | If the number of unfinished tasks in queue hits the max count, upcoming task will be processed till one of the unfinished task is done. 62 | */ 63 | @property (nonatomic, assign) NSUInteger maxConcurrentTasksCount; 64 | 65 | /** 66 | A shared STNetTaskQueue instance. 67 | */ 68 | + (instancetype)sharedQueue; 69 | 70 | /** 71 | Add a net task into the net task queue. 72 | The net task may not be executed immediately depends on the "maxConcurrentTasksCount", 73 | but the net task will be marked as "pending" anyway. 74 | 75 | @param task STNetTask The net task to be added into the queue. 76 | */ 77 | - (void)addTask:(STNetTask *)task; 78 | 79 | /** 80 | Cancel and remove the net task from queue. 81 | If the net task is executing, it will be cancelled and remove from the queue without calling the "netTaskDidEnd" delegate method. 82 | 83 | @param task STNetTask The net task to be cancelled and removed from the queue. 84 | */ 85 | - (void)cancelTask:(STNetTask *)task; 86 | 87 | /** 88 | This method should be called when the "handler" finish handling the net task successfully. 89 | After "handler" called this method, the net task will be marked as "finished", set "pending" as "NO", and removed from the queue. 90 | 91 | @param task STNetTask The net task which is handled by "handler". 92 | @param response id The response object. 93 | */ 94 | - (void)task:(STNetTask *)task didResponse:(id)response; 95 | 96 | /** 97 | This method should be caled when the "handler" finish handling the net task with error. 98 | After "hadnler" called this method, the net task will be marked as "finished", set "pending" as "NO", and removed from the queue. 99 | 100 | @param task STNetTask The net task which is handled by "handler". 101 | @param error NSError Error object. 102 | */ 103 | - (void)task:(STNetTask *)task didFailWithError:(NSError *)error; 104 | 105 | /** 106 | Add a net task delegate to "STNetTaskQueue" with uri of the net task, 107 | it's a weak reference and duplicated delegate with same uri will be ignored. 108 | 109 | @param delegate id 110 | @param uri NSString A unique string returned by STNetTask. 111 | */ 112 | - (void)addTaskDelegate:(id)delegate uri:(NSString *)uri; 113 | 114 | /** 115 | Add a net task delegate to "STNetTaskQueue" with class of net task, 116 | it's a weak reference and duplicated delegate with same class will be ignored. 117 | 118 | @param delegate id 119 | @param class Class Class which extends STNetTask. 120 | */ 121 | - (void)addTaskDelegate:(id)delegate class:(Class)clazz; 122 | 123 | /** 124 | Most of the times you don't need to remove net task delegate explicitly, 125 | because "STNetTaskQueue" holds weak reference of each delegate. 126 | 127 | @param delegate id 128 | @param uri NSString 129 | */ 130 | - (void)removeTaskDelegate:(id)delegate uri:(NSString *)uri; 131 | - (void)removeTaskDelegate:(id)delegate class:(Class)clazz; 132 | - (void)removeTaskDelegate:(id)delegate; 133 | 134 | @end 135 | 136 | NS_ASSUME_NONNULL_END 137 | 138 | #import 139 | #import 140 | #import 141 | #import 142 | -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTaskQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTaskQueue.m 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 29/11/14. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STNetTaskQueue.h" 10 | #import "STNetTaskQueueLog.h" 11 | 12 | @interface STNetTask (STInternal) 13 | 14 | @property (atomic, assign) BOOL pending; 15 | @property (atomic, assign) BOOL cancelled; 16 | @property (atomic, assign) BOOL finished; 17 | @property (atomic, assign) NSUInteger retryCount; 18 | 19 | - (void)notifyState:(STNetTaskState)state; 20 | 21 | @end 22 | 23 | @interface STNetTaskQueue() 24 | 25 | @property (nonatomic, strong) NSThread *thread; 26 | @property (nonatomic, strong) NSRecursiveLock *lock; 27 | @property (nonatomic, strong) NSMutableDictionary *taskDelegates; // > 28 | @property (nonatomic, strong) NSMutableArray *tasks; // 29 | @property (nonatomic, strong) NSMutableArray *waitingTasks; // 30 | 31 | @end 32 | 33 | @implementation STNetTaskQueue 34 | 35 | + (instancetype)sharedQueue 36 | { 37 | static STNetTaskQueue *sharedQueue; 38 | static dispatch_once_t onceToken; 39 | dispatch_once(&onceToken, ^{ 40 | sharedQueue = [self new]; 41 | }); 42 | return sharedQueue; 43 | } 44 | 45 | - (id)init 46 | { 47 | if (self = [super init]) { 48 | self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEntryPoint) object:nil]; 49 | self.thread.name = NSStringFromClass(self.class); 50 | [self.thread start]; 51 | self.lock = [NSRecursiveLock new]; 52 | self.lock.name = [NSString stringWithFormat:@"%@Lock", NSStringFromClass(self.class)]; 53 | self.taskDelegates = [NSMutableDictionary new]; 54 | self.tasks = [NSMutableArray new]; 55 | self.waitingTasks = [NSMutableArray new]; 56 | } 57 | return self; 58 | } 59 | 60 | - (void)dealloc 61 | { 62 | [self.handler netTaskQueueDidBecomeInactive:self]; 63 | } 64 | 65 | - (void)threadEntryPoint 66 | { 67 | @autoreleasepool { 68 | NSRunLoop *runloop = [NSRunLoop currentRunLoop]; 69 | [runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; // Just for keeping the runloop 70 | [runloop run]; 71 | } 72 | } 73 | 74 | - (void)performInThread:(NSThread *)thread usingBlock:(void(^)())block 75 | { 76 | [self performSelector:@selector(performUsingBlock:) onThread:thread withObject:block waitUntilDone:NO]; 77 | } 78 | 79 | - (void)performUsingBlock:(void(^)())block 80 | { 81 | block(); 82 | } 83 | 84 | - (void)addTask:(STNetTask *)task 85 | { 86 | NSAssert(self.handler, @"STNetTaskQueueHandler is not set."); 87 | NSAssert(!task.finished && !task.cancelled, @"STNetTask is finished/cancelled, please recreate a net task."); 88 | 89 | task.pending = YES; 90 | [self performInThread:self.thread usingBlock:^{ 91 | [self _addTask:task]; 92 | }]; 93 | } 94 | 95 | - (void)_addTask:(STNetTask *)task 96 | { 97 | if (self.maxConcurrentTasksCount > 0 && self.tasks.count >= self.maxConcurrentTasksCount) { 98 | [self.waitingTasks addObject:task]; 99 | return; 100 | } 101 | 102 | [self.tasks addObject:task]; 103 | [self.handler netTaskQueue:self handleTask:task]; 104 | } 105 | 106 | - (void)cancelTask:(STNetTask *)task 107 | { 108 | if (!task) { 109 | return; 110 | } 111 | 112 | [self performInThread:self.thread usingBlock:^{ 113 | [self _cancelTask:task]; 114 | }]; 115 | } 116 | 117 | - (void)_cancelTask:(STNetTask *)task 118 | { 119 | [self.tasks removeObject:task]; 120 | [self.waitingTasks removeObject:task]; 121 | task.pending = NO; 122 | 123 | [self.handler netTaskQueue:self didCancelTask:task]; 124 | task.cancelled = YES; 125 | [task notifyState:STNetTaskStateCancalled]; 126 | } 127 | 128 | - (BOOL)_retryTask:(STNetTask *)task withError:(NSError *)error 129 | { 130 | if ([task shouldRetryForError:error] && task.retryCount < task.maxRetryCount) { 131 | task.retryCount++; 132 | [self performSelector:@selector(_retryTask:) withObject:task afterDelay:task.retryInterval]; 133 | return YES; 134 | } 135 | return NO; 136 | } 137 | 138 | - (void)_retryTask:(STNetTask *)task 139 | { 140 | if (!task.cancelled) { 141 | [task didRetry]; 142 | [task notifyState:STNetTaskStateRetrying]; 143 | [self addTask:task]; 144 | } 145 | } 146 | 147 | - (void)_sendwaitingTasks 148 | { 149 | if (!self.waitingTasks.count) { 150 | return; 151 | } 152 | STNetTask *task = self.waitingTasks.firstObject; 153 | [self.waitingTasks removeObjectAtIndex:0]; 154 | [self addTask:task]; 155 | } 156 | 157 | - (void)task:(STNetTask *)task didResponse:(id)response 158 | { 159 | [self performInThread:self.thread usingBlock:^{ 160 | [self _task:task didResponse:response]; 161 | }]; 162 | } 163 | 164 | - (void)_task:(STNetTask *)task didResponse:(id)response 165 | { 166 | if (![self.tasks containsObject:task]) { 167 | return; 168 | } 169 | [self.tasks removeObject:task]; 170 | 171 | @try { 172 | [task didResponse:response]; 173 | } 174 | @catch (NSException *exception) { 175 | [STNetTaskQueueLog log:@"Exception in 'didResponse' - %@", exception.debugDescription]; 176 | NSError *error = [NSError errorWithDomain:STNetTaskUnknownError 177 | code:-1 178 | userInfo:@{ @"msg": exception.description ? : @"nil" }]; 179 | 180 | if ([self _retryTask:task withError:error]) { 181 | return; 182 | } 183 | 184 | task.error = error; 185 | [task didFail]; 186 | } 187 | 188 | task.pending = NO; 189 | task.finished = YES; 190 | [task notifyState:STNetTaskStateFinished]; 191 | 192 | [self _netTaskDidEnd:task]; 193 | 194 | [self _sendwaitingTasks]; 195 | } 196 | 197 | - (void)task:(STNetTask *)task didFailWithError:(NSError *)error 198 | { 199 | [self performInThread:self.thread usingBlock:^{ 200 | [self _task:task didFailWithError:error]; 201 | }]; 202 | } 203 | 204 | - (void)_task:(STNetTask *)task didFailWithError:(NSError *)error 205 | { 206 | if (![self.tasks containsObject:task]) { 207 | return; 208 | } 209 | [self.tasks removeObject:task]; 210 | 211 | [STNetTaskQueueLog log:error.debugDescription]; 212 | 213 | if ([self _retryTask:task withError:error]) { 214 | return; 215 | } 216 | 217 | task.error = error; 218 | [task didFail]; 219 | task.pending = NO; 220 | task.finished = YES; 221 | [task notifyState:STNetTaskStateFinished]; 222 | 223 | [self _netTaskDidEnd:task]; 224 | 225 | [self _sendwaitingTasks]; 226 | } 227 | 228 | - (void)_netTaskDidEnd:(STNetTask *)task 229 | { 230 | [self.lock lock]; 231 | 232 | NSHashTable *delegatesForURI = self.taskDelegates[task.uri]; 233 | NSHashTable *delegatesForClass = self.taskDelegates[NSStringFromClass(task.class)]; 234 | NSMutableSet *set = [NSMutableSet new]; 235 | [set addObjectsFromArray:delegatesForURI.allObjects]; 236 | [set addObjectsFromArray:delegatesForClass.allObjects]; 237 | NSArray *delegates = set.allObjects; 238 | 239 | [self.lock unlock]; 240 | 241 | if (delegates.count) { 242 | dispatch_async(dispatch_get_main_queue(), ^ { 243 | for (id delegate in delegates) { 244 | [delegate netTaskDidEnd:task]; 245 | } 246 | }); 247 | } 248 | } 249 | 250 | - (void)addTaskDelegate:(id)delegate uri:(NSString *)uri 251 | { 252 | [self.lock lock]; 253 | 254 | NSHashTable *delegates = self.taskDelegates[uri]; 255 | if (!delegates) { 256 | delegates = [NSHashTable weakObjectsHashTable]; 257 | self.taskDelegates[uri] = delegates; 258 | } 259 | [delegates addObject:delegate]; 260 | 261 | [self.lock unlock]; 262 | } 263 | 264 | - (void)addTaskDelegate:(id)delegate class:(Class)clazz 265 | { 266 | NSString *className = NSStringFromClass(clazz); 267 | NSAssert([clazz isSubclassOfClass:[STNetTask class]], @"%@ should be a subclass of STNetTask", className); 268 | 269 | [self.lock lock]; 270 | 271 | NSHashTable *delegates = self.taskDelegates[className]; 272 | if (!delegates) { 273 | delegates = [NSHashTable weakObjectsHashTable]; 274 | self.taskDelegates[className] = delegates; 275 | } 276 | [delegates addObject:delegate]; 277 | 278 | [self.lock unlock]; 279 | } 280 | 281 | - (void)removeTaskDelegate:(id)delegate 282 | { 283 | [self.lock lock]; 284 | 285 | for (NSString *key in self.taskDelegates) { 286 | [self removeTaskDelegate:delegate key:key]; 287 | } 288 | 289 | [self.lock unlock]; 290 | } 291 | 292 | - (void)removeTaskDelegate:(id)delegate uri:(NSString *)uri 293 | { 294 | [self removeTaskDelegate:delegate key:uri]; 295 | } 296 | 297 | - (void)removeTaskDelegate:(id)delegate class:(Class)clazz 298 | { 299 | [self removeTaskDelegate:delegate key:NSStringFromClass(clazz)]; 300 | } 301 | 302 | - (void)removeTaskDelegate:(id)delegate key:(NSString *)key 303 | { 304 | [self.lock lock]; 305 | 306 | NSHashTable *delegates = self.taskDelegates[key]; 307 | [delegates removeObject:delegate]; 308 | 309 | [self.lock unlock]; 310 | } 311 | 312 | @end -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTaskQueueLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTaskQueueLog.h 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 6/9/15. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface STNetTaskQueueLog : NSObject 14 | 15 | + (void)log:(NSString *)content, ...; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /STNetTaskQueue/STNetTaskQueueLog.m: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTaskQueueLog.m 3 | // STNetTaskQueue 4 | // 5 | // Created by Kevin Lin on 6/9/15. 6 | // Copyright (c) 2014 Sth4Me. All rights reserved. 7 | // 8 | 9 | #import "STNetTaskQueueLog.h" 10 | 11 | @implementation STNetTaskQueueLog 12 | 13 | + (void)log:(NSString *)content, ... 14 | { 15 | #ifdef DEBUG 16 | if (!content) { 17 | return; 18 | } 19 | va_list args; 20 | va_start(args, content); 21 | NSLogv([NSString stringWithFormat:@"[STNetTaskQueue] %@", content], args); 22 | va_end(args); 23 | #endif 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0A054A7A1C3F758A00534A9A /* STTestPackerNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A054A791C3F758A00534A9A /* STTestPackerNetTask.m */; }; 11 | 76586ED01B6CC698007D29E9 /* STTestMaxConcurrentTasksCountNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 76586ECF1B6CC698007D29E9 /* STTestMaxConcurrentTasksCountNetTask.m */; }; 12 | 765FF89B1B5B565E00DE637E /* STTestGetNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 765FF89A1B5B565E00DE637E /* STTestGetNetTask.m */; }; 13 | 765FF89F1B5B763000DE637E /* STTestPostNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 765FF89E1B5B763000DE637E /* STTestPostNetTask.m */; }; 14 | 765FF8A21B5B7B3100DE637E /* STTestPutNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 765FF8A11B5B7B3100DE637E /* STTestPutNetTask.m */; }; 15 | 765FF8AE1B5B840B00DE637E /* STTestPatchNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 765FF8AD1B5B840B00DE637E /* STTestPatchNetTask.m */; }; 16 | 765FF8B41B5B854A00DE637E /* STTestDeleteNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 765FF8B31B5B854A00DE637E /* STTestDeleteNetTask.m */; }; 17 | 7665028E1B554C9600B4F63D /* STNetTaskQueueTestHTTP.m in Sources */ = {isa = PBXBuildFile; fileRef = 7665028D1B554C9600B4F63D /* STNetTaskQueueTestHTTP.m */; }; 18 | 766502C11B55541F00B4F63D /* STTestRetryNetTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 766502C01B55541F00B4F63D /* STTestRetryNetTask.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 76012C811C26609D0090BE94 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 76012C7D1C26609D0090BE94 /* STNetTaskQueue.xcodeproj */; 25 | proxyType = 2; 26 | remoteGlobalIDString = 76012C551C265A160090BE94; 27 | remoteInfo = STNetTaskQueue; 28 | }; 29 | 76012C831C2660AC0090BE94 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 76012C7D1C26609D0090BE94 /* STNetTaskQueue.xcodeproj */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 76012C541C265A160090BE94; 34 | remoteInfo = STNetTaskQueue; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 0A054A781C3F758A00534A9A /* STTestPackerNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STTestPackerNetTask.h; sourceTree = ""; }; 40 | 0A054A791C3F758A00534A9A /* STTestPackerNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STTestPackerNetTask.m; sourceTree = ""; }; 41 | 76012C7D1C26609D0090BE94 /* STNetTaskQueue.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = STNetTaskQueue.xcodeproj; path = ../STNetTaskQueue.xcodeproj; sourceTree = ""; }; 42 | 76586ECE1B6CC698007D29E9 /* STTestMaxConcurrentTasksCountNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STTestMaxConcurrentTasksCountNetTask.h; sourceTree = ""; }; 43 | 76586ECF1B6CC698007D29E9 /* STTestMaxConcurrentTasksCountNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STTestMaxConcurrentTasksCountNetTask.m; sourceTree = ""; }; 44 | 765FF8991B5B565E00DE637E /* STTestGetNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STTestGetNetTask.h; sourceTree = ""; }; 45 | 765FF89A1B5B565E00DE637E /* STTestGetNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STTestGetNetTask.m; sourceTree = ""; }; 46 | 765FF89D1B5B763000DE637E /* STTestPostNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STTestPostNetTask.h; sourceTree = ""; }; 47 | 765FF89E1B5B763000DE637E /* STTestPostNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STTestPostNetTask.m; sourceTree = ""; }; 48 | 765FF8A01B5B7B3100DE637E /* STTestPutNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STTestPutNetTask.h; sourceTree = ""; }; 49 | 765FF8A11B5B7B3100DE637E /* STTestPutNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STTestPutNetTask.m; sourceTree = ""; }; 50 | 765FF8AC1B5B840B00DE637E /* STTestPatchNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STTestPatchNetTask.h; sourceTree = ""; }; 51 | 765FF8AD1B5B840B00DE637E /* STTestPatchNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STTestPatchNetTask.m; sourceTree = ""; }; 52 | 765FF8B21B5B854A00DE637E /* STTestDeleteNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STTestDeleteNetTask.h; sourceTree = ""; }; 53 | 765FF8B31B5B854A00DE637E /* STTestDeleteNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STTestDeleteNetTask.m; sourceTree = ""; }; 54 | 766502881B554C9600B4F63D /* STNetTaskQueueTest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = STNetTaskQueueTest.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 7665028C1B554C9600B4F63D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 7665028D1B554C9600B4F63D /* STNetTaskQueueTestHTTP.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STNetTaskQueueTestHTTP.m; sourceTree = ""; }; 57 | 766502BF1B55541F00B4F63D /* STTestRetryNetTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STTestRetryNetTask.h; sourceTree = ""; }; 58 | 766502C01B55541F00B4F63D /* STTestRetryNetTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STTestRetryNetTask.m; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 766502851B554C9600B4F63D /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | 76012C7E1C26609D0090BE94 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 76012C821C26609D0090BE94 /* STNetTaskQueue.framework */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | 765FF8981B5B562000DE637E /* NetTasks */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 765FF89C1B5B58E500DE637E /* HTTP */, 84 | ); 85 | name = NetTasks; 86 | sourceTree = ""; 87 | }; 88 | 765FF89C1B5B58E500DE637E /* HTTP */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 766502BF1B55541F00B4F63D /* STTestRetryNetTask.h */, 92 | 766502C01B55541F00B4F63D /* STTestRetryNetTask.m */, 93 | 765FF8991B5B565E00DE637E /* STTestGetNetTask.h */, 94 | 765FF89A1B5B565E00DE637E /* STTestGetNetTask.m */, 95 | 765FF89D1B5B763000DE637E /* STTestPostNetTask.h */, 96 | 765FF89E1B5B763000DE637E /* STTestPostNetTask.m */, 97 | 765FF8A01B5B7B3100DE637E /* STTestPutNetTask.h */, 98 | 765FF8A11B5B7B3100DE637E /* STTestPutNetTask.m */, 99 | 765FF8AC1B5B840B00DE637E /* STTestPatchNetTask.h */, 100 | 765FF8AD1B5B840B00DE637E /* STTestPatchNetTask.m */, 101 | 765FF8B21B5B854A00DE637E /* STTestDeleteNetTask.h */, 102 | 765FF8B31B5B854A00DE637E /* STTestDeleteNetTask.m */, 103 | 76586ECE1B6CC698007D29E9 /* STTestMaxConcurrentTasksCountNetTask.h */, 104 | 76586ECF1B6CC698007D29E9 /* STTestMaxConcurrentTasksCountNetTask.m */, 105 | 0A054A781C3F758A00534A9A /* STTestPackerNetTask.h */, 106 | 0A054A791C3F758A00534A9A /* STTestPackerNetTask.m */, 107 | ); 108 | name = HTTP; 109 | sourceTree = ""; 110 | }; 111 | 7665027D1B554C6700B4F63D = { 112 | isa = PBXGroup; 113 | children = ( 114 | 76012C7D1C26609D0090BE94 /* STNetTaskQueue.xcodeproj */, 115 | 7665028A1B554C9600B4F63D /* STNetTaskQueueTest */, 116 | 766502891B554C9600B4F63D /* Products */, 117 | ); 118 | sourceTree = ""; 119 | }; 120 | 766502891B554C9600B4F63D /* Products */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 766502881B554C9600B4F63D /* STNetTaskQueueTest.xctest */, 124 | ); 125 | name = Products; 126 | sourceTree = ""; 127 | }; 128 | 7665028A1B554C9600B4F63D /* STNetTaskQueueTest */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 7665028B1B554C9600B4F63D /* Supporting Files */, 132 | 765FF8981B5B562000DE637E /* NetTasks */, 133 | 7665028D1B554C9600B4F63D /* STNetTaskQueueTestHTTP.m */, 134 | ); 135 | path = STNetTaskQueueTest; 136 | sourceTree = ""; 137 | }; 138 | 7665028B1B554C9600B4F63D /* Supporting Files */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 7665028C1B554C9600B4F63D /* Info.plist */, 142 | ); 143 | name = "Supporting Files"; 144 | sourceTree = ""; 145 | }; 146 | /* End PBXGroup section */ 147 | 148 | /* Begin PBXNativeTarget section */ 149 | 766502871B554C9600B4F63D /* STNetTaskQueueTest */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = 7665028F1B554C9600B4F63D /* Build configuration list for PBXNativeTarget "STNetTaskQueueTest" */; 152 | buildPhases = ( 153 | 766502841B554C9600B4F63D /* Sources */, 154 | 766502851B554C9600B4F63D /* Frameworks */, 155 | 766502861B554C9600B4F63D /* Resources */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | 76012C841C2660AC0090BE94 /* PBXTargetDependency */, 161 | ); 162 | name = STNetTaskQueueTest; 163 | productName = STNetTaskQueueTest; 164 | productReference = 766502881B554C9600B4F63D /* STNetTaskQueueTest.xctest */; 165 | productType = "com.apple.product-type.bundle.unit-test"; 166 | }; 167 | /* End PBXNativeTarget section */ 168 | 169 | /* Begin PBXProject section */ 170 | 7665027E1B554C6700B4F63D /* Project object */ = { 171 | isa = PBXProject; 172 | attributes = { 173 | LastUpgradeCheck = 0800; 174 | TargetAttributes = { 175 | 766502871B554C9600B4F63D = { 176 | CreatedOnToolsVersion = 6.4; 177 | }; 178 | }; 179 | }; 180 | buildConfigurationList = 766502811B554C6700B4F63D /* Build configuration list for PBXProject "STNetTaskQueueTest" */; 181 | compatibilityVersion = "Xcode 3.2"; 182 | developmentRegion = English; 183 | hasScannedForEncodings = 0; 184 | knownRegions = ( 185 | en, 186 | ); 187 | mainGroup = 7665027D1B554C6700B4F63D; 188 | productRefGroup = 766502891B554C9600B4F63D /* Products */; 189 | projectDirPath = ""; 190 | projectReferences = ( 191 | { 192 | ProductGroup = 76012C7E1C26609D0090BE94 /* Products */; 193 | ProjectRef = 76012C7D1C26609D0090BE94 /* STNetTaskQueue.xcodeproj */; 194 | }, 195 | ); 196 | projectRoot = ""; 197 | targets = ( 198 | 766502871B554C9600B4F63D /* STNetTaskQueueTest */, 199 | ); 200 | }; 201 | /* End PBXProject section */ 202 | 203 | /* Begin PBXReferenceProxy section */ 204 | 76012C821C26609D0090BE94 /* STNetTaskQueue.framework */ = { 205 | isa = PBXReferenceProxy; 206 | fileType = wrapper.framework; 207 | path = STNetTaskQueue.framework; 208 | remoteRef = 76012C811C26609D0090BE94 /* PBXContainerItemProxy */; 209 | sourceTree = BUILT_PRODUCTS_DIR; 210 | }; 211 | /* End PBXReferenceProxy section */ 212 | 213 | /* Begin PBXResourcesBuildPhase section */ 214 | 766502861B554C9600B4F63D /* Resources */ = { 215 | isa = PBXResourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXResourcesBuildPhase section */ 222 | 223 | /* Begin PBXSourcesBuildPhase section */ 224 | 766502841B554C9600B4F63D /* Sources */ = { 225 | isa = PBXSourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | 0A054A7A1C3F758A00534A9A /* STTestPackerNetTask.m in Sources */, 229 | 765FF8B41B5B854A00DE637E /* STTestDeleteNetTask.m in Sources */, 230 | 7665028E1B554C9600B4F63D /* STNetTaskQueueTestHTTP.m in Sources */, 231 | 765FF8A21B5B7B3100DE637E /* STTestPutNetTask.m in Sources */, 232 | 766502C11B55541F00B4F63D /* STTestRetryNetTask.m in Sources */, 233 | 765FF89B1B5B565E00DE637E /* STTestGetNetTask.m in Sources */, 234 | 765FF89F1B5B763000DE637E /* STTestPostNetTask.m in Sources */, 235 | 76586ED01B6CC698007D29E9 /* STTestMaxConcurrentTasksCountNetTask.m in Sources */, 236 | 765FF8AE1B5B840B00DE637E /* STTestPatchNetTask.m in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXSourcesBuildPhase section */ 241 | 242 | /* Begin PBXTargetDependency section */ 243 | 76012C841C2660AC0090BE94 /* PBXTargetDependency */ = { 244 | isa = PBXTargetDependency; 245 | name = STNetTaskQueue; 246 | targetProxy = 76012C831C2660AC0090BE94 /* PBXContainerItemProxy */; 247 | }; 248 | /* End PBXTargetDependency section */ 249 | 250 | /* Begin XCBuildConfiguration section */ 251 | 766502821B554C6700B4F63D /* Debug */ = { 252 | isa = XCBuildConfiguration; 253 | buildSettings = { 254 | CLANG_WARN_BOOL_CONVERSION = YES; 255 | CLANG_WARN_CONSTANT_CONVERSION = YES; 256 | CLANG_WARN_EMPTY_BODY = YES; 257 | CLANG_WARN_ENUM_CONVERSION = YES; 258 | CLANG_WARN_INFINITE_RECURSION = YES; 259 | CLANG_WARN_INT_CONVERSION = YES; 260 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 261 | CLANG_WARN_UNREACHABLE_CODE = YES; 262 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 263 | ENABLE_STRICT_OBJC_MSGSEND = YES; 264 | ENABLE_TESTABILITY = YES; 265 | GCC_NO_COMMON_BLOCKS = YES; 266 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 267 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 268 | GCC_WARN_UNDECLARED_SELECTOR = YES; 269 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 270 | GCC_WARN_UNUSED_FUNCTION = YES; 271 | GCC_WARN_UNUSED_VARIABLE = YES; 272 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 273 | ONLY_ACTIVE_ARCH = YES; 274 | }; 275 | name = Debug; 276 | }; 277 | 766502831B554C6700B4F63D /* Release */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | CLANG_WARN_BOOL_CONVERSION = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_EMPTY_BODY = YES; 283 | CLANG_WARN_ENUM_CONVERSION = YES; 284 | CLANG_WARN_INFINITE_RECURSION = YES; 285 | CLANG_WARN_INT_CONVERSION = YES; 286 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | GCC_NO_COMMON_BLOCKS = YES; 291 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 292 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 293 | GCC_WARN_UNDECLARED_SELECTOR = YES; 294 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 295 | GCC_WARN_UNUSED_FUNCTION = YES; 296 | GCC_WARN_UNUSED_VARIABLE = YES; 297 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 298 | }; 299 | name = Release; 300 | }; 301 | 766502901B554C9600B4F63D /* Debug */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 306 | CLANG_CXX_LIBRARY = "libc++"; 307 | CLANG_ENABLE_MODULES = YES; 308 | CLANG_ENABLE_OBJC_ARC = YES; 309 | CLANG_WARN_BOOL_CONVERSION = YES; 310 | CLANG_WARN_CONSTANT_CONVERSION = YES; 311 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 312 | CLANG_WARN_EMPTY_BODY = YES; 313 | CLANG_WARN_ENUM_CONVERSION = YES; 314 | CLANG_WARN_INT_CONVERSION = YES; 315 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 316 | CLANG_WARN_UNREACHABLE_CODE = YES; 317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 318 | COPY_PHASE_STRIP = NO; 319 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 320 | ENABLE_STRICT_OBJC_MSGSEND = YES; 321 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 322 | GCC_C_LANGUAGE_STANDARD = gnu99; 323 | GCC_DYNAMIC_NO_PIC = NO; 324 | GCC_NO_COMMON_BLOCKS = YES; 325 | GCC_OPTIMIZATION_LEVEL = 0; 326 | GCC_PREPROCESSOR_DEFINITIONS = ( 327 | "DEBUG=1", 328 | "$(inherited)", 329 | ); 330 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 331 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 332 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 333 | GCC_WARN_UNDECLARED_SELECTOR = YES; 334 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 335 | GCC_WARN_UNUSED_FUNCTION = YES; 336 | GCC_WARN_UNUSED_VARIABLE = YES; 337 | INFOPLIST_FILE = STNetTaskQueueTest/Info.plist; 338 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 339 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 340 | MTL_ENABLE_DEBUG_INFO = YES; 341 | ONLY_ACTIVE_ARCH = YES; 342 | PRODUCT_BUNDLE_IDENTIFIER = "me.sth4.$(PRODUCT_NAME:rfc1034identifier)"; 343 | PRODUCT_NAME = "$(TARGET_NAME)"; 344 | SDKROOT = iphoneos; 345 | }; 346 | name = Debug; 347 | }; 348 | 766502911B554C9600B4F63D /* Release */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | ALWAYS_SEARCH_USER_PATHS = NO; 352 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 353 | CLANG_CXX_LIBRARY = "libc++"; 354 | CLANG_ENABLE_MODULES = YES; 355 | CLANG_ENABLE_OBJC_ARC = YES; 356 | CLANG_WARN_BOOL_CONVERSION = YES; 357 | CLANG_WARN_CONSTANT_CONVERSION = YES; 358 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 359 | CLANG_WARN_EMPTY_BODY = YES; 360 | CLANG_WARN_ENUM_CONVERSION = YES; 361 | CLANG_WARN_INT_CONVERSION = YES; 362 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 363 | CLANG_WARN_UNREACHABLE_CODE = YES; 364 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 365 | COPY_PHASE_STRIP = NO; 366 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 367 | ENABLE_NS_ASSERTIONS = NO; 368 | ENABLE_STRICT_OBJC_MSGSEND = YES; 369 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 370 | GCC_C_LANGUAGE_STANDARD = gnu99; 371 | GCC_NO_COMMON_BLOCKS = YES; 372 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 373 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 374 | GCC_WARN_UNDECLARED_SELECTOR = YES; 375 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 376 | GCC_WARN_UNUSED_FUNCTION = YES; 377 | GCC_WARN_UNUSED_VARIABLE = YES; 378 | INFOPLIST_FILE = STNetTaskQueueTest/Info.plist; 379 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 380 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 381 | MTL_ENABLE_DEBUG_INFO = NO; 382 | PRODUCT_BUNDLE_IDENTIFIER = "me.sth4.$(PRODUCT_NAME:rfc1034identifier)"; 383 | PRODUCT_NAME = "$(TARGET_NAME)"; 384 | SDKROOT = iphoneos; 385 | VALIDATE_PRODUCT = YES; 386 | }; 387 | name = Release; 388 | }; 389 | /* End XCBuildConfiguration section */ 390 | 391 | /* Begin XCConfigurationList section */ 392 | 766502811B554C6700B4F63D /* Build configuration list for PBXProject "STNetTaskQueueTest" */ = { 393 | isa = XCConfigurationList; 394 | buildConfigurations = ( 395 | 766502821B554C6700B4F63D /* Debug */, 396 | 766502831B554C6700B4F63D /* Release */, 397 | ); 398 | defaultConfigurationIsVisible = 0; 399 | defaultConfigurationName = Release; 400 | }; 401 | 7665028F1B554C9600B4F63D /* Build configuration list for PBXNativeTarget "STNetTaskQueueTest" */ = { 402 | isa = XCConfigurationList; 403 | buildConfigurations = ( 404 | 766502901B554C9600B4F63D /* Debug */, 405 | 766502911B554C9600B4F63D /* Release */, 406 | ); 407 | defaultConfigurationIsVisible = 0; 408 | defaultConfigurationName = Release; 409 | }; 410 | /* End XCConfigurationList section */ 411 | }; 412 | rootObject = 7665027E1B554C6700B4F63D /* Project object */; 413 | } 414 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest.xcodeproj/xcshareddata/xcschemes/STNetTaskQueueTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STNetTaskQueueTestHTTP.m: -------------------------------------------------------------------------------- 1 | // 2 | // STNetTaskQueueTestHTTP.m 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 14/7/15. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import "STTestRetryNetTask.h" 12 | #import "STTestGetNetTask.h" 13 | #import "STTestPostNetTask.h" 14 | #import "STTestPutNetTask.h" 15 | #import "STTestPatchNetTask.h" 16 | #import "STTestDeleteNetTask.h" 17 | #import "STTestMaxConcurrentTasksCountNetTask.h" 18 | #import "STTestPackerNetTask.h" 19 | 20 | @interface STNetTaskQueueTestHTTP : XCTestCase 21 | 22 | @end 23 | 24 | @implementation STNetTaskQueueTestHTTP 25 | { 26 | XCTestExpectation *_expectation; 27 | } 28 | 29 | - (void)setUp 30 | { 31 | [super setUp]; 32 | } 33 | 34 | - (void)tearDown 35 | { 36 | [super tearDown]; 37 | _expectation = nil; 38 | } 39 | 40 | - (void)setUpNetTaskQueueWithBaseURLString:(NSString *)baseURLString 41 | { 42 | STHTTPNetTaskQueueHandler *httpHandler = [[STHTTPNetTaskQueueHandler alloc] initWithBaseURL:[NSURL URLWithString:baseURLString]]; 43 | [STNetTaskQueue sharedQueue].handler = httpHandler; 44 | } 45 | 46 | - (void)testRetryNetTask 47 | { 48 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 49 | 50 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 51 | 52 | STTestRetryNetTask *testRetryTask = [STTestRetryNetTask new]; 53 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self uri:testRetryTask.uri]; 54 | [[STNetTaskQueue sharedQueue] addTask:testRetryTask]; 55 | 56 | [self waitForExpectationsWithTimeout:20 handler:nil]; 57 | } 58 | 59 | - (void)testCancelNetTask 60 | { 61 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 62 | 63 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 64 | 65 | STTestRetryNetTask *testRetryTask = [STTestRetryNetTask new]; 66 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self uri:testRetryTask.uri]; 67 | [[STNetTaskQueue sharedQueue] addTask:testRetryTask]; 68 | 69 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 70 | [[STNetTaskQueue sharedQueue] cancelTask:testRetryTask]; 71 | [_expectation fulfill]; 72 | }); 73 | 74 | [self waitForExpectationsWithTimeout:10 handler:nil]; 75 | } 76 | 77 | - (void)testGetNetTask 78 | { 79 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 80 | 81 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 82 | 83 | STTestGetNetTask *testGetTask = [STTestGetNetTask new]; 84 | testGetTask.id = 1; 85 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self uri:testGetTask.uri]; 86 | [[STNetTaskQueue sharedQueue] addTask:testGetTask]; 87 | 88 | [self waitForExpectationsWithTimeout:10 handler:nil]; 89 | } 90 | 91 | - (void)testPostNetTask 92 | { 93 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 94 | 95 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 96 | 97 | STTestPostNetTask *testPostTask = [STTestPostNetTask new]; 98 | testPostTask.title = @"Test Post Net Task Title"; 99 | testPostTask.body = @"Test Post Net Task Body"; 100 | testPostTask.userId = 1; 101 | testPostTask.date = [NSDate new]; 102 | testPostTask.ignored = @"test"; 103 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self uri:testPostTask.uri]; 104 | [[STNetTaskQueue sharedQueue] addTask:testPostTask]; 105 | 106 | [self waitForExpectationsWithTimeout:10 handler:nil]; 107 | } 108 | 109 | - (void)testPutNetTask 110 | { 111 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 112 | 113 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 114 | 115 | STTestPutNetTask *testPutTask = [STTestPutNetTask new]; 116 | testPutTask.id = 1; 117 | testPutTask.title = @"Test Put Net Task Title"; 118 | testPutTask.body = @"Test Put Net Task Body"; 119 | testPutTask.userId = 1; 120 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self uri:testPutTask.uri]; 121 | [[STNetTaskQueue sharedQueue] addTask:testPutTask]; 122 | 123 | [self waitForExpectationsWithTimeout:10 handler:nil]; 124 | } 125 | 126 | - (void)testPatchNetTask 127 | { 128 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 129 | 130 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 131 | 132 | STTestPatchNetTask *testPatchTask = [STTestPatchNetTask new]; 133 | testPatchTask.id = 1; 134 | testPatchTask.title = @"Test Patch Net Task Title"; 135 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self uri:testPatchTask.uri]; 136 | [[STNetTaskQueue sharedQueue] addTask:testPatchTask]; 137 | 138 | [self waitForExpectationsWithTimeout:10 handler:nil]; 139 | } 140 | 141 | - (void)testDeleteNetTask 142 | { 143 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 144 | 145 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 146 | 147 | STTestDeleteNetTask *testDeleteTask = [STTestDeleteNetTask new]; 148 | testDeleteTask.id = 1; 149 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self uri:testDeleteTask.uri]; 150 | [[STNetTaskQueue sharedQueue] addTask:testDeleteTask]; 151 | 152 | [self waitForExpectationsWithTimeout:10 handler:nil]; 153 | } 154 | 155 | - (void)testMaxConcurrentTasksCountTask 156 | { 157 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 158 | [STNetTaskQueue sharedQueue].maxConcurrentTasksCount = 1; 159 | 160 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 161 | 162 | STTestMaxConcurrentTasksCountNetTask *task1 = [STTestMaxConcurrentTasksCountNetTask new]; 163 | task1.id = 1; 164 | 165 | STTestMaxConcurrentTasksCountNetTask *task2 = [STTestMaxConcurrentTasksCountNetTask new]; 166 | task2.id = 2; 167 | // Should be sent out after task1 is finished 168 | 169 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self uri:task1.uri]; 170 | [[STNetTaskQueue sharedQueue] addTask:task1]; 171 | [[STNetTaskQueue sharedQueue] addTask:task2]; 172 | 173 | [self waitForExpectationsWithTimeout:20 handler:nil]; 174 | } 175 | 176 | - (void)testTaskDelegateForClass 177 | { 178 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 179 | 180 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 181 | 182 | STTestGetNetTask *testGetTask = [STTestGetNetTask new]; 183 | testGetTask.id = 1; 184 | [[STNetTaskQueue sharedQueue] addTaskDelegate:self class:testGetTask.class]; 185 | [[STNetTaskQueue sharedQueue] addTask:testGetTask]; 186 | 187 | [self waitForExpectationsWithTimeout:10 handler:nil]; 188 | } 189 | 190 | - (void)testSubscriptionBlock 191 | { 192 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 193 | 194 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 195 | 196 | STTestGetNetTask *testGetTask = [STTestGetNetTask new]; 197 | testGetTask.id = 1; 198 | [[STNetTaskQueue sharedQueue] addTask:testGetTask]; 199 | 200 | __block NSUInteger subscriptionCalled = 0; 201 | [testGetTask subscribeState:STNetTaskStateFinished usingBlock:^{ 202 | subscriptionCalled++; 203 | }]; 204 | [testGetTask subscribeState:STNetTaskStateFinished usingBlock:^{ 205 | subscriptionCalled++; 206 | if (testGetTask.finished && subscriptionCalled == 2) { 207 | [_expectation fulfill]; 208 | } 209 | else { 210 | XCTFail(@"%@ failed", _expectation.description); 211 | } 212 | }]; 213 | 214 | [self waitForExpectationsWithTimeout:10 handler:nil]; 215 | } 216 | 217 | - (void)testPackerTask 218 | { 219 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 220 | 221 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 222 | 223 | STTestPackerNetTask *testPackerTask = [STTestPackerNetTask new]; 224 | testPackerTask.string = @"String Value"; 225 | testPackerTask.date = [NSDate new]; 226 | testPackerTask.dictionary = @{ @"dictionaryKey1": @"dictionaryValue1", 227 | @"dictionaryKey2": @"dictionaryValue2", 228 | @"dictionaryKey3": @"dictionaryValue3" }; 229 | testPackerTask.array = @[ @"arrayValue1", @"arrayValue2", @"arrayValue3" ]; 230 | [[STNetTaskQueue sharedQueue] addTask:testPackerTask]; 231 | 232 | [testPackerTask subscribeState:STNetTaskStateFinished usingBlock:^{ 233 | if (testPackerTask.error) { 234 | XCTFail(@"%@ failed", _expectation.description); 235 | return; 236 | } 237 | 238 | if ([testPackerTask.post[@"string"] isEqualToString:testPackerTask.string] && 239 | [testPackerTask.post[@"date"] doubleValue] == testPackerTask.date.timeIntervalSince1970 && 240 | [testPackerTask.post[@"dictionary"][@"dictionary_key1"] isEqualToString:testPackerTask.dictionary[@"dictionaryKey1"]] && 241 | [testPackerTask.post[@"array"] isEqualToString:[testPackerTask.array componentsJoinedByString:@","]]) { 242 | [_expectation fulfill]; 243 | } 244 | else { 245 | XCTFail(@"%@ failed", _expectation.description); 246 | } 247 | }]; 248 | 249 | [self waitForExpectationsWithTimeout:10 handler:nil]; 250 | } 251 | 252 | - (void)testNonErrorSerialNetTaskGroup 253 | { 254 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 255 | [self testNonErrorNetTaskGroupWithMode:STNetTaskGroupModeSerial]; 256 | } 257 | 258 | - (void)testErrorSerialNetTaskGroup 259 | { 260 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 261 | [self testErrorNetTaskGroupWithMode:STNetTaskGroupModeSerial]; 262 | } 263 | 264 | - (void)testNonErrorConcurrentNetTaskGroup 265 | { 266 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 267 | [self testNonErrorNetTaskGroupWithMode:STNetTaskGroupModeConcurrent]; 268 | } 269 | 270 | - (void)testErrorConcurrentNetTaskGroup 271 | { 272 | _expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; 273 | [self testErrorNetTaskGroupWithMode:STNetTaskGroupModeConcurrent]; 274 | } 275 | 276 | - (void)testNonErrorNetTaskGroupWithMode:(STNetTaskGroupMode)mode 277 | { 278 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 279 | 280 | STTestGetNetTask *task1 = [STTestGetNetTask new]; 281 | task1.id = 1; 282 | 283 | STTestGetNetTask *task2 = [STTestGetNetTask new]; 284 | task2.id = 2; 285 | 286 | STNetTaskGroup *group = [[STNetTaskGroup alloc] initWithTasks:@[ task1, task2 ] mode:mode]; 287 | [group subscribeState:STNetTaskGroupStateFinished usingBlock:^(STNetTaskGroup *group, NSError *error) { 288 | if (error) { 289 | XCTFail(@"%@ failed", _expectation.description); 290 | return; 291 | } 292 | 293 | if ([task1.post[@"id"] intValue] == task1.id && [task2.post[@"id"] intValue] == task2.id) { 294 | [_expectation fulfill]; 295 | } 296 | else { 297 | XCTFail(@"%@ failed", _expectation.description); 298 | } 299 | }]; 300 | [group start]; 301 | 302 | [self waitForExpectationsWithTimeout:20 handler:nil]; 303 | } 304 | 305 | - (void)testErrorNetTaskGroupWithMode:(STNetTaskGroupMode)mode 306 | { 307 | [self setUpNetTaskQueueWithBaseURLString:@"http://jsonplaceholder.typicode.com"]; 308 | 309 | STTestGetNetTask *task1 = [STTestGetNetTask new]; 310 | task1.id = 1; 311 | 312 | STTestRetryNetTask *task2 = [STTestRetryNetTask new]; 313 | 314 | STNetTaskGroup *group = [[STNetTaskGroup alloc] initWithTasks:@[ task1, task2 ] mode:mode]; 315 | [group subscribeState:STNetTaskGroupStateFinished usingBlock:^(STNetTaskGroup *group, NSError *error) { 316 | if (error && error == task2.error) { 317 | [_expectation fulfill]; 318 | } 319 | else { 320 | XCTFail(@"%@ failed", _expectation.description); 321 | } 322 | 323 | }]; 324 | [group start]; 325 | 326 | [self waitForExpectationsWithTimeout:20 handler:nil]; 327 | } 328 | 329 | - (void)netTaskDidEnd:(STNetTask *)task 330 | { 331 | if (!_expectation) { 332 | return; 333 | } 334 | if ([task isKindOfClass:[STTestRetryNetTask class]]) { 335 | [_expectation fulfill]; 336 | if (!task.error || task.retryCount != task.maxRetryCount) { 337 | XCTFail(@"%@ failed", _expectation.description); 338 | } 339 | } 340 | else if ([task isKindOfClass:[STTestGetNetTask class]]) { 341 | [_expectation fulfill]; 342 | STTestGetNetTask *testGetTask = (STTestGetNetTask *)task; 343 | if (task.error || [testGetTask.post[@"id"] intValue] != testGetTask.id) { 344 | XCTFail(@"%@ failed", _expectation.description); 345 | } 346 | } 347 | else if ([task isKindOfClass:[STTestPostNetTask class]]) { 348 | [_expectation fulfill]; 349 | STTestPostNetTask *testPostTask = (STTestPostNetTask *)task; 350 | if (task.error || 351 | ![testPostTask.post[@"title"] isEqualToString:testPostTask.title] || 352 | ![testPostTask.post[@"body"] isEqualToString:testPostTask.body] || 353 | [testPostTask.post[@"user_id"] intValue] != testPostTask.userId || 354 | [testPostTask.post[@"date"] doubleValue] != testPostTask.date.timeIntervalSince1970 || 355 | testPostTask.post[@"ignored"] != nil) { 356 | XCTFail(@"%@ failed", _expectation.description); 357 | } 358 | } 359 | else if ([task isKindOfClass:[STTestPutNetTask class]]) { 360 | [_expectation fulfill]; 361 | STTestPutNetTask *testPutTask = (STTestPutNetTask *)task; 362 | if (task.error || !testPutTask.post) { 363 | XCTFail(@"%@ failed", _expectation.description); 364 | } 365 | } 366 | else if ([task isKindOfClass:[STTestPatchNetTask class]]) { 367 | [_expectation fulfill]; 368 | STTestPatchNetTask *testPatchTask = (STTestPatchNetTask *)task; 369 | if (task.error || !testPatchTask.post) { 370 | XCTFail(@"%@ failed", _expectation.description); 371 | } 372 | } 373 | else if ([task isKindOfClass:[STTestDeleteNetTask class]]) { 374 | [_expectation fulfill]; 375 | if (task.error) { 376 | XCTFail(@"%@ failed", _expectation.description); 377 | } 378 | } 379 | else if ([task isKindOfClass:[STTestMaxConcurrentTasksCountNetTask class]]) { 380 | STTestMaxConcurrentTasksCountNetTask *testMaxConcurrentTasksCountTask = (STTestMaxConcurrentTasksCountNetTask *)task; 381 | if (testMaxConcurrentTasksCountTask.id == 2) { 382 | [_expectation fulfill]; 383 | } 384 | } 385 | } 386 | 387 | @end 388 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestDeleteNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STTestDeleteNetTask.h 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface STTestDeleteNetTask : STHTTPNetTask 12 | 13 | @property (nonatomic, assign) int id; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestDeleteNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STTestDeleteNetTask.m 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import "STTestDeleteNetTask.h" 10 | 11 | @implementation STTestDeleteNetTask 12 | 13 | - (STHTTPNetTaskMethod)method 14 | { 15 | return STHTTPNetTaskDelete; 16 | } 17 | 18 | - (NSString *)uri 19 | { 20 | return [NSString stringWithFormat:@"posts/%d", self.id]; 21 | } 22 | 23 | - (NSArray *)ignoredProperties 24 | { 25 | return STHTTPNetTaskIgnoreAllProperties; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestGetNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STTestGetNetTask.h 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface STTestGetNetTask : STHTTPNetTask 12 | 13 | @property (nonatomic, assign) int id; 14 | @property (nonatomic, strong, readonly) NSDictionary *post; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestGetNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STTestGetNetTask.m 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import "STTestGetNetTask.h" 10 | 11 | @implementation STTestGetNetTask 12 | 13 | - (STHTTPNetTaskMethod)method 14 | { 15 | return STHTTPNetTaskGet; 16 | } 17 | 18 | - (NSString *)uri 19 | { 20 | return [NSString stringWithFormat:@"posts/%d", self.id]; 21 | } 22 | 23 | - (void)didResponseDictionary:(NSDictionary *)dictionary 24 | { 25 | _post = dictionary; 26 | } 27 | 28 | - (NSArray *)ignoredProperties 29 | { 30 | return STHTTPNetTaskIgnoreAllProperties; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestMaxConcurrentTasksCountNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STTestMaxConcurrentTasksCountNetTask.h 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 1/8/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface STTestMaxConcurrentTasksCountNetTask : STHTTPNetTask 12 | 13 | @property (nonatomic, assign) int id; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestMaxConcurrentTasksCountNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STTestMaxConcurrentTasksCountNetTask.m 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 1/8/15. 6 | // 7 | // 8 | 9 | #import "STTestMaxConcurrentTasksCountNetTask.h" 10 | 11 | @implementation STTestMaxConcurrentTasksCountNetTask 12 | 13 | - (NSString *)uri 14 | { 15 | return @"nonexist_uri"; 16 | } 17 | 18 | - (void)didRetry 19 | { 20 | NSLog(@"id: %d, retryCount: %tu", self.id, self.retryCount); 21 | } 22 | 23 | - (NSUInteger)maxRetryCount 24 | { 25 | return 1; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestPackerNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STTestPackerNetTask.h 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 8/1/16. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface STTestPackerNetTask : STHTTPNetTask 12 | 13 | @property (nonatomic, strong) NSString *string; 14 | @property (nonatomic, strong) NSDate *date; 15 | @property (nonatomic, strong) NSDictionary *dictionary; 16 | @property (nonatomic, strong) NSArray *array; 17 | @property (nonatomic, strong, readonly) NSDictionary *post; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestPackerNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STTestPackerNetTask.m 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 8/1/16. 6 | // 7 | // 8 | 9 | #import "STTestPackerNetTask.h" 10 | 11 | @implementation STTestPackerNetTask 12 | 13 | - (STHTTPNetTaskMethod)method 14 | { 15 | return STHTTPNetTaskPost; 16 | } 17 | 18 | - (STHTTPNetTaskRequestType)requestType 19 | { 20 | return STHTTPNetTaskRequestJSON; 21 | } 22 | 23 | - (NSString *)uri 24 | { 25 | return @"posts"; 26 | } 27 | 28 | - (id)transformValue:(id)value 29 | { 30 | if ([value isKindOfClass:[NSArray class]]) { 31 | return [value componentsJoinedByString:@","]; 32 | } 33 | if ([value isKindOfClass:[NSDate class]]) { 34 | return @([value timeIntervalSince1970]); 35 | } 36 | return value; 37 | } 38 | 39 | - (void)didResponseDictionary:(NSDictionary *)dictionary 40 | { 41 | _post = dictionary; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestPatchNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STTestPatchNetTask.h 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface STTestPatchNetTask : STHTTPNetTask 12 | 13 | @property (nonatomic, assign) int id; 14 | @property (nonatomic, strong) NSString *title; 15 | @property (nonatomic, strong, readonly) NSDictionary *post; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestPatchNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STTestPatchNetTask.m 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import "STTestPatchNetTask.h" 10 | 11 | @implementation STTestPatchNetTask 12 | 13 | - (STHTTPNetTaskMethod)method 14 | { 15 | return STHTTPNetTaskPatch; 16 | } 17 | 18 | - (NSString *)uri 19 | { 20 | return [NSString stringWithFormat:@"posts/%d", self.id]; 21 | } 22 | 23 | - (void)didResponseDictionary:(NSDictionary *)dictionary 24 | { 25 | _post = dictionary; 26 | } 27 | 28 | - (NSArray *)ignoredProperties 29 | { 30 | return @[ NSStringFromSelector(@selector(id)) ]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestPostNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STTestPostNetTask.h 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface STTestPostNetTask : STHTTPNetTask 12 | 13 | @property (nonatomic, strong) NSString *title; 14 | @property (nonatomic, strong) NSString *body; 15 | @property (nonatomic, strong) NSDate *date; 16 | @property (nonatomic, assign) int userId; 17 | @property (nonatomic, strong) NSString *ignored; // This property is ignored when packing the request. 18 | @property (nonatomic, strong, readonly) NSDictionary *post; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestPostNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STTestPostNetTask.m 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import "STTestPostNetTask.h" 10 | 11 | @implementation STTestPostNetTask 12 | 13 | - (STHTTPNetTaskMethod)method 14 | { 15 | return STHTTPNetTaskPost; 16 | } 17 | 18 | - (NSString *)uri 19 | { 20 | return @"posts"; 21 | } 22 | 23 | // Optional. Retry 3 times after error occurs. 24 | - (NSUInteger)maxRetryCount 25 | { 26 | return 3; 27 | } 28 | 29 | // Optional. Retry for all types of errors 30 | - (BOOL)shouldRetryForError:(NSError *)error 31 | { 32 | return YES; 33 | } 34 | 35 | // Optional. Retry after 5 seconds. 36 | - (NSTimeInterval)retryInterval 37 | { 38 | return 5; 39 | } 40 | 41 | // Optional. Custom headers. 42 | - (NSDictionary *)headers 43 | { 44 | return @{ @"custom_header": @"value" }; 45 | } 46 | 47 | // Optional. Add parameters which are not inclued in requestObject and net task properties. 48 | - (NSDictionary *)parameters 49 | { 50 | return @{ @"other_parameter": @"value" }; 51 | } 52 | 53 | // Optional. Transform value to a format you want. 54 | - (id)transformValue:(id)value 55 | { 56 | if ([value isKindOfClass:[NSDate class]]) { 57 | return @([value timeIntervalSince1970]); 58 | } 59 | return value; 60 | } 61 | 62 | - (void)didResponseDictionary:(NSDictionary *)dictionary 63 | { 64 | _post = dictionary; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestPutNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STTestPutNetTask.h 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface STTestPutNetTask : STHTTPNetTask 12 | 13 | @property (nonatomic, assign) int id; 14 | @property (nonatomic, strong) NSString *title; 15 | @property (nonatomic, strong) NSString *body; 16 | @property (nonatomic, assign) int userId; 17 | @property (nonatomic, strong, readonly) NSDictionary *post; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestPutNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STTestPutNetTask.m 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 19/7/15. 6 | // 7 | // 8 | 9 | #import "STTestPutNetTask.h" 10 | 11 | @implementation STTestPutNetTask 12 | 13 | - (STHTTPNetTaskMethod)method 14 | { 15 | return STHTTPNetTaskPut; 16 | } 17 | 18 | - (NSString *)uri 19 | { 20 | return [NSString stringWithFormat:@"posts/%d", self.id]; 21 | } 22 | 23 | - (void)didResponseDictionary:(NSDictionary *)dictionary 24 | { 25 | _post = dictionary; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestRetryNetTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // STTestRetryNetTask.h 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 14/7/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface STTestRetryNetTask : STHTTPNetTask 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /STNetTaskQueueTest/STNetTaskQueueTest/STTestRetryNetTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // STTestRetryNetTask.m 3 | // STNetTaskQueueTest 4 | // 5 | // Created by Kevin Lin on 14/7/15. 6 | // 7 | // 8 | 9 | #import "STTestRetryNetTask.h" 10 | 11 | @implementation STTestRetryNetTask 12 | 13 | - (NSString *)uri 14 | { 15 | return @"nonexist_uri"; 16 | } 17 | 18 | - (void)didRetry 19 | { 20 | NSLog(@"retryCount: %tu", self.retryCount); 21 | } 22 | 23 | - (NSUInteger)maxRetryCount 24 | { 25 | return 2; 26 | } 27 | 28 | - (NSTimeInterval)retryInterval 29 | { 30 | return 0.2; 31 | } 32 | 33 | @end 34 | --------------------------------------------------------------------------------