Skip to content

Instantly share code, notes, and snippets.

@joebrislin
Last active December 10, 2015 07:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joebrislin/57c56889571bf7d1fa15 to your computer and use it in GitHub Desktop.
Save joebrislin/57c56889571bf7d1fa15 to your computer and use it in GitHub Desktop.
Code Samples - Mobile Application to Evaluate Equipment TradeIns (iOS / Objective C)
//
// DataService.h
// TradeIn
//
// Created by Joe Brislin on 10/29/14.
// Copyright (c) 2014 BlueRidge Interactive. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "PostEvaluation.h"
#import "Evaluation.h"
@interface DataService : NSObject
- (NSArray*)getPendingEvaluations;
- (NSUInteger)getPendingEvaluationsCount;
- (NSArray*)getPendingImages;
- (NSUInteger)getPendingImagesCount;
- (UIImage*)getFrontRightImageByDraftID:(NSManagedObjectID *)draftID;
- (void)deletePendingDraft:(NSManagedObjectID *)draftID;
- (void)processPendingImages;
-(void)processPendingImages:(void (^)(id responseObject, NSError *error))completion;
- (void)savePendingEvaluation:(NSInteger)evaluationID draftID:(NSManagedObjectID *)draftID postData:(NSMutableDictionary*)postData draft:(NSNumber *)draft imageFrontRight:(UIImage*)imageFrontRight imageFrontLeft:(UIImage*)imageFrontLeft imageBackRight:(UIImage*)imageBackRight imageBackLeft:(UIImage*)imageBackLeft imageHours:(UIImage*)imageHours imageSerial:(UIImage*)imageSerial completion:(void (^)(id responseObject, NSError *error))completion;
- (void)savePendingImage:(long)evaluationID formID:(long)formID imgName:(NSString *)imgName image:(NSData *)image category:(NSString *)category make:(NSString *)make model:(NSString *)model tagNumber:(NSString *)tagNumber;
- (void)sendEvaluation:(long)evaluationID eval:(NSMutableDictionary*)eval completion:(void (^)(id responseObject, NSError *error))completion;
- (void)sendEvaluationConfirmation:(NSInteger)evaluationID;
- (void)sendImage:(long)evaluationID formID:(long)formID imgName:(NSString *)imgName imgData:(NSData *)imgData completion:(void (^)(id, NSError *))completion;
- (void)sendImageConfirmation:(NSInteger)imageID;
@end
//
// DataService.m
// TradeIn
//
// Created by Joe Brislin on 10/29/14.
// Copyright (c) 2014 BlueRidge Interactive. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <Crashlytics/Crashlytics.h>
#import <RestKit/RestKit.h>
#import <SVProgressHUD/SVProgressHUD.h>
#import "DataService.h"
#import "AppDelegate.h"
#import "BasicResponse.h"
#import "Constants.h"
#import "DBValidator.h"
#import "ImageUpload.h"
#import "PendingImage.h"
#import "PendingEvaluation.h"
#import "PostEvaluation.h"
#import "Utils.h"
@implementation DataService
- (void)addEvaluationToPending
{
NSLog(@"[DataService] addEvaluationToPending");
}
- (void)addImageToPending
{
NSLog(@"[DataService] addImageToPending");
}
- (void)deletePendingDraft:(NSManagedObjectID *)draftID
{
// AppDelegate
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *obj = [context objectWithID:draftID];
[context deleteObject:obj];
NSError *pendingEvaluationError;
if (![context save:&pendingEvaluationError]) {
NSLog(@"[DataService] (deletePendingDraft) Entity: PendingEvaluation, Error: %@", [pendingEvaluationError localizedDescription]);
}
}
- (UIImage*)getFrontRightImageByDraftID:(NSManagedObjectID *)draftID
{
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *obj = [context objectWithID:draftID];
UIImage* ret = nil;
if ( [obj valueForKey:@"imageFrontRight"] != nil ) {
ret = [UIImage imageWithData:[obj valueForKey:@"imageFrontRight"]];
}
return ret;
}
- (NSArray*)getPendingEvaluations
{
NSLog(@"[DataService] getPendingEvaluations");
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"PendingEvaluation" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *fetcherror;
NSArray *fetchedEvaluations = [context executeFetchRequest:fetchRequest error:&fetcherror];
return fetchedEvaluations;
}
- (NSUInteger)getPendingEvaluationsCount
{
NSLog(@"[DataService] getPendingEvaluationsCount");
return [[self getPendingEvaluations] count];
}
- (NSArray*)getPendingImages
{
NSLog(@"[DataService] getPendingImages");
// NSLog(@"%@",[NSThread callStackSymbols]);
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"PendingImage" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *fetcherror;
NSArray *fetchedImages = [context executeFetchRequest:fetchRequest error:&fetcherror];
return fetchedImages;
}
- (NSUInteger)getPendingImagesCount
{
NSLog(@"[DataService] getPendingImagesCount");
return [[self getPendingImages] count];
}
-(void)processPendingImages
{
[self processPendingImages:nil];
}
-(void)processPendingImages:(void (^)(id responseObject, NSError *error))completion
{
CLSLog(@"[DataService processPendingImages] Background Image Upload - PROCESSING IMAGES");
[AppDelegate sharedDelegate].processRunning = YES;
NSArray *pendingArray = [self getPendingImages];
__block int failedImgs = 0;
for (PendingImage *pendingImg in pendingArray) {
[self sendImage:[pendingImg.evaluationID integerValue] formID:[pendingImg.formID integerValue] imgName:pendingImg.name imgData:pendingImg.image completion:^(id responseObject, NSError *error) {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
if (!error && [[responseObject valueForKeyPath:@"success"] boolValue]) {
[context deleteObject:pendingImg]; // image successfully uploaded - delete from pending images
NSInteger imgID = [[responseObject valueForKeyPath:@"recordID"] integerValue];
[self sendImageConfirmation: imgID]; // send confirmation response back to server to let image be sent to EMS
NSError *contextError = nil;
if (![context save:&contextError]) {
CLSLog(@"[DataService processPendingImages] Error Saving Processed Image (Core Data) - %@",contextError); // log error saving to core data
}
}else{
failedImgs++;
}
if (pendingImg == [pendingArray lastObject]) {
NSLog(@"[DataService processPendingImages] check for timer restart if pending images still exist");
NSUInteger imgCount = [self getPendingImagesCount];
if (imgCount) {
[appDelegate startTimer];
CLSLog(@"[DataService processPendingImages] Background Image Upload - START TIMER");
}else{
[appDelegate stopTimer];
CLSLog(@"[DataService processPendingImages] Background Image Upload - STOP TIMER");
}
[AppDelegate sharedDelegate].processRunning = NO;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"notifyImageUpdates" object:self];
});
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion([NSNumber numberWithInteger:failedImgs], nil);
});
}
}
}];
}
}
- (void)savePendingEvaluation:(NSInteger)evaluationID draftID:(NSManagedObjectID *)draftID postData:(NSMutableDictionary*)postData draft:(NSNumber *)draft imageFrontRight:(UIImage*)imageFrontRight imageFrontLeft:(UIImage*)imageFrontLeft imageBackRight:(UIImage*)imageBackRight imageBackLeft:(UIImage*)imageBackLeft imageHours:(UIImage*)imageHours imageSerial:(UIImage*)imageSerial completion:(void (^)(id responseObject, NSError *error))completion
{
NSLog(@"[DataService] savePendingEvaluation");
// AppDelegate
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
PendingEvaluation *obj = nil;
if ( draftID != nil ) {
PendingEvaluation *oldDraft = (PendingEvaluation*)[context objectWithID:draftID];
obj = [NSEntityDescription insertNewObjectForEntityForName:@"PendingEvaluation" inManagedObjectContext:context];
obj.imageFrontRight = oldDraft.imageFrontRight;
obj.imageFrontLeft = oldDraft.imageFrontLeft;
obj.imageBackRight = oldDraft.imageBackRight;
obj.imageBackLeft = oldDraft.imageBackLeft;
obj.imageHours = oldDraft.imageHours;
obj.imageSerial = oldDraft.imageSerial;
obj.draft = oldDraft.draft;
obj.attempts = oldDraft.attempts;
[context deleteObject:oldDraft];
}
else {
obj = [NSEntityDescription insertNewObjectForEntityForName:@"PendingEvaluation" inManagedObjectContext:context];
}
[obj setValue:[NSNumber numberWithInteger:evaluationID] forKey:@"evaluationID"];
[obj setValue:postData forKey:@"evaluation"];
// Once evaluation has been submitted, don't reset draft status
if ( [obj valueForKey:@"draft"] == nil || [[obj valueForKey:@"draft"] boolValue] ) {
[obj setValue:draft forKey:@"draft"];
}
if ( imageFrontRight != nil ) {
[obj setValue:UIImageJPEGRepresentation(imageFrontRight,1) forKey:@"imageFrontRight"];
}
if ( imageFrontLeft != nil ) {
[obj setValue:UIImageJPEGRepresentation(imageFrontLeft,1) forKey:@"imageFrontLeft"];
}
if ( imageBackRight != nil ) {
[obj setValue:UIImageJPEGRepresentation(imageBackRight,1) forKey:@"imageBackRight"];
}
if ( imageBackLeft != nil ) {
[obj setValue:UIImageJPEGRepresentation(imageBackLeft,1) forKey:@"imageBackLeft"];
}
if ( imageHours != nil ) {
[obj setValue:UIImageJPEGRepresentation(imageHours,1) forKey:@"imageHours"];
}
if ( imageSerial != nil ) {
[obj setValue:UIImageJPEGRepresentation(imageSerial,1) forKey:@"imageSerial"];
}
NSError *pendingEvaluationError;
if (![context save:&pendingEvaluationError]) {
NSLog(@"[DataService] (savePendingEvaluation) Entity: PendingEvaluation, Error: %@", [pendingEvaluationError localizedDescription]);
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, pendingEvaluationError);
});
}
}else{
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion([obj objectID], nil);
});
}
}
}
- (void)savePendingImage:(NSInteger)evaluationID formID:(NSInteger)formID imgName:(NSString *)imgName image:(NSData *)image category:(NSString *)category make:(NSString *)make model:(NSString *)model tagNumber:(NSString *)tagNumber
{
// AppDelegate
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *obj = [NSEntityDescription insertNewObjectForEntityForName:@"PendingImage" inManagedObjectContext:context];
[obj setValue:[NSNumber numberWithInteger:evaluationID] forKey:@"evaluationID"];
[obj setValue:[NSNumber numberWithInteger:formID] forKey:@"formID"];
[obj setValue:imgName forKey:@"name"];
[obj setValue:image forKey:@"image"];
[obj setValue:category forKey:@"category"];
[obj setValue:make forKey:@"make"];
[obj setValue:model forKey:@"model"];
[obj setValue:tagNumber forKey:@"tagNumber"];
NSError *pendingImageError;
if (![context save:&pendingImageError]) {
NSLog(@"[AppDelegate] (loadEntity) Entity: PendingImages, Error: %@", [pendingImageError localizedDescription]);
}
}
- (void)sendEvaluation:(NSInteger)evaluationID eval:(NSMutableDictionary*)eval completion:(void (^)(id responseObject, NSError *error))completion
{
NSLog(@"[DataService] sendEvaluation");
PostEvaluation* postData = [[PostEvaluation alloc] init];
postData._data = eval;
// Prepare response
NSMutableDictionary *response = [NSMutableDictionary
dictionaryWithDictionary:@{
@"success" : @NO,
@"transfer" : @NO,
@"evaluationID" : [NSString stringWithFormat:@"%ld", evaluationID],
@"tagNumber" : @"",
@"pendingID" : [NSNumber numberWithInt:0]
}];
// Initialize HTTPClient
NSURL *baseURL = [NSURL URLWithString:portalAPIUrl];
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
//we want to work with JSON-Data
[client setDefaultHeader:@"Accept" value:RKMIMETypeJSON];
NSDictionary *params = @{
@"apiKey" : portalAPIKey,
@"company" : [[NSUserDefaults standardUserDefaults] valueForKey:@"company"]
};
// API URL path - base URL already set above
NSString *path = [NSString stringWithFormat:@"evaluation/%li/", evaluationID]; // include evaluationID in API Path
RKObjectManager *manager = [[RKObjectManager alloc] initWithHTTPClient:client];
[manager setRequestSerializationMIMEType:RKMIMETypeJSON];
RKObjectMapping *objectMapping = [RKObjectMapping requestMapping];
[objectMapping addAttributeMappingsFromArray:@[@"_data"]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:objectMapping
objectClass:[PostEvaluation class]
rootKeyPath:nil
method:RKRequestMethodPOST];
[manager addRequestDescriptor:requestDescriptor];
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[BasicResponse class]];
[responseMapping addAttributeMappingsFromDictionary:@{
@"message": @"message",
@"success": @"success",
@"tagNumber": @"tagNumber",
@"recordID": @"recordID",
@"failedImageCnt": @"failedImageCnt"
}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[responseMapping inverseMapping]
method:RKRequestMethodPOST
pathPattern:path
keyPath:nil
statusCodes: RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
];
[manager addResponseDescriptor:responseDescriptor];
// Perform POST to server
[postData._data removeObjectForKey:@"make"]; // remove make string from object posted to API
[manager postObject:postData
path:path
parameters:params
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
BasicResponse *result = (BasicResponse*)[mappingResult firstObject];
// If success == true everything was good server side
if ( [[result valueForKeyPath:@"success"] boolValue] ) {
[response setObject:@YES forKey:@"success"];
[response setObject:@YES forKey:@"transfer"];
[response setObject:[result valueForKeyPath:@"recordID"] forKey:@"evaluationID"];
// [response setObject:[result valueForKeyPath:@"tagNumber"] forKey:@"tagNumber"];
}
// If success === false but we have a recordID, evaluation was saved but not transferred
else if ( [result valueForKeyPath:@"recordID"] > 0 ) {
[response setObject:@YES forKey:@"success"];
[response setObject:@NO forKey:@"transfer"];
[response setObject:[result valueForKeyPath:@"recordID"] forKey:@"evaluationID"];
}
// Server side failed
else {
[response setObject:@NO forKey:@"success"];
[response setObject:@NO forKey:@"transfer"];
}
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(response, nil);
});
}
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(@"Operation failed with error: %@", error);
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, error);
});
}
}];
}
- (void)sendEvaluationConfirmation:(NSInteger)evaluationID
{
NSLog(@"[DataService] sendEvaluationConfirmation");
// update database record on server with new device identifier
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:portalAPIUrl]];
NSDictionary *params = @{@"apiKey":portalAPIKey};
[httpClient setParameterEncoding:AFFormURLParameterEncoding];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
path:[NSString stringWithFormat:@"evaluation/confirm/%li",evaluationID]
parameters:params];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"[DataService :: sendEvaluationConfirmation] Response: %@", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
CLSLog(@"Error: %@", error);
}];
[operation start];
}
- (void) sendImage:(NSInteger)evaluationID formID:(NSInteger)formID imgName:(NSString *)imgName imgData:(NSData *)imgData completion:(void (^)(id, NSError *))completion
{
CLSLog(@"[DataService] Attempting to Send Photo");
// Initialize HTTPClient
NSURL *baseURL = [NSURL URLWithString:portalAPIUrl];
AFHTTPClient* client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
//we want to work with JSON-Data
[client setDefaultHeader:@"Accept" value:RKMIMETypeJSON];
RKObjectManager *manager = [[RKObjectManager alloc] initWithHTTPClient:client];
[manager setRequestSerializationMIMEType:RKMIMETypeJSON];
// attempt to send photos
// API URL - ems/images/{company}/{tagNumber}/{moveLogID}/{salesID}
NSString *imgPath = [NSString stringWithFormat:@"images/%ld/%ld/", (long)formID, (long)evaluationID]; // include evaluationID in API Path
NSDictionary *params = @{
@"apiKey" : portalAPIKey,
@"company" : [[NSUserDefaults standardUserDefaults] valueForKey:@"company"]
};
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[BasicResponse class]];
[responseMapping addAttributeMappingsFromDictionary:@{
@"message": @"message",
@"success": @"success",
@"recordID": @"recordID"
}];
RKResponseDescriptor *imageResponse = [RKResponseDescriptor responseDescriptorWithMapping:[responseMapping inverseMapping]
method:RKRequestMethodPOST
pathPattern:imgPath
keyPath:nil
statusCodes: RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
];
[manager addResponseDescriptor:imageResponse];
NSMutableURLRequest *request = [manager multipartFormRequestWithObject:nil method:RKRequestMethodPOST path:imgPath parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData)
{
// convert image to base64
NSString *fileName = [NSString stringWithFormat:@"%@.jpg",imgName];
[formData appendPartWithFileData:imgData
name:imgName
fileName:fileName
mimeType:@"image/jpeg"];
}];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:60];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPAdditionalHeaders = @{
@"Content-Type" : @"application/json"
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:nil completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
// report any network-related errors
if (!data) {
if (completion) {
completion(nil, error);
}
return;
}
if (!error) {
// report any errors parsing the JSON
NSError *parseError = nil;
NSMutableDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&parseError];
if (!result) {
if (completion) {
completion(nil, parseError);
}
return;
}
// if everything is ok, then just return the JSON object
if (completion) {
completion(result, nil);
}
}else{
RKLogError(@"Operation failed with error: %@", error);
CLSLog(@"[DataService sendImages] ERROR - %@",[NSString stringWithFormat:@"%@", error.localizedDescription]);
if (completion) {
completion(nil, error);
}
return;
}
}];
[task resume];
}
- (void)sendImageConfirmation:(NSInteger)imageID
{
NSLog(@"[DataService] sendImageConfirmation");
// update database record on server with new device identifier
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:portalAPIUrl]];
NSDictionary *params = @{@"apiKey":portalAPIKey};
[httpClient setParameterEncoding:AFFormURLParameterEncoding];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
path:[NSString stringWithFormat:@"images/confirm/%li",imageID]
parameters:params];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"[DataService :: sendImageConfirmation] Response: %@", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
CLSLog(@"Error: %@", error);
}];
[operation start];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment