Stop Activity Indicator while no row in Xcode - xcode

I am using the code below to retrieve data from Url via PHP file, the tableview controller will start the Activity Indicator. What I am trying to do is to stop the indicator As soon as there are no data has been loaded to the tableview.
Her is the code;
NSString *urlString = [NSString stringWithFormat:#"http:/MyWebSite/ChoseMyLike.php?userName=%#", myString];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!data) {
NSLog(#"connection error: %#", error);
return;
}
NSError *parseError;
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&parseError];
if (!json) {
NSLog(#"JSON Parsing error: %#", parseError);
NSLog(#"data = %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
return;
}
NSMutableArray *results = [[NSMutableArray alloc]init];
for (int i = 0; i < json.count; i++) {
NSString *cQasidaName = json[i][#"qasidaName"];
NSString *cQasidaShaerName = json[i][#"qasidaShaerName"];
NSString *cQasidaBody = json[i][#"qasidaBody"];
NSString *cQasidaDate = json[i][#"myDate"];
NSString *cQasidaTime = json[i][#"myTime"];
NSString *cQasidaRate = json[i][#"myRate"];
NSString *cQasidaId = json[i][#"qasidaId"];
NSString *cQasidaUserName = json[i][#"userName"];
NSString *cTheUserId = json[i][#"theUserId"];
NSString *cTheUserNameArabic = json[i][#"userNameArabic"];
[results addObject:[[ListOfObjects alloc] initWithQasidaName: (NSString *) cQasidaName andQasidaShaerName: (NSString *) cQasidaShaerName andQasidaBody: (NSString *) cQasidaBody andQasidaDate: (NSString *) cQasidaDate andQasidaTime: (NSString *) cQasidaTime andQasidaRate: (NSString *)cQasidaRate andQasidaId:cQasidaId andQasidaUserName:cQasidaUserName andTheUserId:cTheUserId andTheUserNameArabic:cTheUserNameArabic]];
}
dispatch_async(dispatch_get_main_queue(), ^{
self.listArray = results;
[self.tableView reloadData];
[spinner stopAnimating];
});
}];
[task resume];
}
At the moment the indicator is not stopping. What do I need to do?
Thanks

Simply call [spinner stopAnimating]; for all return paths:
if (!data) {
NSLog(#"connection error: %#", error);
[spinner stopAnimating];
return;
}
and
if (!json) {
NSLog(#"JSON Parsing error: %#", parseError);
NSLog(#"data = %#", [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]);
[spinner stopAnimating];
return;
}
Note: it's much easier to have a single return statement and then you can always call [spinner stopAnimating]; at the end of the method... consider restructuring your code.

Related

absoluteString "handleOpenURL " to match email attachment name?

I have a working code, but now find the need to import & save two different Sqlite files via email "Open in" option, I tried using UIAlertView to create to different save file paths but it only seems to work with one way
- (void)handleOpenURL:(NSURL *)urlx {
NSData *dbimport = [[NSData alloc] initWithContentsOfURL:urlx];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:dbimport forKey:#"import"];
[defaults synchronize];
NSLog(#"import saved");
databaseName = #"svrStoreData2.sql";
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSError *writeError = nil;
NSString *filePathx = [documentsPath stringByAppendingPathComponent:databaseName]; [dbimport writeToFile:filePathx atomically:YES];
if (writeError)
{
NSLog(#"Error writing file: %#", writeError);
}
}
Both file have specific file names so was wonder if I could use "if equal to string" & use an if statement after I have synchronised NSUserDefaults to give two file path save options
Thanks for any help in advance
This was my UIALertView try
UIAlertView *alertdata = [[UIAlertView alloc]
initWithTitle:#"What type of data do you want to import?"
message:#"Choose data type"
delegate:self
cancelButtonTitle:#"Import RM Name Data"
otherButtonTitles:#"Import Store Visit Data", nil];
NSInteger *buttonIndex = NULL;
NSLog(#"Button Index =%ld",(long)buttonIndex);
if (buttonIndex == 0)
{
}
else if (buttonIndex == 1)
{
databaseExportName = #"export.sql";
NSArray *pathssqlite3 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *pathssqliteDir2 = [pathssqlite3 objectAtIndex:0];
filePathsqlite3 = [pathssqliteDir2 stringByAppendingPathComponent:databaseExportName];
NSData *dbimport = [[NSData alloc] initWithContentsOfURL:urlx];
NSMutableData *dbimport = [[NSMutableData alloc] initWithContentsOfURL:urlx];
NSError *writeError = nil;
[dbimport writeToFile:filePathsqlite3 atomically:YES];
if (writeError)
{
NSLog(#"Error writing file: %#", writeError);
} } [alertdata show];
Still trying to resolve, was hoping this would work, but still cannot crack it
`- (void)handleOpenURL:(NSURL *)urlx {
NSData *dbimport = [[NSData alloc] initWithContentsOfURL:urlx];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:dbimport forKey:#"import"];
[defaults synchronize];
NSLog(#"import saved");
if([[urlx absoluteString] isEqualToString:#"svrStoreData2.sql"])
{
NSLog(#"If statement called");
//
databaseName = #"svrStoreData2.sql";
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSError *writeError = nil;
NSString *filePathx = [documentsPath stringByAppendingPathComponent:databaseName];
[dbimport writeToFile:filePathx atomically:YES];
if (writeError)
{
NSLog(#"Error writing file: %#", writeError);
}
//
else {
databaseExportName = #"export.sql";
NSString *documentsPathy = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSError *writeError = nil;
NSString *filePathy = [documentsPathy stringByAppendingPathComponent:databaseExportName];
[dbimport writeToFile:filePathy atomically:YES];
if (writeError)
{
NSLog(#"Error writing file: %#", writeError);
}
NSLog(#"If statement Not called");
}
}
}
`
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex(NSInteger)buttonIndex {
if ([alertView tag] == 14)
{
if (buttonIndex == 1)
{
[self reviewSave];
}
}
if ([alertView tag] == 11) {
if (buttonIndex == 1)
{
NSDictionary *defaultsDictionary = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
for (NSString *key in [defaultsDictionary allKeys]) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];}
}
}
Solved the problem, I had another UIAlert, so by adding Tags sorted it

Block variable gets null outside of block - xcode

Im trying to carry over a block variable and put it in my convenience initializer but the variable returns null, can someone show me the right way of doing it, Thanks, also please check if I have done my NSComparison Result correctly, here's the code,
- (void) retrieveData
{
NSURL *url = [NSURL URLWithString:jsonFile];
NSData *data = [NSData dataWithContentsOfURL:url];
_jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_salesArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _jsonArray.count; i++) {
NSString *sID = [[_jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString *sName = [[_jsonArray objectAtIndex:i] objectForKey:#"name"];
NSString *sAddress = [[_jsonArray objectAtIndex:i] objectForKey:#"address"];
NSString *sPostcode = [[_jsonArray objectAtIndex:i] objectForKey:#"postcode"];
__block NSString *distance;
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
[geocoder geocodeAddressString:sPostcode completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && placemarks.count > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocation *myLocation = self.manager.location;
CLLocationDistance miles = [location distanceFromLocation:myLocation];
//this is the variable i want in my convenience init.
distance = [NSString stringWithFormat:#"%.1f m", (miles/1609.344)];
}
}];
[_salesArray addObject:[[sales alloc] initWithSales:sID andName:sName andAddress:sAddress andPostcode:distance]];
}
[_salesArray sortUsingComparator:
^NSComparisonResult(id obj1, id obj2){
sales *p1 = (sales *)obj1;
sales *p2 = (sales *)obj2;
if (p1.postcode > p2.postcode) {
return (NSComparisonResult)NSOrderedDescending;
}
if (p1.postcode < p2.postcode) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}
];
[self.tableView reloadData];
}
Thanks in advance to anyone that can help, this happens to be the last part of my app until completion :)
-[CLGeocoder geocodeAddressString:completionHandler:] runs the completion handler asynchronously and it won't run the block before the rest of that function finishes. distance doesn't "get null"; rather, it hasn't been set yet.
your block is run AFTER the function completes ... doesn't matter if the 'line of code' is after or before the block.
the solution is to have to block call a 'continuation' function:
e.g. instead of - (void) retrieveData
have - (void) beginRetrieveData, which calls the block and then have a - (void) finishRetrieveData, which is called by the block and doesn't run before the block ;)
here is how your code could look:
- (void) beginRetrieveData
{
NSURL *url = [NSURL URLWithString:jsonFile];
NSData *data = [NSData dataWithContentsOfURL:url];
_jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_salesArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _jsonArray.count; i++) {
NSString *sID = [[_jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString *sName = [[_jsonArray objectAtIndex:i] objectForKey:#"name"];
NSString *sAddress = [[_jsonArray objectAtIndex:i] objectForKey:#"address"];
NSString *sPostcode = [[_jsonArray objectAtIndex:i] objectForKey:#"postcode"];
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
[geocoder geocodeAddressString:sPostcode completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && placemarks.count > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocation *myLocation = self.manager.location;
CLLocationDistance miles = [location distanceFromLocation:myLocation];
//this is the variable i want in my convenience init.
NSString *distance = [NSString stringWithFormat:#"%.1f m", (miles/1609.344)];
[self finishRetrieveData:distance]; //continue
}
}];
}
}
- (void) finishRetrieveData:(NSString*)distance
{
[_salesArray addObject:[[sales alloc] initWithSales:sID andName:sName andAddress:sAddress andPostcode:distance]];
}

How to wait dispatch asyn finish?

I used dispatch async to get som info from facebook , but i can't know when asyn finish
-(void)shareScoreViaFB:(int)score{
__block NSDictionary * dic;
ACAccountStore *_accStore=[[ACAccountStore alloc]init];
ACAccountType *_accType =[_accStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *options = #{
ACFacebookAppIdKey:_mAppID,
ACFacebookPermissionsKey: #[#"publish_actions"],
ACFacebookAudienceKey: ACFacebookAudienceFriends
};
[_accStore requestAccessToAccountsWithType:_accType options:options completion:^(BOOL granted, NSError *e) {
if (granted) {
NSArray *accounts = [_accStore accountsWithAccountType:_accType];
_accFB = [accounts lastObject];
// [self openShareDialog];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dic =[self getListScore];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"data1: %#",dic);
});
});
};
}];
}
And method to get info
-(NSDictionary *)getListScore{
__block NSDictionary * dict=nil;
NSString *url =[NSString stringWithFormat:#"https://graph.facebook.com/%#/scores",_mAppID];
NSURL * strURL =[NSURL URLWithString:url];
NSDictionary * parameters =[NSDictionary dictionaryWithObjectsAndKeys:#"score,user",#"fields", nil];
SLRequest * request =[SLRequest requestForServiceType:SLServiceTypeFacebook
requestMethod:SLRequestMethodGET URL:strURL parameters:parameters];
request.account = _accFB;
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (!error && responseData) {
dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];
NSLog(#"%#",dict[#"data"]);
// return dict[#"data"];
}
}];
return dict;
}
When i run' the log show:
- dict1: null.
- dict: OK (this show true value).
How i wait for dispatch_asyn finish and set this value to main interface?
Try doing it as follows:
-(void)shareScoreViaFB:(int)score{
__block NSDictionary * dic;
ACAccountStore *_accStore=[[ACAccountStore alloc]init];
ACAccountType *_accType =[_accStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *options = #{
ACFacebookAppIdKey:_mAppID,
ACFacebookPermissionsKey: #[#"publish_actions"],
ACFacebookAudienceKey: ACFacebookAudienceFriends
};
[_accStore requestAccessToAccountsWithType:_accType options:options completion:^(BOOL granted, NSError *e) {
if (granted) {
NSArray *accounts = [_accStore accountsWithAccountType:_accType];
_accFB = [accounts lastObject];
NSString *url =[NSString stringWithFormat:#"https://graph.facebook.com/%#/scores",_mAppID];
NSURL * strURL =[NSURL URLWithString:url];
NSDictionary * parameters =[NSDictionary dictionaryWithObjectsAndKeys:#"score,user",#"fields", nil];
SLRequest * request =[SLRequest requestForServiceType:SLServiceTypeFacebook
requestMethod:SLRequestMethodGET URL:strURL parameters:parameters];
request.account = _accFB;
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (!error && responseData) {
dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];
NSLog(#"%#",dict[#"data"]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"data1: %#",dic);
});
}
}];
};
}];
}

how could i integrate via me social site into iphone app

hi i want to integrate Via Me social site into my iphone app,i googled but didn't find any samples.
The basic process is as follows:
Create a custom URL scheme for your app. Via Me will use this after the user has been authenticated, to return to your app. In my example, I created one called "robviame://"
Register your app at http://via.me/developers. This will give you a client id and a client secret:
When you want to authenticate the user, you call:
NSString *redirectUri = [[self redirectURI] stringByAddingPercentEscapesForURLParameterUsingEncoding:NSUTF8StringEncoding];
NSString *urlString = [NSString stringWithFormat:#"https://api.via.me/oauth/authorize/?client_id=%#&redirect_uri=%#&response_type=code", kClientID, redirectUri];
NSURL *url = [NSURL URLWithString:urlString];
[[UIApplication sharedApplication] openURL:url];
What that will do is fire up your web browser and give the user a chance to log on and grant permissions to your app. When user finishes that process, because you've defined your custom URL scheme, it will call the following method in your app delegate:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
// do whatever you want here to parse the code provided back to the app
}
for example, I'll call a handler for my Via Me response:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
ViaMeManager *viaMeManager = [ViaMeManager sharedManager];
if ([[url host] isEqualToString:viaMeManager.host])
{
[viaMeManager handleViaMeResponse:[self parseQueryString:[url query]]];
return YES;
}
return NO;
}
// convert the query string into a dictionary
- (NSDictionary *)parseQueryString:(NSString *)query
{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
NSArray *queryParameters = [query componentsSeparatedByString:#"&"];
for (NSString *queryParameter in queryParameters) {
NSArray *elements = [queryParameter componentsSeparatedByString:#"="];
NSString *key = [elements[0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *value = [elements[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
value = [[value componentsSeparatedByString:#"+"] componentsJoinedByString:#" "];
[dictionary setObject:value forKey:key];
}
return dictionary;
}
That handler might, for example, save the code and then request the access token:
- (void)handleViaMeResponse:(NSDictionary *)parameters
{
self.code = parameters[#"code"];
if (self.code)
{
// save the code
[[NSUserDefaults standardUserDefaults] setValue:self.code forKey:kViaMeUserDefaultKeyCode];
[[NSUserDefaults standardUserDefaults] synchronize];
// now let's authenticate the user and get an access key
[self requestToken];
}
else
{
NSLog(#"%s: parameters = %#", __FUNCTION__, parameters);
NSString *errorCode = parameters[#"error"];
if ([errorCode isEqualToString:#"access_denied"])
{
[[[UIAlertView alloc] initWithTitle:nil
message:#"Via Me functions will not be enabled because you did not authorize this app"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
}
else
{
[[[UIAlertView alloc] initWithTitle:nil
message:#"Unknown Via Me authorization error"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
}
}
}
and the code to retrieve the token might look like:
- (void)requestToken
{
NSURL *url = [NSURL URLWithString:#"https://api.via.me/oauth/access_token"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
NSDictionary *paramsDictionary = #{#"client_id" : kClientID,
#"client_secret" : kClientSecret,
#"grant_type" : #"authorization_code",
#"redirect_uri" : [self redirectURI],
#"code" : self.code,
#"response_type" : #"token"
};
NSMutableArray *paramsArray = [NSMutableArray array];
[paramsDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
[paramsArray addObject:[NSString stringWithFormat:#"%#=%#", key, [obj stringByAddingPercentEscapesForURLParameterUsingEncoding:NSUTF8StringEncoding]]];
}];
NSData *paramsData = [[paramsArray componentsJoinedByString:#"&"] dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:paramsData];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error)
{
NSLog(#"%s: NSURLConnection error = %#", __FUNCTION__, error);
return;
}
NSError *parseError;
id results = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (parseError)
{
NSLog(#"%s: NSJSONSerialization error = %#", __FUNCTION__, parseError);
return;
}
self.accessToken = results[#"access_token"];
if (self.accessToken)
{
[[NSUserDefaults standardUserDefaults] setValue:self.accessToken forKey:kViaMeUserDefaultKeyAccessToken];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}];
}
Hopefully this will be enough to get you going. This is described in greater detail at the http://via.me/developers page.

iphone, SBJsonWriter .. converting an object

I want to post a json object to a request. But JSONWriter doesn't seem to convert my object, don't know what I'm doing wrong ...
here's my code for the conversion..
customer = [[Customer alloc] init];
Address *tempAddr = [[Address alloc] init];
tempAddr.Line1=strAddr1 ;
tempAddr.Zip =strPost;
tempAddr.City =strStreet;
[customer setInvoiceAddress:tempAddr];
customer.FirstName=strFirstName;
customer.LastName=strLastName;
customer.CellPhone=strCellPhone;
customer.Phone=strPhone;
customer.Email=strEmail;
SBJsonWriter *writer = [SBJsonWriter alloc];
NSString *jsonConvertedObj = [writer stringWithObject:customer];
NSLog(#"the json converted object ... %#", jsonConvertedObj);
Please help. I've no idea what's wrong with the code above.
To figure out why your Json parsing fails, You shoud use this approach:
NSError *error = nil;
NSString * jsonTest = [[[SBJsonWriter alloc] init] stringWithObject:jsonTestDictionary error:&error];
if ( ! jsonTest ) {
NSLog(#"Error: %#", error);
}else{
NSLog(#"%#", jsonTest);
}
Here is a simple code demonstrating how to use it:
#import <Foundation/Foundation.h>
#import "SBJsonWriter.h"
#import "SBJsonParser.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSDictionary* aNestedObject = [NSDictionary dictionaryWithObjectsAndKeys:
#"nestedStringValue", #"aStringInNestedObject",
[NSNumber numberWithInt:1], #"aNumberInNestedObject",
nil];
NSArray * aJSonArray = [[NSArray alloc] initWithObjects: #"arrayItem1", #"arrayItem2", #"arrayItem3", nil];
NSDictionary * jsonTestDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
#"stringValue", #"aString",
[NSNumber numberWithInt:1], #"aNumber",
[NSNumber numberWithFloat:1.2345f], #"aFloat",
[[NSDate date] description], #"aDate",
aNestedObject, #"nestedObject",
aJSonArray, #"aJSonArray",
nil];
// create JSON output from dictionary
NSError *error = nil;
NSString * jsonTest = [[[SBJsonWriter alloc] init] stringWithObject:jsonTestDictionary error:&error];
if ( ! jsonTest ) {
NSLog(#"Error: %#", error);
}else{
NSLog(#"%#", jsonTest);
}
}
return 0;
}
outputs
{
"aDate":"2012-09-12 07:39:00 +0000",
"aFloat":1.2345000505447388,
"nestedObject":{
"aStringInNestedObject":"nestedStringValue",
"aNumberInNestedObject":1
},
"aJSonList":["arrayItem1","arrayItem2","arrayItem3"],
"aString":"stringValue",
"aNumber":1
}
Note:
Using 'error' allowed me to figure out that if you write [NSDate
date] instead of [[NSDate date] description] you will get a "JSON
serialisation not supported for
__NSTaggedDate" error.
notice the float rounding error... 1.2345 became 1.2345000505447388
AFAIK SBJSONWriter only supports conversion of dictionaries and arrays to JSON strings. It won't work for arbitrary objects.
Edit: Here is the corresponding implementation, which returns nil if the supplied object is neither a dictionary nor an array.
- (NSString*)stringWithObject:(id)value {
if ([value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSArray class]]) {
return [self stringWithFragment:value];
}
[self clearErrorTrace];
[self addErrorWithCode:EFRAGMENT description:#"Not valid type for JSON"];
return nil;
}

Resources