Skip to content

Commit 6b8290b

Browse files
committed
Abandon depending on AFNetworking, use NSURLSession instead for STHTTPNetTaskQueueHandler.
Add unit tests for HTTP STHTTPNetTaskQueueHandler.
1 parent ffaea9a commit 6b8290b

21 files changed

+719
-189
lines changed

STNetTaskQueue/STHTTPNetTask.h

+22-3
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,39 @@
88

99
#import "STNetTask.h"
1010

11-
typedef enum {
11+
FOUNDATION_EXPORT NSString *const STHTTPNetTaskServerError;
12+
FOUNDATION_EXPORT NSString *const STHTTPNetTaskResponseParsedError;
13+
14+
typedef NS_ENUM(NSUInteger, STHTTPNetTaskMethod) {
1215
STHTTPNetTaskGet,
1316
STHTTPNetTaskPost,
1417
STHTTPNetTaskPut,
1518
STHTTPNetTaskDelete,
1619
STHTTPNetTaskHead,
1720
STHTTPNetTaskPatch
18-
} STHTTPNetTaskMethod;
21+
};
22+
23+
typedef NS_ENUM(NSUInteger, STHTTPNetTaskRequestType) {
24+
STHTTPNetTaskRequestJSON,
25+
STHTTPNetTaskRequestKeyValueString,
26+
STHTTPNetTaskRequestFormData
27+
};
28+
29+
typedef NS_ENUM(NSUInteger, STHTTPNetTaskResponseType) {
30+
STHTTPNetTaskResponseJSON,
31+
STHTTPNetTaskResponseString,
32+
STHTTPNetTaskResponseRawData
33+
};
1934

2035
@interface STHTTPNetTask : STNetTask
2136

2237
- (STHTTPNetTaskMethod)method;
38+
- (STHTTPNetTaskRequestType)requestType;
39+
- (STHTTPNetTaskResponseType)responseType;
2340
- (NSDictionary *)parameters;
2441
- (NSDictionary *)datas;
25-
- (void)didResponseJSON:(NSDictionary *)response;
42+
- (void)didResponseJSON:(NSDictionary *)json;
43+
- (void)didResponseString:(NSString *)string;
44+
- (void)didResponseData:(NSData *)data;
2645

2746
@end

STNetTaskQueue/STHTTPNetTask.m

+37-3
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,26 @@
88

99
#import "STHTTPNetTask.h"
1010

11+
NSString *const STHTTPNetTaskServerError = @"STHTTPNetTaskServerError";
12+
NSString *const STHTTPNetTaskResponseParsedError = @"STHTTPNetTaskResponseParsedError";
13+
1114
@implementation STHTTPNetTask
1215

1316
- (STHTTPNetTaskMethod)method
1417
{
1518
return STHTTPNetTaskGet;
1619
}
1720

21+
- (STHTTPNetTaskRequestType)requestType
22+
{
23+
return STHTTPNetTaskRequestKeyValueString;
24+
}
25+
26+
- (STHTTPNetTaskResponseType)responseType
27+
{
28+
return STHTTPNetTaskResponseJSON;
29+
}
30+
1831
- (NSDictionary *)datas
1932
{
2033
return nil;
@@ -25,12 +38,33 @@ - (NSDictionary *)parameters
2538
return nil;
2639
}
2740

28-
- (void)didResponse:(NSObject *)response
41+
- (void)didResponse:(id)response
2942
{
30-
[self didResponseJSON:(NSDictionary *)response];
43+
if ([response isKindOfClass:[NSDictionary class]]) {
44+
[self didResponseJSON:response];
45+
}
46+
else if ([response isKindOfClass:[NSString class]]) {
47+
[self didResponseString:response];
48+
}
49+
else if ([response isKindOfClass:[NSData class]]) {
50+
[self didResponseData:response];
51+
}
52+
else {
53+
NSAssert(NO, @"Invalid response");
54+
}
55+
}
56+
57+
- (void)didResponseJSON:(NSDictionary *)json
58+
{
59+
60+
}
61+
62+
- (void)didResponseString:(NSString *)string
63+
{
64+
3165
}
3266

33-
- (void)didResponseJSON:(NSDictionary *)response
67+
- (void)didResponseData:(NSData *)data
3468
{
3569

3670
}

STNetTaskQueue/STHTTPNetTaskQueueHandler.m

+154-37
Original file line numberDiff line numberDiff line change
@@ -8,78 +8,195 @@
88

99
#import "STHTTPNetTaskQueueHandler.h"
1010
#import "STHTTPNetTask.h"
11-
#import "AFNetworking.h"
1211

1312
@implementation STHTTPNetTaskQueueHandler
1413
{
15-
AFHTTPSessionManager *_httpManager;
14+
NSURL *_baseURL;
15+
NSURLSession *_urlSession;
16+
NSDictionary *_methodMap;
17+
NSString *_formDataBoundary;
1618
}
1719

1820
- (instancetype)initWithBaseURL:(NSURL *)baseURL
1921
{
2022
if (self = [super init]) {
21-
_httpManager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
23+
_baseURL = baseURL;
24+
_urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
25+
_methodMap = @{ @(STHTTPNetTaskGet): @"GET",
26+
@(STHTTPNetTaskDelete): @"DELETE",
27+
@(STHTTPNetTaskHead): @"HEAD",
28+
@(STHTTPNetTaskPatch): @"PATCH",
29+
@(STHTTPNetTaskPost): @"POST",
30+
@(STHTTPNetTaskPut): @"PUT" };
31+
_formDataBoundary = [NSString stringWithFormat:@"ST-Boundary-%@", [[NSUUID UUID] UUIDString]];
2232
}
2333
return self;
2434
}
2535

2636
- (void)netTaskQueue:(STNetTaskQueue *)netTaskQueue task:(STNetTask *)task taskId:(int)taskId
2737
{
28-
NSAssert([task isKindOfClass:[STHTTPNetTask class]], @"Should be subclass of STHTTPNetTask");
38+
NSAssert([task isKindOfClass:[STHTTPNetTask class]], @"Net task should be subclass of STHTTPNetTask");
2939

30-
void (^success)(NSURLSessionDataTask *, id) = ^(NSURLSessionDataTask *task, id responseObject) {
31-
[netTaskQueue didResponse:responseObject taskId:taskId];
32-
};
40+
STHTTPNetTask *httpTask = (STHTTPNetTask *)task;
3341

34-
void (^failure)(NSURLSessionDataTask *, NSError *) = ^(NSURLSessionDataTask *task, NSError *error) {
35-
[netTaskQueue didFailWithError:error taskId:taskId];
42+
void (^completionHandler)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) {
43+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
44+
if (httpResponse.statusCode >= 200 && httpResponse.statusCode < 300) {
45+
id responseObj = nil;
46+
NSError *error = nil;
47+
switch (httpTask.responseType) {
48+
case STHTTPNetTaskResponseRawData: {
49+
responseObj = data;
50+
}
51+
break;
52+
case STHTTPNetTaskResponseString: {
53+
@try {
54+
responseObj = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
55+
}
56+
@catch (NSException *exception) {
57+
NSLog(@"Response parsed error: %@", exception.debugDescription);
58+
error = [NSError errorWithDomain:STHTTPNetTaskResponseParsedError code:0 userInfo:nil];
59+
}
60+
}
61+
break;
62+
case STHTTPNetTaskResponseJSON:
63+
default: {
64+
responseObj = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
65+
if (error) {
66+
NSLog(@"Response parsed error: %@", error.debugDescription);
67+
error = [NSError errorWithDomain:STHTTPNetTaskResponseParsedError code:0 userInfo:nil];
68+
}
69+
}
70+
break;
71+
}
72+
73+
if (error) {
74+
[netTaskQueue didFailWithError:error taskId:taskId];
75+
}
76+
else {
77+
[netTaskQueue didResponse:responseObj taskId:taskId];
78+
}
79+
}
80+
else {
81+
if (!error) { // Response status code is not 200
82+
error = [NSError errorWithDomain:STHTTPNetTaskServerError
83+
code:0
84+
userInfo:@{ @"statusCode": @(httpResponse.statusCode) }];
85+
}
86+
[netTaskQueue didFailWithError:error taskId:taskId];
87+
}
3688
};
3789

38-
STHTTPNetTask *httpTask = (STHTTPNetTask *)task;
39-
NSDictionary *parameters = [httpTask parameters];
90+
NSDictionary *parameters = httpTask.parameters;
91+
92+
NSURLSessionTask *sessionTask = nil;
93+
NSMutableURLRequest *request = [NSMutableURLRequest new];
94+
request.HTTPMethod = _methodMap[@(httpTask.method)];
4095

41-
switch ([httpTask method]) {
42-
case STHTTPNetTaskGet: {
43-
[_httpManager GET:[httpTask uri] parameters:parameters success:success failure:failure];
96+
switch (httpTask.method) {
97+
case STHTTPNetTaskGet:
98+
case STHTTPNetTaskHead:
99+
case STHTTPNetTaskDelete: {
100+
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:[_baseURL URLByAppendingPathComponent:httpTask.uri]
101+
resolvingAgainstBaseURL:NO];
102+
urlComponents.query = [self queryStringFromParameters:parameters];
103+
request.URL = urlComponents.URL;
104+
sessionTask = [_urlSession dataTaskWithRequest:request completionHandler:completionHandler];
44105
}
45106
break;
46-
case STHTTPNetTaskPost: {
47-
NSDictionary *datas = [httpTask datas];
107+
case STHTTPNetTaskPost:
108+
case STHTTPNetTaskPut:
109+
case STHTTPNetTaskPatch: {
110+
request.URL = [_baseURL URLByAppendingPathComponent:httpTask.uri];
111+
NSDictionary *datas = httpTask.datas;
48112
if (!datas.count) {
49-
[_httpManager POST:[httpTask uri] parameters:parameters success:success failure:failure];
113+
request.HTTPBody = [self bodyDataFromParameters:parameters requestType:httpTask.requestType];
114+
sessionTask = [_urlSession dataTaskWithRequest:request completionHandler:completionHandler];
50115
}
51116
else {
52-
[_httpManager POST:[httpTask uri] parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
53-
for (NSString *name in datas) {
54-
[formData appendPartWithFileData:datas[name] name:name fileName:@"st_file" mimeType:@"*/*"];
55-
}
56-
} success:success failure:failure];
117+
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", _formDataBoundary];
118+
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];
119+
sessionTask = [_urlSession uploadTaskWithRequest:request
120+
fromData:[self formDataFromParameters:parameters datas:datas]
121+
completionHandler:completionHandler];
57122
}
58123
}
59124
break;
60-
case STHTTPNetTaskPut: {
61-
[_httpManager PUT:[httpTask uri] parameters:parameters success:success failure:failure];
62-
}
63-
break;
64-
case STHTTPNetTaskDelete: {
65-
[_httpManager DELETE:[httpTask uri] parameters:parameters success:success failure:failure];
66-
}
67-
break;
68-
case STHTTPNetTaskPatch: {
69-
[_httpManager PATCH:[httpTask uri] parameters:parameters success:success failure:failure];
125+
default: {
126+
NSAssert(NO, @"Invalid STHTTPNetTaskMethod");
70127
}
71128
break;
72-
case STHTTPNetTaskHead: {
73-
[_httpManager HEAD:[httpTask uri] parameters:parameters success:^(NSURLSessionDataTask *task) {
74-
[netTaskQueue didResponse:@{} taskId:taskId];
75-
} failure:failure];
129+
}
130+
131+
[sessionTask resume];
132+
}
133+
134+
- (NSString *)queryStringFromParameters:(NSDictionary *)parameters
135+
{
136+
if (!parameters.count) {
137+
return @"";
138+
}
139+
140+
NSMutableString *queryString = [NSMutableString string];
141+
[parameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) {
142+
value = [value stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
143+
[queryString appendFormat:@"%@=%@&", key, value];
144+
}];
145+
[queryString deleteCharactersInRange:NSMakeRange(queryString.length - 1, 1)];
146+
return queryString;
147+
}
148+
149+
- (NSData *)bodyDataFromParameters:(NSDictionary *)parameters requestType:(STHTTPNetTaskRequestType)requestType
150+
{
151+
if (!parameters.count) {
152+
return nil;
153+
}
154+
155+
NSData *bodyData = nil;
156+
157+
switch (requestType) {
158+
case STHTTPNetTaskRequestJSON: {
159+
NSError *error = nil;
160+
bodyData = [NSJSONSerialization dataWithJSONObject:parameters options:kNilOptions error:&error];
161+
NSAssert(!error, @"Request is not in JSON format");
76162
}
77163
break;
164+
case STHTTPNetTaskRequestKeyValueString:
78165
default: {
79-
NSAssert(NO, @"Invalid STHTTPNetTaskMethod");
166+
NSMutableString *bodyString = [NSMutableString string];
167+
[parameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) {
168+
[bodyString appendFormat:@"%@=%@&", key, value];
169+
}];
170+
[bodyString deleteCharactersInRange:NSMakeRange(bodyString.length - 1, 1)];
171+
bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
80172
}
81173
break;
82174
}
175+
176+
return bodyData;
177+
}
178+
179+
- (NSData *)formDataFromParameters:(NSDictionary *)parameters datas:(NSDictionary *)datas
180+
{
181+
NSMutableData *formData = [NSMutableData data];
182+
183+
[parameters enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) {
184+
[formData appendData:[[NSString stringWithFormat:@"--%@\r\n", _formDataBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
185+
[formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
186+
[formData appendData:[[NSString stringWithFormat:@"%@\r\n", value] dataUsingEncoding:NSUTF8StringEncoding]];
187+
}];
188+
189+
[datas enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSData *fileData, BOOL *stop) {
190+
[formData appendData:[[NSString stringWithFormat:@"--%@\r\n", _formDataBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
191+
[formData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, key] dataUsingEncoding:NSUTF8StringEncoding]];
192+
[formData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", @"*/*"] dataUsingEncoding:NSUTF8StringEncoding]];
193+
[formData appendData:fileData];
194+
[formData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
195+
}];
196+
197+
[formData appendData:[[NSString stringWithFormat:@"--%@--\r\n", _formDataBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
198+
199+
return formData;
83200
}
84201

85202
@end

STNetTaskQueue/STNetTask.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88

99
#import <Foundation/Foundation.h>
1010

11+
FOUNDATION_EXPORT NSString *const STNetTaskUnknownError;
12+
1113
@interface STNetTask : NSObject
1214

1315
@property (nonatomic, strong) NSError *error;
1416
@property (nonatomic, assign) BOOL pending;
1517
@property (nonatomic, assign) NSUInteger retryCount;
1618

1719
- (NSString *)uri;
18-
- (void)didResponse:(NSObject *)response;
20+
- (void)didResponse:(id)response;
1921
- (void)didFail;
2022
- (void)didRetry;
2123

STNetTaskQueue/STNetTask.m

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88

99
#import "STNetTask.h"
1010

11+
NSString *const STNetTaskUnknownError = @"STNetTaskUnknownError";
12+
1113
@implementation STNetTask
1214

1315
- (NSString *)uri
1416
{
1517
return @"";
1618
}
1719

18-
- (void)didResponse:(NSObject *)response
20+
- (void)didResponse:(id)response
1921
{
2022

2123
}

0 commit comments

Comments
 (0)