summaryrefslogtreecommitdiff
path: root/Juick/APIClient.m
diff options
context:
space:
mode:
Diffstat (limited to 'Juick/APIClient.m')
-rw-r--r--Juick/APIClient.m331
1 files changed, 190 insertions, 141 deletions
diff --git a/Juick/APIClient.m b/Juick/APIClient.m
index 2712f1d..3332556 100644
--- a/Juick/APIClient.m
+++ b/Juick/APIClient.m
@@ -9,10 +9,12 @@
#import "Message.h"
#import "Chat.h"
-@interface APIClient()
-@property(nonatomic, readwrite) AFHTTPSessionManager *manager;
+@interface APIClient ()
@property(nonatomic, strong) NSOperationQueue *backgroundQueue;
+@property(nonatomic, strong) NSURL *baseURL;
@property(nonatomic, strong) NSURLSession *urlSession;
+@property(nonatomic, strong) NSURLProtectionSpace *apiProtectionSpace;
+-(void) get:(NSString *)path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback;
@end
@implementation APIClient
@@ -30,197 +32,244 @@
if (self = [super init]) {
NSString *baseURLString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"base_url"];
NSLog(@"Initializing with %@ base URL", baseURLString);
- _manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:baseURLString]];
- _manager.requestSerializer = [AFJSONRequestSerializer new];
- [_manager.requestSerializer setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
+ self.baseURL = [NSURL URLWithString:baseURLString];
+ self.apiProtectionSpace = [[NSURLProtectionSpace alloc] initWithHost:self.baseURL.host port:0 protocol:NSURLProtectionSpaceHTTPS realm:@"Juick" authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
self.backgroundQueue = [NSOperationQueue new];
+ self.urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:self.backgroundQueue];
+
self.dateFormatter = [[NSDateFormatter alloc] init];
self.dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
[self.dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
- _urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
}
return self;
}
--(AFHTTPSessionManager *) manager {
- NSString *username = [SAMKeychain passwordForService:[[NSBundle mainBundle] bundleIdentifier] account:@"com.juick.username"];
- if (username) {
- [_manager.requestSerializer
- setAuthorizationHeaderFieldWithUsername:username
- password:[SAMKeychain passwordForService:[[NSBundle mainBundle] bundleIdentifier] account:@"com.juick.password"]];
- }
- return _manager;
-}
-
-(void) pullNextFromPath:(NSString *)path params:(NSDictionary *) params callback:(void(^)(NSArray<Message *> *, NSError *))callback {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self.backgroundQueue addOperationWithBlock:^{
- [self.manager GET:path parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- NSInteger statusCode = [((NSHTTPURLResponse *) task.response) statusCode];
- if (statusCode == 200) {
- NSMutableArray *messages = [NSMutableArray new];
- [((NSArray *)responseObject) enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
- [messages addObject:[Message fromJSON:obj]];
- }];
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(messages, nil);
- }];
- } else {
- callback(nil, [NSError errorWithDomain:@"JuickErrorDomain" code:statusCode userInfo:nil]);
- }
-
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- NSLog(@"REST Error: %@", error);
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- NSInteger statusCode = ((NSHTTPURLResponse *)task.response).statusCode;
- callback(nil, [NSError errorWithDomain:@"JuickErrorDomain" code:statusCode userInfo:nil]);
- }];
+ [self get:path params:params callback:^(NSDictionary *response, NSError *error) {
+ NSMutableArray *messages = [NSMutableArray new];
+ if (!error) {
+ [((NSArray *)response) enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+ [messages addObject:[Message fromJSON:obj]];
}];
- }];
- });
+ }
+ callback(messages, error);
+ }];
}
-(void) postMessage:(NSString *)text result:(void (^)(Message *, NSError *))callback {
- [self.backgroundQueue addOperationWithBlock:^{
- [self.manager POST:@"post" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
- [formData appendPartWithFormData:[text dataUsingEncoding:NSUTF8StringEncoding] name:@"body"];
- } progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- NSLog(@"Success!");
- Message *result = [Message fromJSON:responseObject];
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(result, nil);
- }];
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- NSLog(@"Error: %@", [error localizedDescription]);
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(nil, error);
- }];
- }];
+ [self post:@"post" params:@{
+ @"body": text
+ } callback:^(NSDictionary *response, NSError *err) {
+ Message *result;
+ if (!err) {
+ result = [Message fromJSON:response[@"newMessage"]];
+ }
+ callback(result, err);
}];
}
-(void) postReplyToThread:(NSNumber *)mid inReplyTo:(NSNumber *)rid text:(NSString *)text result:(void(^)(Message *, NSError *))callback {
- [self.backgroundQueue addOperationWithBlock:^{
- [self.manager POST:@"comment" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
- [formData appendPartWithFormData:[[mid stringValue] dataUsingEncoding:NSUTF8StringEncoding] name:@"mid"];
- [formData appendPartWithFormData:[[NSString stringWithFormat:@"%d", [rid intValue]] dataUsingEncoding:NSUTF8StringEncoding] name:@"rid"];
- [formData appendPartWithFormData:[text dataUsingEncoding:NSUTF8StringEncoding] name:@"body"];
- } progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
+ [self post:@"comment" params:@{
+ @"mid": mid,
+ @"rid": [NSString stringWithFormat:@"%ld", [rid integerValue]],
+ @"body": text
+ } callback:^(NSDictionary *response, NSError *err) {
+ Message *reply;
+ if (!err) {
NSLog(@"Success!");
- Message *reply = [Message fromJSON:responseObject];
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(reply, nil);
- }];
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- NSLog(@"Error: %@", [error localizedDescription]);
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(nil, error);
- }];
- }];
+ reply = [Message fromJSON:response];
+ }
+ callback(reply, err);
}];
}
+
-(void) postPMToUser:(NSString *)uname text:(NSString *)text result:(void (^)(NSError *))callback {
[self.backgroundQueue addOperationWithBlock:^{
- [self.manager POST:@"pm" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
- [formData appendPartWithFormData:[text dataUsingEncoding:NSUTF8StringEncoding] name:@"body"];
- [formData appendPartWithFormData:[uname dataUsingEncoding:NSUTF8StringEncoding] name:@"uname"];
- } progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(nil);
- }];
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- NSLog(@"Error: %@", [error localizedDescription]);
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(error);
- }];
+ [self post:@"pm" params:@{
+ @"uname": uname,
+ @"body": text
+ } callback:^(NSDictionary *response, NSError *err) {
+ callback(err);
}];
}];
}
-(void) fetchChats:(void (^)(NSArray *, NSError *))callback {
- [self.backgroundQueue addOperationWithBlock:^{
- [self.manager GET:@"groups_pms" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- NSMutableArray *groups = [NSMutableArray new];
- NSArray *pms = [(NSDictionary *)responseObject objectForKey:@"pms"];
- [pms enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
- [groups addObject:[Chat fromJSON:obj]];
- }];
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(groups, nil);
- }];
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- NSLog(@"Error: %@", [error localizedDescription]);
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(nil, error);
- }];
+ [self get:@"groups_pms" params:nil callback:^(NSDictionary *response, NSError *err) {
+ NSMutableArray *groups = [NSMutableArray new];
+ NSArray *pms = [response objectForKey:@"pms"];
+ [pms enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+ [groups addObject:[Chat fromJSON:obj]];
}];
+ callback(groups, err);
}];
}
+
-(void) fetchChatWithUser:(NSString *)uname callback:(void (^)(NSArray *, NSError *))callback {
NSDictionary *params = @{@"uname": uname};
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self.backgroundQueue addOperationWithBlock:^{
- [self.manager GET:@"pm" parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- NSMutableArray *messages = [NSMutableArray new];
- NSArray *messagesList = (NSArray *)responseObject;
- [messagesList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
- [messages addObject:[Message fromJSON:obj]];
- }];
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(messages, nil);
- }];
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- NSLog(@"Error: %@", [error localizedDescription]);
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(nil, error);
- }];
+ [self get:@"pm" params:params callback:^(NSDictionary *response, NSError *err) {
+ NSMutableArray *messages = [NSMutableArray new];
+ if (!err) {
+ NSArray *messagesList = (NSArray *)response;
+ [messagesList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+ [messages addObject:[Message fromJSON:obj]];
}];
- }];
- });
+ }
+ callback(messages, err);
+ }];
}
+
-(void) authenticate:(void (^)(User *user, NSError *error))callback {
- [self.manager GET:@"me" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- NSString *username = [SAMKeychain passwordForService:[[NSBundle mainBundle] bundleIdentifier] account:@"com.juick.username"];
- NSString *password = [SAMKeychain passwordForService:[[NSBundle mainBundle] bundleIdentifier] account:@"com.juick.password"];
- [self.manager.requestSerializer setAuthorizationHeaderFieldWithUsername:username password:password];
- callback([User fromJSON:responseObject], nil);
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- [self.manager.requestSerializer clearAuthorizationHeader];
- callback(nil, error);
+ [self get:@"me" params:nil callback:^(NSDictionary *response, NSError *error) {
+ User *user;
+ if (!error) {
+ user = [User fromJSON:response];
+ }
+ callback(user, error);
}];
}
-(void) getUserByName:(NSString *) name callback:(void(^)(User *))callback {
NSDictionary *params = @{@"uname": name};
- [self.manager GET:@"users" parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- callback([User fromJSON:[(NSArray *)responseObject firstObject]]);
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- callback(nil);
+ [self get:@"users" params:params callback:^(NSDictionary *response, NSError *err) {
+ if (!err) {
+ callback([User fromJSON:[(NSArray *)response firstObject]]);
+ } else {
+ callback(nil);
+ }
}];
}
-(void) refreshDeviceRegistration:(DeviceRegistration *)registrationData callback:(void (^)(BOOL))callback {
- [self.manager PUT:@"/notifications" parameters:@[[registrationData toJSON]] success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
- callback(YES);
- } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
- NSLog(@"fail %@", [error localizedDescription]);
- callback(NO);
- } ];
+ NSURL *notificationsUrl = [NSURL URLWithString:@"notifications" relativeToURL:self.baseURL];
+ NSError *error;
+ NSDictionary *token = [registrationData toJSON];
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@[token] options:kNilOptions error:&error];
+ [self fetchDataWithURL:notificationsUrl data:jsonData boundary:nil callback:^(NSData *response, NSError *err) {
+ callback(!err);
+ }];
}
+
+- (NSURLCredential *)credential {
+ NSDictionary *credentials = [[NSURLCredentialStorage sharedCredentialStorage] credentialsForProtectionSpace:self.apiProtectionSpace];
+ return [credentials.objectEnumerator nextObject];
+}
+
+- (void)setCredential:(NSURLCredential *)credential {
+ [[NSURLCredentialStorage sharedCredentialStorage] setCredential:credential forProtectionSpace:self.apiProtectionSpace];
+}
+
+
-(BOOL) isAuthenticated {
- NSString *password = [SAMKeychain passwordForService:[[NSBundle mainBundle] bundleIdentifier] account:@"com.juick.password"];
+ NSString *password = self.credential.password;
return password != nil;
}
-- (void)fetchImageWithURL:(NSURL *)url callback:(void (^)(NSData *))callback {
- NSURLSessionDataTask *dataTask = [self.urlSession dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- callback(data);
- }];
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
+ if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic) {
+ completionHandler(NSURLSessionAuthChallengeUseCredential, self.credential);
+ } else {
+ completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
+ }
+}
+
+-(void) fetchDataWithURL:(NSURL *) url data:(NSData *)postData boundary:(NSString *)boundary callback:(void(^)(NSData *, NSError *))callback {
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
+ if (boundary) {
+ request.HTTPMethod = @"POST";
+ request.HTTPBody = postData;
+ NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
+ [request setValue:contentType forHTTPHeaderField: @"Content-Type"];
+ } else if (postData) {
+ request.HTTPMethod = @"PUT";
+ request.HTTPBody = postData;
+ [request setValue:@"application/json" forHTTPHeaderField: @"Content-Type"];
+ } else {
+ request.HTTPMethod = @"GET";
+ }
+
+ NSURLSessionDataTask *task = [self.urlSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
+ NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
+ if (!response || statusCode != 200) {
+ NSError *err = [NSError errorWithDomain:@"JuickErrorDomain"
+ code:statusCode
+ userInfo:nil];
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ callback(nil, err);
+ }];
+ } else {
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ callback(data, error);
+ }];
+ }
}];
[self.backgroundQueue addOperationWithBlock:^{
- [dataTask resume];
+ [task resume];
+ }];
+}
+
+-(NSURL *) url:(NSURL *)url withParameters:(NSDictionary *)params {
+ NSURLComponents *parametersUrl = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];
+ NSMutableArray *items = [NSMutableArray array];
+ for (NSString * key in params) {
+ NSString *value = [NSString stringWithFormat:@"%@", params[key]];
+ [items addObject:[NSURLQueryItem queryItemWithName:key value:value]];
+ }
+ [parametersUrl setQueryItems:items];
+ return [parametersUrl URL];
+}
+
+-(NSData *) multipartData:(NSDictionary *)params withBoundary:(NSString *)boundary {
+ NSMutableData *body = [NSMutableData data];
+ for (NSString *key in params) {
+ [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
+ [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=%@\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
+ [body appendData:[[NSString stringWithFormat:@"%@\r\n", params[key]] dataUsingEncoding:NSUTF8StringEncoding]];
+ }
+ [body appendData:[[NSString stringWithFormat:@"--%@--", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
+ return body;
+}
+
+-(void) get:(NSString *) path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback {
+ NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL];
+ NSURL *requestUrl = params ? [self url:url withParameters:params] : url;
+ [self fetchDataWithURL:requestUrl data:nil boundary:nil callback:^(NSData *data, NSError *err) {
+ if (err) {
+ callback(nil, err);
+ } else {
+ NSError *jsonError;
+ NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
+ if (jsonError) {
+ callback(nil, jsonError);
+ } else {
+ callback(jsonData, nil);
+ }
+ }
+ }];
+}
+
+-(void) post:(NSString *) path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback {
+ NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL];
+ NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];
+ [self fetchDataWithURL:url data:[self multipartData:params withBoundary:boundary] boundary:boundary callback:^(NSData *data, NSError *err) {
+ NSError *jsonError;
+ NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
+ if (jsonError) {
+ callback(nil, jsonError);
+ } else {
+ callback(jsonData, nil);
+ }
+ }];
+}
+
+-(void) fetchImageWithURL:(NSURL *) url callback:(void(^)(NSData *))callback {
+ [self fetchDataWithURL:url data:nil boundary:nil callback:^(NSData *data, NSError *err) {
+ if (err) {
+ callback(nil);
+ } else {
+ callback(data);
+ }
}];
}