Inside the following method I am saving the current temp of a location, I want to be able to access the temp anywhere inside the class.
[_weatherAPI currentWeatherByCoordinate:CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude) withCallback:^(NSError *error, NSDictionary *result) {
downloadCount++;
if (downloadCount > 1)
if (error) {
}
[_locationManager stopUpdatingLocation];
self.tempLabel.text = [NSString stringWithFormat:#"%.0f°",
[result[#"main"][#"temp"] floatValue] ];
self.locationLabel.text = [NSString stringWithFormat:#"%#",
result[#"name"]
];
self.summaryLabel.text = [NSString stringWithFormat:#"%#",
result[#"weather"][0][#"main"] ];
[self setSaveTemp:result[#"main"][#"temp"]];
}];
I am then saving it to a property
#property (strong, nonatomic) NSString *saveTemp;
And then saving it to this.
-(void)setSaveTemp:(NSString *)saveTemp {
_saveTemp = saveTemp;
}
In my viewDidLoad I am then calling everything
_weatherAPI = [[OWMWeatherAPI alloc] initWithAPIKey:#""];
[_weatherAPI setLangWithPreferedLanguage];
[_weatherAPI setTemperatureFormat:kOWMTempCelcius];
NSLog(#"%#", _saveTemp);
But this returns null as the viewDidLoad is getting called before the _weatherAPI currentWeatherByCoordinate what can I do to be able make _saveTemp not equal null but the actual value?
I think this api will make server call and so it takes time to get the json data.
once json data is available, then it will execute callback block.
saveTemp variable will be initially nil, and you are setting it in completion block.
All you can do is just wait until the download is finished.And if you want to execute some method after download,then call that method in callBack block. so, that way your app can know that download is finished.
Related
I am using UNIRest to make a call and return a JSON object to my app. I have it returning the proper data as a NSDictionary and it logs our perfect. I am now trying to take that data and display it inside of my view. I cannot use my dictionary outside of the callback.
I have been digging around here on StackOverflow for similar results and posts related to variables. I feel it is a scope issue with it being limited to inside of the callback block.
My header file: (UIViewController)
#property (nonatomic, strong) NSDictionary *tideData;
My implementation:
#interface TideDetailViewController ()
#end
#implementation TideDetailViewController
#synthesize tideData;
- (void)viewDidLoad {
[super viewDidLoad];
// tideData = [[NSDictionary alloc] init];
// location is working, I removed it for testing to call a static string for now
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[self.locationManager startUpdatingLocation];
NSString *locationQueryURL = #"http://api.wunderground.com/api/XXXXXXXXXXXXX/tide/geolookup/q/43.5263,-70.4975.json";
NSLog(#"%#", locationQueryURL);
[[UNIRest get:^(UNISimpleRequest *request) {
[request setUrl: locationQueryURL];
}] asJsonAsync:^(UNIHTTPJsonResponse *response, NSError *error) {
// This is the asyncronous callback block
self.code = [response code];
NSDictionary *responseHeaders = [response headers];
UNIJsonNode *body = [response body];
self.rawResults = [response rawBody];
// I tried this as self as well
tideData = [NSJSONSerialization JSONObjectWithData:self.rawResults options: 0 error: &error];
// this logs perfectly.
NSLog(#"tideData %#", tideData);
// tried setting it to the instance
//self.tideData = tideData;
}];
// returns null
NSLog(#"tideData outside of call back %#", self.tideData);
// this is where I will be setting label text for now, will refactor once I get it working
// rest of file contents........
I have tried a good amount of items related to scoping, clearly just missing the mark. Any ideas? I have searched setting global variables, etc. Been stuck on this for a bit now.
Thanks,
Ryan
The reason you see nil is because you are logging it too soon: when you call
NSLog(#"tideData outside of call back %#", self.tideData);
the get:asJsonAsync: method has not received the results yet.
You can fix this problem by adding a setter for your property, and adding some special handling to it, like this:
-(void)setTideData:(NSDictionary*)dict {
_tideData = dict;
NSLog(#"tideData outside of call back %#", _tideData);
}
This method will be called from the asynchronous code when you do the tideData = ... assignment.
Try setting the object on main thread:
[self performSelectorOnMainThread:#selector(setTideData:) withObject:[NSJSONSerialization JSONObjectWithData:self.rawResults options: 0 error: &error] waitUntilDone:NO];
- (void)setTideData:(NSDictionary*)dict {
self.tideData = dict;
}
I'm using the iOS motion manager and it works by using a block Handler. I have an "Acceleration" object with ...
#property (atomic, readonly) CMAccelerometerData * rawAccelerometerData;
In the .m file, I have...
-(void) startServices{
[_motionManager startAccelerometerUpdatesToQueue:_queue withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
_rawAccelerometerData = accelerometerData;
dispatch_async(dispatch_get_main_queue(),^(void){
[delegate accelerometerUpdate:accelerometer];
});
}];
}
The Delegate is a view controller:
-(void) accelerometerUpdate:(Accelerometer *) accelerometer {
_accelX.text = [NSString stringWithFormat:#"%f", accelerometer.rawAccelerometerData.acceleration.x];
_accelY.text = [NSString stringWithFormat:#"%f", accelerometer.rawAccelerometerData.acceleration.y];
_accelZ.text = [NSString stringWithFormat:#"%f", accelerometer.rawAccelerometerData.acceleration.z];
}
But when I access _rawAccelerometerData, I get a Bad Access Exception.
My guess is that the accelerometerData object is getting destroyed. How do I keep it?
This is a multi-threading issue. (Thanks holex for pointing me in that direction.)
_rawAccelerometerData is always being updated, and the new object is replacing the old object (which gets destroyed because of ARC).
On another thread, I am accessing _rawAccelerometerData. Occasionally, when I access _rawAccelerometerData, I am accessing an older 'copy' of the object. Since that object has already been destroyed, I get a Bad Access.
I'm using JSONModel to retrieve data from a simple webservice. I need to get the values of key #"message" into a mutable array.
- (void)viewDidLoad
{
[super viewDidLoad];
self.delegate = self;
self.dataSource = self;
NSString *conversationid = #"xx";
NSString *callURL = [NSString stringWithFormat:#"http://mydomain.com/inbox_messages.php?conversation=%#", conversationid];
_feed = [[MessageFeed alloc] initFromURLWithString:callURL
completion:^(JSONModel *model, JSONModelError *err)
{
self.messages = [[NSMutableArray alloc]initWithObjects:[_feed.messagesinconversation valueForKey:#"message"], nil];
NSLog(#"messages %#", self.messages);
}];
NSLog(#"test %#", self.messages);
}
The problem I'm experiencing is that while: NSLog(#"messages %#", self.messages); returns all the right data, NSLog(#"test %#", self.messages); returns (null).
The variable is declared in .h of my class as: #property (strong, nonatomic) NSMutableArray *messages;
This is probably a noob question but I'm a beginner and if somebody could give me a pointer in the right direction, I would be very happy.
Your NSLog for self.messages is outside of the completion block. The completion block is called after the data is loaded. The log is called immediately after creating the MessageFeed request. So, of course, the object self.messages is null because the request has not completed.
The solution to this would be to either handle all of your parsing within the completion block, or call another class method to parse the received data.
Your completion handler is being called after your NSLog("test %#", self.messages); is.
Blocks usually happen concurrently and when a certain event has occurred like the completion handler here or sometimes an error handler. By looking at your code you're probably getting something like:
test nil
messages
So your MessageFeed object is being run but it continues through your code and runs the NSLog outside of the completion handler scope first. When your JSON object has downloaded, which happens after, and parses it then runs the completion handler.
- (void)viewDidLoad
{
[super viewDidLoad];
self.delegate = self;
self.dataSource = self;
NSString *conversationid = #"xx";
NSString *callURL = [NSString stringWithFormat:#"http://mydomain.com/inbox_messages.php?conversation=%#", conversationid];
_feed = [[MessageFeed alloc] initFromURLWithString:callURL //this method takes some time to complete and is handled on a different thread.
completion:^(JSONModel *model, JSONModelError *err)
{
self.messages = [[NSMutableArray alloc]initWithObjects:[_feed.messagesinconversation valueForKey:#"message"], nil];
NSLog(#"messages %#", self.messages); // this is called last in your code and your messages has been has been set as an iVar.
}];
NSLog(#"test %#", self.messages); // this logging is called immediately after initFromURLWithString is passed thus it will return nothing
}
NSString *pictureUrl = [[[oneUserDict objectForKey:#"picture"]objectForKey:#"data"]objectForKey:#"url"];
[[AppEngine sharedEngine]imageAtURL:[NSURL URLWithString:pictureUrl] onCompletion:^(UIImage *fetchedImage, NSURL *url, BOOL isInCache)
{
int index = [usersArray indexOfObject:oneUserDict];
NSString *loadName = [NSString stringWithFormat:#"%d of %d",index,[usersArray count]];
NSLog(#"%i",usersArray.count);
int temp=[usersArray count]-10;
if (index!=temp)
{
[[LoadingIndicator currentIndicator]displayActivity:loadName];
NSLog(#"inside loading indicator");
}
else
{
[[LoadingIndicator currentIndicator]displayCompleted:#"Done"];
NSLog(#"finally done");
}
aPerson.image = UIImagePNGRepresentation(fetchedImage);
[appDelegate.managedObjectContext save:nil];
}];
AppEngine is the subclass of MKNetworkEngine which uses a method called imageAtURL:onCompletion:
what I am currently doing is retrieving all the images from a particular url and and storing them in aPerson.image,basically the above code is in a FOR loop(i.e the for the count of users).
Issues
The above code which is in the completion block never gets executed,i dont know why but i have put a breakpoint inside the block but still the compiler wont run the statements inside the completion block.
Api imageAtURL:onCompletion: is deprecated. Use imageAtURL:completionHandler:errorHandler: instead. Also MKNetworkKit provides for UIImageView+MKNetworkKitAdditions category which provides simple API for image download like setImageFromURL: placeHolderImage:
Cheers!
Amar.
I am creating a update method for in house enterprise apps. What I am trying to create is a class that I can quickly put in a app and it will check with the server if it needs to be updated. This is what I have so far, it checks correctly but its returning NO before it finishes the method.
In my checkUpdate.h
#interface checkForUpdate : NSObject
+ (BOOL)updateCheck;
#end
In my checkUpdate.m
#import "checkForUpdate.h"
#implementation checkForUpdate
BOOL needsUpdate
NSDictionary *versionDict;
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
+ (BOOL)updateCheck {
NSString *urlStringVersion = [[NSString alloc] initWithFormat:#"http://URL/app_info?app=app"];
NSURL *urlVersion = [NSURL URLWithString:urlStringVersion];
dispatch_async(kBgQueue, ^{
NSData* data =[NSData dataWithContentsOfURL:urlVersion];
if (data){
NSError* error;
NSArray* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (json != nil && [json count] != 0) {
versionDict = [json objectAtIndex:0];
CGFloat serverVersion = [[versionDict valueForKey:#"version"]floatValue];
CGFloat appVersion = [[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleVersionKey] floatValue];
NSLog(#"Server Version %f",serverVersion);
NSLog(#"App Version %f",appVersion);
if ([versionDict count] != 0){
if (serverVersion > appVersion){
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:1];
needsUpdate = YES;
}else{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
needsUpdate = NO;
}
}
}
}
});
return needsUpdate;
}
#end
I call it like this
NSLog(#"Needs Update %#",[checkForUpdate checkForUpdateWithResponse] ? #"Yes":#"No");
This is my log
Needs Update No
Server Version 2.000000
App Version 1.000000
I'm not sure why it's returning NO before it even checks. I need it to be a asynchronous because the server that the app will check with is behind our firewall. So if the person is outside the firewall the app needs to continue when is can't reach the server. I'm I headed in the right direction, or is there a better way?
You are asynchronously checking for an update but expecting an immediate response by virtue of your method's design. You can re-engineer your method to something like the example below to notify a handler whenever the operation is complete:
Note: Unchecked and untested for errors; however, the lesson to be gleaned from the example is to use a callback of sorts:
UpdateChecker Class
typedef void (^onComplete)(BOOL requiresUpdate);
#interface UpdateChecker : NSObject
-(void)checkForUpdates:(onComplete)completionHandler;
#end
#implementation UpdateChecker
-(void)checkForUpdates:(onComplete)completionHandler
{
NSString *urlStringVersion = [[NSString alloc] initWithFormat:#"http://URL/app_info?app=app"];
NSURL *urlVersion = [NSURL URLWithString:urlStringVersion];
dispatch_block_t executionBlock =
^{
/*
Your update checking script here
(Use the same logic you are currently using to retrieve the data using the url)
*/
NSData* data = [NSData dataWithContentsOfURL:urlVersion];
BOOL requiresUpdate = NO;
if (data)
{
...
...
...
requiresUpdate = ...; //<-whatever your outcome
}
//Then when completed, notify the handler (this is our callback)
//Note: I typically call the handler on the main thread, but is not required.
//Suit to taste.
dispatch_async(dispatch_get_main_queue(),
^{
if (completionHandler!=NULL)
completionHandler(requiresUpdate);
});
};
dispatch_async(kBgQueue, executionBlock);
}
#end
This is what it would look when you use UpdateChecker to check for updates throughout your app
UpdateChecker *checker = [UpdateChecker alloc] init];
[checker checkForUpdates:^(BOOL requiresUpdate)
{
if (requiresUpdate)
{
//Do something if your app requires update
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:1];
}
else
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
}];
Since dispatch_async is non-blocking, your method returns before your update information has returned (does the dispatch and continues). As needsUpdate defaults to NO, that's what you'll see. You can see this in your log timing - the "Needs Update No" shows up before the server and app versions.
You need some sort of callback (a delegate method or second dispatch_async for example) to ensure you get the correct result, or you need to block. I recommend looking into NSURLConnection, and sendAsynchronousRequest:queue:completionHandler: - it will execute the completion handler on completion, where you can have whatever code you need for handling the update.