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.
Related
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];
first I'd like to say that the issue I am having is in regards to how my own code is set, not IOS or AFNetworking.
I am going to show 1 example where a view is not refreshing immediately even though the updated JSON response object from the server is being recieved.
There are 2 views being used in the first view the button below takes the user to a second view where he can upload an image that will then be displayed in the first view.
- (IBAction)editImage:(id)sender {
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
TDUserProfileViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"TDUserProfileImageEditViewController"];
[self.navigationController pushViewController:vc animated:YES];
}
Below is the code that uploads the new data to the server and then gets new data regarding the images's address on the server.
- (IBAction)dataSubmitToServer{
// NSLog(#"photo: %#", self.photoData);
NSString * userID = [[NSUserDefaults standardUserDefaults] objectForKey:USERID];
//make the call to the web API
NSString* command = #"setProfilePhoto";
self.params =[NSMutableDictionary dictionaryWithObjectsAndKeys:command, #"command", userID, #"userid", nil];
//////////////////
NSLog(#"%#", self.params);
self.photoName = #"newname.png";
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager POST:BaseURLString parameters:self.params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:UIImageJPEGRepresentation(self.userPhoto.image, 1)
name:#"image"
fileName:self.photoName
mimeType:#"image/jpeg"];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error localizedDescription]);
[TDPublicFunctions showDefaultAlert:#"Error" body:[error description]];
}];
[self getNewProfileData];
[[NSUserDefaults standardUserDefaults] synchronize];
}
-(void)getNewProfileData{
/*start getting new data for profile pic and status message*/
NSString* command = #"displayProfileData";
NSString * userID = [[NSUserDefaults standardUserDefaults] objectForKey:USERID];
NSMutableDictionary* params =[NSMutableDictionary dictionaryWithObjectsAndKeys:command, #"command", userID,#"userid", nil];
NSLog( #"%#", params);
[SVProgressHUD showWithStatus:#"Loading..." maskType:SVProgressHUDMaskTypeBlack];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:BaseURLString parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
[SVProgressHUD dismiss];
if([responseObject objectForKey:#"error"])
{
[[[UIAlertView alloc] initWithTitle:#"Data Retrieval Error" message:[responseObject description] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
}
else
{
NSString * profilePhoto = [[[responseObject objectForKey:#"result"] objectAtIndex:0] objectForKey:#"profile_picture"];
NSString * status = [[[responseObject objectForKey:#"result"] objectAtIndex:0] objectForKey:#"status"];
NSString * statusMsg = [[[responseObject objectForKey:#"result"] objectAtIndex:0] objectForKey:#"status_message"];
if(profilePhoto && ![profilePhoto isKindOfClass:[NSNull class]])
[[NSUserDefaults standardUserDefaults] setObject:profilePhoto forKey:PROFILE_PHOTO];
if(status && ![status isKindOfClass:[NSNull class]])
[[NSUserDefaults standardUserDefaults] setObject:status forKey:STATUS];
if(statusMsg && ![statusMsg isKindOfClass:[NSNull class]])
[[NSUserDefaults standardUserDefaults] setObject:statusMsg forKey:STATUS_MESSAGE];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
//end of getting data for status message and profile pic
}
Now once this above process is over the HUD is supposed to stop and when the back button on the view is hit there should be a newly uploaded image in the first view. The code below is the last of many things that I have tried in order to get the view with the image to refresh:
-(void) viewWillAppear:(BOOL)animated
{
NSString * profPic = [[NSUserDefaults standardUserDefaults] objectForKey:PROFILE_PHOTO];
NSLog(#"profilepic: %#", profPic);
// NSString* status = [[NSUserDefaults standardUserDefaults] objectForKey:STATUS];
NSString * statusMsg = [[NSUserDefaults standardUserDefaults] objectForKey:STATUS_MESSAGE];
if(profPic)
[photo loadIconForProduct:profPic];
if(statusMsg)
statusMsgView.text = statusMsg;
//self.view=nil;
//[self viewDidLoad];
[self getFriendsList];
[photo setNeedsDisplay];
[self.view setNeedsDisplay];
}
After an image is uploaded it may work the first time an image is uploaded but it does not work if the user were to press the editImage button a second time and upload another image.
What is it that is being done wrong? Let me know if there is anything else that I can supply.
Thanks.
As I can see here, you are getting a URL back as the result of the profilePhoto. So you might as well use the AFNetworking function:
Im presuming photo is a UIImageView.
[photo setImageWithURL:[NSURL URLWithString:profPic] placeholderImage:[UIImage imageNamed:#"somePlaceholderImage"]];
I am coding a Abstract class to fetch data from multiple social networks. After thinking all the stuff I would need to use in this class that would make sense to use it in other subclasses from that class I've started to write a simple auth_token fetcher to get authorization from facebook to get all the statuses updates from a page profile. I am failing to see what would be the best approach to make code work:
+ (NSString *)requestAuthToken {
NSString *authTokenKey = [[self socialNetworkName] stringByAppendingString:#"AuthToken"];
NSString *authTokenDateKey = [[self socialNetworkName] stringByAppendingString:#"AuthTokenDate"];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
__block NSString *authToken = [userDefaults stringForKey:authTokenKey];
NSDate *authTokenDate = (NSDate* )[userDefaults objectForKey:authTokenDateKey];
NSTimeInterval expirationDate = [authTokenDate timeIntervalSinceNow];
NSTimeInterval now = [[NSDate date] timeIntervalSinceNow];
NSTimeInterval dateDiff = expirationDate - now;
int diff = roundf(dateDiff / (60 * 60 * 24));
if (authToken == nil|| diff >= kMaxDaysExpiratonForAuthToken) {
DLog(#"AuthToken not Cached, requesting token with %#", [self socialNetworkName]);
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:[self socialNetworkAPIURL]]];
NSDictionary *parameters = [self socialNetworkAPIAuthTokenParameters];
[httpClient getPath:[self socialNetworkAPIAuthTokenURLPath] parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
authToken = [NSString stringWithUTF8String:[responseObject bytes]];
[userDefaults setObject:[NSDate date] forKey:authTokenDateKey];
[userDefaults setObject:authToken forKey:authTokenKey];
// [userDefaults synchronize];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
DLog(#"error requesting the token %#", error);
}];
}
return authToken;
}
I am getting the return always nill;
Blocks are executed asynchronously. So, you should pass the object of the class in request function. On success or failure, block will return the result using that object.
+ (NSString *)requestAuthToken:(id)objOfCallerClass {
....
if (authToken == nil|| diff >= kMaxDaysExpiratonForAuthToken) {
....
[objOfCallerClass accessToken: authToken];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[objOfCallerClass accessToken: nil];
}];
}
return authToken;
}
I have a view controller, that loads some an array. While everything is loading, I need to present another view controller (with the UIProgressView) and update it's UI (the progress property of a UIProgressView) and then dismiss and present first vc with downloaded data. I'm really struggling on it and I've tried delegation, but nothing worked for me.
- (void)viewDidLoad
{
[super viewDidLoad];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"downloaded"]) {
} else {
NSLog(#"First time Launched");
ProgressIndicatorViewController *progressVC = [ProgressIndicatorViewController new];
progressVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self syncContacts];
[self presentViewController:progressVC animated:YES completion:nil];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"downloaded"];
[progressVC release];
}
}
sync contacts method:
- (void)syncContacts
{
NSLog(#"Sync data");
NSMutableArray *allContacts = [ContactsOperations getAllContactsFromAddressBook];
NSInteger allContactsCount = [allContacts count];
if (allContactsCount > 0) {
for (ContactData *contact in allContacts) {
NSMutableArray *phoneNumbersArray = [[NSMutableArray alloc] init];
NSString *nospacestring = nil;
for (UserTelephone *tel in [contact.abonNumbers retain]) {
NSArray *words = [tel.phoneNumber componentsSeparatedByCharactersInSet :[NSCharacterSet whitespaceCharacterSet]];
NSString *nospacestring = [words componentsJoinedByString:#""];
[phoneNumbersArray addObject:nospacestring];
}
contact.abonNumbers = phoneNumbersArray;
if (phoneNumbersArray != nil) {
NSLog(#"NOT NULL PHONENUMBERS: %#", phoneNumbersArray);
}
NSDictionary *dataDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:contact.abonNumbers, #"phoneNumbers", contact.contactName, #"fullName", [NSNumber numberWithBool:contact.isBlackList], #"blacklist", [NSNumber numberWithBool:contact.isIgnore], #"ignore", contact.status, #"status", nil];
NSLog(#"dictionary: %#", dataDictionary);
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:dataDictionary options:0 error:&error];
NSLog(#"POST DATA IS : %#", postData);
NSMutableURLRequest *newRequest = [self generateRequest:[[NSString stringWithFormat:#"%#c/contacts%#%#", AVATATOR_ADDR, SESSION_PART, [[ServiceWorker sharedInstance] SessionID]] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding] withHTTPMethod:#"POST"];
[newRequest setHTTPBody:postData];
[newRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
//__block NSMutableData *newData;
[NSURLConnection sendAsynchronousRequest:newRequest queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
if (!connectionError) {
NSDictionary *allData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"alldata from contacts: %#", allData);
//NSInteger errorCode = [[allData objectForKey:#"CommandRes"] integerValue];
//if (errorCode == 0) {
NSInteger remoteId = [[allData objectForKey:#"contactId"] integerValue];
contact.remoteId = remoteId;
NSLog(#"remote id is from parse content : %d", remoteId);
[[AvatatorDBManager getSharedDBManager]createContactWithContactData:contact];
} else {
NSLog(#"error");
}
}];
//Somewhere here I need to update the UI in another VC
[phoneNumbersArray release];
[dataDictionary release];
}
} else {
}
}
generate request method:
- (NSMutableURLRequest *)generateRequest:(NSString *)urlString withHTTPMethod:(NSString *)httpMethod
{
NSLog(#"url is :%#", urlString);
NSURL *url = [NSURL URLWithString:urlString];
request = [NSMutableURLRequest requestWithURL:url];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[request setHTTPMethod:httpMethod];
return request;
}
ProgressViewController is just an empty VC with the progress bar. No code yet.
In the view controller that will display the progress view expose a method like this...
- (void)updateProgress:(float)progress;
Its implementation will look like this...
- (void)updateProgress:(float)progress {
[self.progressView setProgress:progress animated:YES];
}
On the main view controller you need to execute the long-running process on a background thread. Here's viewDidLoad for the main view controller. This example code uses a property for the progress view controller (you may not require this) and assumes your are in a navigation controller...
- (void)viewDidLoad {
[super viewDidLoad];
// Create and push the progress view controller...
self.pvc = [[ProgressViewController alloc] init];
[self.navigationController pushViewController:self.pvc animated:YES];
// Your long-running process executes on a background thread...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Your long-running process goes here. Wherever required you would
// call updateProgress but that needs to happen on the main queue...
dispatch_async(dispatch_get_main_queue(), ^{
[self.pvc updateProgress:progress];
});
// At the end pop the progress view controller...
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController popViewControllerAnimated:YES];
});
});
}
I´m trying to wait for the response using Restkit with Blocks.
Example:
NSArray *myArray = ["RESULT OF REST-REQUEST"];
// Working with the array here.
One of my Block-requests:
- (UIImage*)getPhotoWithID:(NSString*)photoID style:(NSString*)style authToken:(NSString*)authToken {
__block UIImage *image;
NSDictionary *parameter = [NSDictionary dictionaryWithKeysAndObjects:#"auth_token", authToken, nil];
RKURL *url = [RKURL URLWithBaseURLString:#"urlBase" resourcePath:#"resourcePath" queryParameters:parameter];
NSLog(#"%#", [url absoluteString]);
[[RKClient sharedClient] get:[url absoluteString] usingBlock:^(RKRequest *request) {
request.onDidLoadResponse = ^(RKResponse *response) {
NSLog(#"Response: %#", [response bodyAsString]);
image = [UIImage imageWithData:[response body]];
};
}];
return image;
}
You can't return anything in this method since the getting of the image will be asynchronous - it must be -(void).
So, what do you do? You should put the action calling this method inside the response block. Be wary of retain cycles in the block.
__block MyObject *selfRef = self;
[[RKClient sharedClient] get:[url absoluteString] usingBlock:^(RKRequest *request) {
request.onDidLoadResponse = ^(RKResponse *response) {
NSLog(#"Response: %#", [response bodyAsString]);
image = [UIImage imageWithData:[response body]];
[selfRef doSomethingWithImage:image];
};
}];
The code above won't work with ARC turned on (XCode default since iOS 5.0). __block variables are no longer exempted from auto-retain under ARC. Use __weak instead of __block in iOS 5.0 and above to break the retain cycle.