// // APIClient.m // Juick // // Created by Vitaly Takmazov on 22/08/16. // Copyright © 2016 com.juick. All rights reserved. // #import "APIClient.h" #import "Message.h" #import "Chat.h" @interface APIClient() @property(nonatomic, readwrite) AFHTTPSessionManager *manager; @property(nonatomic, strong) NSOperationQueue *backgroundQueue; @property(nonatomic, strong) NSURLSession *urlSession; @end @implementation APIClient +(APIClient *) sharedClient { static APIClient *sharedAPIClient = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedAPIClient = [[self alloc] init]; }); return sharedAPIClient; } -(id)init { 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.backgroundQueue = [NSOperationQueue new]; 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 *, 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]); }]; }]; }]; }); } -(void) postMessage:(NSString *)text result:(void (^)(Message *, NSError *))callback { [self.backgroundQueue addOperationWithBlock:^{ [self.manager POST:@"post" parameters:nil constructingBodyWithBlock:^(id _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); }]; }]; }]; } -(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 _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) { 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); }]; }]; }]; } -(void) postPMToUser:(NSString *)uname text:(NSString *)text result:(void (^)(NSError *))callback { [self.backgroundQueue addOperationWithBlock:^{ [self.manager POST:@"pm" parameters:nil constructingBodyWithBlock:^(id _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); }]; }]; }]; } -(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); }]; }]; }]; } -(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); }]; }]; }]; }); } -(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); }]; } -(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); }]; } -(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); } ]; } -(BOOL) isAuthenticated { NSString *password = [SAMKeychain passwordForService:[[NSBundle mainBundle] bundleIdentifier] account:@"com.juick.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); }]; }]; [self.backgroundQueue addOperationWithBlock:^{ [dataTask resume]; }]; } +(NSString *) messagesUrl { return @"messages"; } +(NSString *) discussionsUrl { return @"messages/discussions"; } +(NSString *) threadUrl { return @"thread"; } +(NSString *) feedUrl { return @"home"; } @end