UIlabel text not getting update when download files in progress - ios

I am trying to update the `UILabel` i.e downloaded data and remeaning data to be downloaded estimated time and total size of the downloading files via `NSnotificationCenter`, but not being updated `UILabel` text Please help me on this.
Also tried putting the `NSnotificationCenter` block in the main thread but no result found.
I have tried like this:
- (AFHTTPRequestOperation )downloadMediaOperation:(ILSCDowloadMedia )media success:(void (^)(ILSCDowloadMedia *media))success {
if (media.mediaUrl.length == 0) nil;
__block NSString *mediaKey = [[NSUserDefaults standardUserDefaults] objectForKey:media.mediaUrl];
NSURL *url = [NSURL URLWithString:media.mediaUrl];
if (mediaKey.length == 0) {
mediaKey = [NSString stringWithFormat:#"%#.%#", [ILSCUtility createUUID], [[[url path] lastPathComponent] pathExtension]];
}
NSFileManager *fileManager= [NSFileManager defaultManager];
NSString *mediaFilePath = NIPathForDocumentsResource(mediaKey);
media.mediaFilePath = mediaFilePath; if (![fileManager fileExistsAtPath:mediaFilePath]) {
__weak ILSCSyncManager *weakSelf = self;
NSURLRequest *request = [self.HTTPClient requestWithMethod:#"GET" path:[url path] parameters:nil];
AFHTTPRequestOperation *downLoadOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
downLoadOperation.downloadSpeedMeasure.active = YES; [downLoadOperation setShouldExecuteAsBackgroundTaskWithExpirationHandler:^{
// Clean up anything that needs to be handled if the request times out
// It may be useful to initially check whether the operation finished or was cancelled
}];
downLoadOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:mediaFilePath append:NO];
[downLoadOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[[NSUserDefaults standardUserDefaults] setObject:mediaKey forKey:media.mediaUrl];
[[NSUserDefaults standardUserDefaults] synchronize];
if (success) {
success(media);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NIDERROR(#"y error %#", [error localizedDescription]);
__strong ILSCSyncManager *strongSelf = weakSelf;
strongSelf.numberOfDownloadErrors++;
}];
[downLoadOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
NSLog(#"vvv Byted total expected to read %f",totalImagesBytesExpectedToRead);
totalImagesBytesRead += bytesRead;
humanReadableSpeed = downLoadOperation.downloadSpeedMeasure.humanReadableSpeed;
humanReadableRemaingTime = [downLoadOperation.downloadSpeedMeasure humanReadableRemainingTimeOfTotalSize:totalImagesBytesExpectedToRead numberOfCompletedBytes:totalImagesBytesRead];
NSLog(#"Speed Human %#",humanReadableSpeed);
NSLog(#"Time is human read %#",humanReadableRemaingTime);
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"UpdateProgressBar" object:[NSString stringWithFormat:#"%#-%f-%f-%#", humanReadableSpeed,totalImagesBytesRead,totalImagesBytesExpectedToRead,humanReadableRemaingTime]];
});
}];
return downLoadOperation;
} else {
if (success) {
success(media);
}
}
return nil;
}
Please help me on this.
This is the listener of the NSnotification please check and please let me know.
I add this class as Loader while once down load starts.
I have gone through some of the sites as i got some information NSOperation queue is runs in the background thread . i am not sure on this please help me .
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:#"UpdateProgressBar" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
NSString *str =[note object]; NSArray *arrayTotalOperationsIn = [str componentsSeparatedByString:#"-"];
NSLog(#"%#",arrayTotalOperationsIn); self.lblSpeedMeasure.text =[NSString stringWithFormat:#"Internet Speed - %#" ,[arrayTotalOperationsIn objectAtIndex:0]];
float bytesRead = [[arrayTotalOperationsIn objectAtIndex:1] floatValue];
float bytesExpectedToRead = [[arrayTotalOperationsIn objectAtIndex:2] floatValue];
NSString *timeExpectedToRead = [arrayTotalOperationsIn objectAtIndex:3];
self.progressCountTextLabel.text=[NSString stringWithFormat:#"%.2f MB/%.2f MB - %# Left",bytesRead/1000000,bytesExpectedToRead/1000000,timeExpectedToRead];
}];
The above is the listener of the NSnotification please check and please let me know.
I add this class as Loader while once down load starts.
I have gone through some of the sites as i got some information NSOperation queue is runs in the background thread . i am not sure on this please help me .

Try calling the setNeedsDisplay method on your UILabel after setting the text
[self.progressCountTextLabel setNeedsDisplay];

Related

Retrieving latest version of component from within AppDelegate fails

I am pretty new to iOS development, and entered a position where I need to maintain a large existing project in obj-c.
I have a sidebar-menu which is a webview. When program starts it makes a url request to check whether there is a newer version of the menu, and in that case retrieves the latest version.
Right now when the app runs for the first time it shows the old version, and from the second time and on it shows the current version.
When I tried debugging I've seen that the method that compares between local and remote version gets an empty value for the remote version. As far as I can understand it, the url request for the latest version is async, and therefore the code continues to execute before the request returns the current version.
Following an answer from StackOverflow, I've tried to call the getDataConfiguration method from within viewDidLoad instead of from AppDelegate, but that didn't work.
Would appreciate any help!
relevant code:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { .
...
[DataManager getDataConfiguration:^(DataConfiguration *dataConfiguration, NSError *error) {
[AppData sharedInstance].dataConfiguration=dataConfiguration;
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:dataConfiguration];
[standardDefaults setObject:encodedObject forKey:DATA_KEY];
[standardDefaults synchronize];
}];
[DataManager getProductMap:^(ProductsArray *products, NSError *error) {
[AppData sharedInstance].productsArray=products;
}];
DataManager.m
+(void)getDataConfiguration:(void (^)(DataConfiguration * dataConfiguration, NSError *error))completion
{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:[Configuration sharedInstance].infoJSONURL parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
DataConfiguration * dataConfiguration = [DataConfiguration modelObjectWithDictionary:responseObject];
completion(dataConfiguration,nil);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
+(void)updateHtmlFiles:(void (^)(NSError *error))completion{
float upToDateMenuVersion = [[AppData sharedInstance] dataConfiguration].general.menuVersion;
float localMenuVersion = [self getLocalMenuVersion];
if(upToDateMenuVersion != localMenuVersion){
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSString *url = [NSString stringWithFormat:#"%#?v=%f", [Configuration sharedInstance].menuHTMLFileURL, [[NSDate new] timeIntervalSince1970]];
[manager GET:url parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *htmlFiles = [userDefaults dictionaryForKey:#"HTML_FILES"];
NSMutableDictionary *mutableHtmlFiles = [NSMutableDictionary new];
NSString *myString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
[mutableHtmlFiles setValue:myString forKey:#"MENU"];
[userDefaults setObject:mutableHtmlFiles forKey:#"HTML_FILES"];
[self setLocalMenuVersion:upToDateMenuVersion];
completion(nil);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *htmlFiles = [userDefaults dictionaryForKey:#"HTML_FILES"];
if(htmlFiles == nil){
NSString *menuFile = [[NSBundle mainBundle] pathForResource:#"menu" ofType:#"html"];
htmlFiles = #{#"MENU":[NSString stringWithContentsOfFile:menuFile encoding:NSUTF8StringEncoding error:nil]};
[userDefaults setObject:htmlFiles forKey:#"HTML_FILES"];
}
NSLog(#"Error: %#", error);
}];
}
}
+(void) setLocalMenuVersion: (float) version{
[[NSUserDefaults standardUserDefaults] setFloat:version forKey:#"menuVersion"];
}
+(float) getLocalMenuVersion {
return [[NSUserDefaults standardUserDefaults] floatForKey:#"menuVersion"];
}
Menu.m
- (void)viewDidLoad {
[super viewDidLoad];
_firstLoad = YES;
...
[self initWebView];
}
-(void) initWebView {
if(_webView == nil){
_webView = [[WKWebView alloc] initWithFrame:_webViewPlaceholder.frame];
[_webView.scrollView setZoomScale:3 animated:YES];
_webView.navigationDelegate = self;
_webView.UIDelegate = self;
NSString *javaScriptText = #"document.body.style.zoom = 3;";
[_webView evaluateJavaScript:javaScriptText completionHandler:nil];
[self.view addSubview:_webView];
_webView.scrollView.bounces = NO;
[self updateHtml];
[AppData updateHeaderAndMenu:^(NSError *error){
[self updateHtml];
}];
}
}
- (void)viewDidAppear:(BOOL)animated{
_webView.frame = CGRectMake(_webViewPlaceholder.frame.origin.x,_webViewPlaceholder.frame.origin.y, _webViewPlaceholder.frame.size.width, _webViewPlaceholder.frame.size.height);
}
-(void)updateHtml{
NSDictionary *htmlFiles = [AppData getHeaderAndMenu];
NSString *menu = [htmlFiles objectForKey:#"MENU"];
[_webView loadHTMLString:menu baseURL: [[NSBundle mainBundle] bundleURL]];
}
AppData.m
+(void)updateHeaderAndMenu:(void (^)(NSError *error))completion{
[DataManager updateHtmlFiles:completion];
}
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
...
[AppData updateHeaderAndMenu:^(NSError *error){ [self loadHeader]; }];
_firstLoad = YES;
...
The updateHeaderAndMenu method has a completion block which is called after the async operation completes without an error.
I'm assuming ViewController.m holds a reference to Menu?
If that is the case, viewDidLoad calls the updateHeaderAndMenu method and will execute the completionBlock (if there is no error). In this block I can already see that a method is called loadHeader. You could call [self.menu updateHtml]; there and this would probably work.
...
[AppData updateHeaderAndMenu:^(NSError *error){
[self loadHeader];
// [self.menu updateHtml];
}];
_firstLoad = YES;
...
I'm doing some guess work here but I think this would update your webview after the DataManager completes the http request.
Edit:
As to the order of execution. Here is a breakdown:
This is the method definition in AppData
+(void)updateHeaderAndMenu:(void (^)(NSError *error))completion{
[DataManager updateHtmlFiles:completion];
}
You can see completion (which is a block parameter) is passed on to the updateHtmlFiles method in DataManager:
+(void)updateHtmlFiles:(void (^)(NSError *error))completion{
...
completion(nil);
...
}
Eventually the completion parameter (which is a block) is called when the async http request completes. You can look at blocks as kind of inline methods which can be passed as a parameter. Google working with blocks ios to see the official Apple documentation for this.
So the order of execution is:
Menu calls updateHeaderAndMenu in AppData
which calls updateHtmlFiles in DataManager and passes on completion
http request completes and calls completion.
the content of the block is executed all the way back in Menu which is:
{
[self loadHeader];
// [self.menu updateHtml];
}
loadHeader is executed ...
If you want to get a better overview of what is called when, you can use breakpoints inside your code.

Memory pressure due to download and saving of images

Fortunately I know where my memory pressure issue is coming from, and I have tried a number of techniques such as wrapping a block in an #autorelease block and setting objects to nil but still no success.
Sorry for dumping too much code here, I tried to cut it down to the essentials. Here is the code for downloading and saving images:
NSMuttableArray *photosDownOps = [NSMuttableArray array];
NSURL *URL = [...];
NSURLRequest *request = [...];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFImageResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
dispatch_queue_t amBgSyncQueue = dispatch_queue_create("writetoFileThread", NULL);
dispatch_async(amBgSyncQueue, ^{
[self savePhotoToFile:(UIImage *)responseObject usingFileName:photo.id];
});
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if ([error code] != NSURLErrorCancelled)
NSLog(#"Error occured downloading photos: %#", error);
}];
[photosDownOps addObject:op];
NSArray *photosDownloadOperations = [AFURLConnectionOperation batchOfRequestOperations:photosDownloadOperatons
progressBlock:^(NSUInteger nof, NSUInteger tno) {
} completionBlock:^(NSArray *operations) {
NSLog(#"all photo downloads completed");
}];
[self.photosDownloadQueue addOperations:photosDownloadOperations waitUntilFinished:NO];
+ (void) savePhotoToFile:(UIImage *)imageToSave usingFileName:(NSNumber *)photoID{
#autoreleasepool {
NSData * binaryImageData = UIImageJPEGRepresentation(imageToSave, 0.6);
NSString *filePath = [Utilities fullPathForPhoto:photoID];
[binaryImageData writeToFile:filePath atomically:YES];
binaryImageData = nil;
imageToSave = nil;
}
}
This situation though only happens with iPhone 4s devices that I have tested on, it does not happen on iPhone 5 models.
I managed to solve this by extending NSOperation and within the main block immediately after I receive the data I write it out to file:
- (void)main{
#autoreleasepool {
//...
NSData *imageData = [[NSData alloc] initWithContentsOfURL:imageUrl];
if (imageData) {
NSError *error = nil;
[imageData writeToFile:imageSavePath options:NSDataWritingAtomic error:&error];
}
//...
}
}
This NSOperation object was then added a NSOperationQueue I already had.
Try to create your own class to download image using NSUrlConnection and in the delegate method append that data to your file just see the below code
-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:aPath];
[fileHandle seekToEndOfFile];
[fileHandle writeData:data];
[fileHandle closeFile];
}
This will help you in memory management as all the data which is download is not need to cache .

AFNetworking Memory

Using AFNetworking to download files from a server. Here's the code:
self.networkQueue = [[[NSOperationQueue alloc] init] autorelease];
[networkQueue setMaxConcurrentOperationCount:3];
for(NSDictionary* fileDictionary in self.syncArray) {
#autoreleasepool {
if([[fileDictionary allKeys] containsObject:#"downloadZipURL"]) {
NSString* downloadPath = [fileDictionary objectForKey:#"downloadZipURL"];
downloadPath = [downloadPath stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURLRequest *requestURL = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadPath]];
NSString* localDestPath = [NSString stringWithFormat:#"%#/%#", [FileUtil userDocumentsDirectory], [downloadPath lastPathComponent]];
NSString* localTempPath = [NSString stringWithFormat:#"%#.tmp", localDestPath];
[(NSMutableDictionary*)fileDictionary setObject:localDestPath forKey:#"downloadDestination"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:requestURL];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:localDestPath append:NO];
operation.userInfo = fileDictionary;
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (networkQueue.operationCount == 0)
{
if(hasDownloadError || isCancellingSync) {
return ;
}
[self performSelectorInBackground:#selector(processAllFiles) withObject:nil];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
// [operation setDownloadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
// NSLog(#"Sent %lld of %lld bytes, %#", totalBytesWritten, totalBytesExpectedToWrite, localDestPath);
// float progress = (float)totalBytesWritten/(float)totalBytesExpectedToWrite;
// [(NSMutableDictionary*)operation.userInfo setObject:[NSString stringWithFormat:#"Downloading %.0f%%", progress*100] forKey:#"downloadStatus"];
// [(NSMutableDictionary*)operation.userInfo setObject:[NSNumber numberWithFloat:progress] forKey:#"downloadProgress"];
// [syncViewController onPermitUpdated];
// }];
[networkQueue addOperation:operation];
}
}
}
My problem is that once this code is run, memory slowly gets eaten up and never given back. Now, these can be large files, which is why I used the outputStream.
Any suggestions would be appreciated.
Off the top of my head - I see that you're not using ARC.
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:requestURL]
Are you releasing this operation somewhere?
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (networkQueue.operationCount == 0)
{
if(hasDownloadError || isCancellingSync) {
return ;
}
[self performSelectorInBackground:#selector(processAllFiles) withObject:nil];
}
Here, you're using the networkQueue in the completionBlock and the block retains the networkQueue, you then add the operation to the networkQueue, which retains the operation, which leads to neither of them deallocating. Try making a weak variable of the networkQueue and use that in order to break the cycle.
If these don't work - run instruments and make a note of what objects remain in memory and when their reference count is changed.

How to kill NSThread while clicking on new tab iOS?

I building an app has 4 tab (Tabbar Controller), and each tab I call a function (updateArray) after 2s. I want to when click on other tab, updateArray() function is kill. My problem is when on tab, updateArray() call after 2s, when I click on other tab, this function is still call.
This is updateArray()
-(void)updateArray{
while (loop)
{
[NSThread sleepForTimeInterval:2.0];
[FileCompletedArray removeAllObjects];
[temp removeAllObjects];
[UserNameArray removeAllObjects];
NSURL *url1 = [NSURL URLWithString:#"server"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL: url1] ;
NSMutableURLRequest *afRequest = [httpClient requestWithMethod:#"POST" path:nil parameters:params1] ;
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success");
NSString * parsexmlinput = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Response in Loop CompleteView: %#", parsexmlinput); //000400010001
// dispatch_async(dispatch_get_main_queue(), ^{
[self parseXMLFile:parsexmlinput];
NSLog(#"File Completed array: %#", FileCompletedArray);
NSLog(#"File Temp out array: %#", temp);
NSLog(#"File Completed count: %lu",(unsigned long)[ FileCompletedArray count]);
NSLog(#"File Temp out count: %lu", (unsigned long)[temp count]);
if([FileCompletedArray count] != [temp count])
{
temp = [FileCompletedArray mutableCopy];
NSLog(#"File Temp 1 array: %#", temp);
[_tableView reloadData];
NSLog(#"File Temp 2 array: %#", temp);
}
[alert dismissWithClickedButtonIndex:0 animated:YES];
//});
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", error);
}
];
[httpClient enqueueHTTPRequestOperation:operation];
}
}
And in viewwillappear()
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
loop = YES;
temp = [FileCompletedArray mutableCopy];
[self performSelectorInBackground:#selector(updateArray) withObject:nil ];
}
In my function, i used [NSThread sleepForTimeInterval:2.0];, I don't know how to kill it. Do you have suggestions ? Thanks in advance
You shouldn't really use sleepForTimeInterval, you should use performSelector:withObject:afterDelay: (and cancelPerformSelectorsWithTarget:) or dispatch_after.
As it is, you can add a BOOL attribute that is used to decide if the thread should continue after the sleep or whether it should exit (return).
To control any thread you have to use NSOperation Using this you can control any running thread.
Create a BOOL when you click on another tab set it to FALSE. Use this with dispatch after.
#property (nonatomic, assign) BOOL doUpdate;
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(doUpdate){
//update work.
}
});
You should take look at using NSOperation and then you could call [NSOperation cancelAllOperations] when clicking on a another tab.
Good luck,
Booranger

AFNetworking for Image Downloads, Unresponsive UI

I'm using AFNetworking to pull images from a URL, resize, store to disk and log the path in Core Data, then load to a table view and store . When the code executes it freezes my UI. I'm not sure if it's the download or the manipulation that's causing my troubles.
The code I'm using is below
- (void)getPhoto:(NSInteger)type forManagedObject:(MyManagedObject*)object {
// download the photo
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:object.photoUrl]];
AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request success:^(UIImage *image) {
// MyManagedObject has a custom setters (setPhoto:,setThumb:) that save the
// images to disk and store the file path in the database
object.photo = image;
object.thumb = [image imageByScalingAndCroppingForSize:CGSizeMake(PhotoBlockCellButtonWidth, PhotoBlockCellButtonHeight)];
NSError *nerror;
if (![[DataStore sharedDataStore].managedObjectContext save:&nerror]) {
NSLog(#"Whoops, couldn't save: %#", [nerror localizedDescription]);
return;
}
// notify the table view to reload the table
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadTableView" object:nil];
}];
[operation start];
}
And here is a sample code relevant to the setter from my managed object
- (NSString*)uniquePath{
// prepare the directory string
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// acquire a list of all files within the directory and loop creating a unique file name
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *existingFiles = [fileManager contentsOfDirectoryAtPath:documentsDirectory error:nil];
NSString *uniquePath;
do {
CFUUIDRef newUniqueId = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef newUniqueIdString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueId);
uniquePath = [[documentsDirectory stringByAppendingPathComponent:(__bridge NSString *)newUniqueIdString] stringByAppendingPathExtension:#"png"];
CFRelease(newUniqueId);
CFRelease(newUniqueIdString);
} while ([existingFiles containsObject:uniquePath]);
return uniquePath;
}
- (NSString*)saveImage:(UIImage*)image{
NSString *path = [self uniquePath];
NSData *data = UIImagePNGRepresentation(image);
[data writeToFile:path atomically:YES];
return [NSString stringWithFormat:#"file://%#",path];
}
- (void) setPhoto:(UIImage *)image {
self.photoUrl = [self saveImage:image];
}
I would like to push this to a background thread, but I'm not sure what the implications are with AFNetworking, Core Data, and Messaging in terms of thread safety. Any thought?
AFAIK, the way you are executing your request in incorrect:
[operation start];
you should instead add the operation to an NSOperationQueue:
NSOperationQueue* operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:operation];
(you should correctly memory-manage the queue).
By doing like this, your request will be executed in an async way, it won't block the UI and you will not need to deal with multithreading.
Based on Matt's suggestion, I improved the UI by reworking my call as follows.
- (void)getPhoto:(NSInteger)type forManagedObject:(MyManagedObject*)object {
// download the photo
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:object.photoUrl]];
AFImageRequestOperation *operation = [AFImageRequestOperation
imageRequestOperationWithRequest:request
imageProcessingBlock:^UIImage *(UIImage *image) {
return [image imageByScalingAndCroppingForSize:CGSizeMake(PhotoBlockCellButtonWidth, PhotoBlockCellButtonHeight)];
}
cacheName:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
// MyManagedObject has a custom setters (setPhoto:,setThumb:) that save the
// images to disk and store the file path in the database
object.photo = image;
object.thumb = image;
NSError *nerror;
if (![[DataStore sharedDataStore].managedObjectContext save:&nerror]) {
NSLog(#"Whoops, couldn't save: %#", [nerror localizedDescription]);
return;
}
// notify the table view to reload the table
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadTableView" object:nil];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"Error getting photo");
}];
[operation start];
}

Resources