Second Api call is slowing down app ios - ios

I am using google places api to search for nearby places. I make one call to get the places, and another call to get the phone number of the places. The second call is slowing down the app. Any way around this? If some sample code could be provided as well that would be great.
s1 = [NSString stringWithFormat:#"https://api.foursquare.com/v2/venues/explore?client_id=%#&client_secret=%#&query=%#&v=20201212&m=swarm&sortByDistance=%i&radius=%f&limit=%#&ll=%f,%f", kClientID, kClientSecret, Name, sortByDistance, meterRadius, recorddisplay, lat, lng];
NSLog(#"This is the foursqaure query: %#", s1);
NSURL *jsonURL = [NSURL URLWithString:[self urlEncodeValue:s1]];
NSString *jsonDataString = [[NSString alloc]initWithContentsOfURL:jsonURL];
NSData *jsonData = [jsonDataString dataUsingEncoding:NSUTF8StringEncoding];
//NSLog(#"This is JSON data: %#", jsonDataString);
if(jsonData == nil)
{
NSLog(#"SEARCH RESULT IS NIL.....");
//[pool release];
return FALSE;
}
else
{
//retrieve the objects in the JSON and then make another http request...
}

This line is wrong:
NSString *jsonDataString = [[NSString alloc]initWithContentsOfURL:jsonURL];
You are networking synchronously on the main thread. Never never never do that. That's the cause of the delay.

Related

Graph API with App Token for Page not working

I'm developing an app with Login with Facebook and also Login with username and password.
Now i want to find all the events of a public page on Facebook for both the types of users (Facebook and Normal).
The problem is that the User with Facebook can retrieve the data, but the "normal" user cannot because data is nil.
The steps are :
1 - Compile this url with the correct credential of my Facebook App :
https://graph.facebook.com/oauth/access_token?client_id=APP_ID&client_secret=APP_SECRET&grant_type=client_credentials
2 - Put the url in my browser and retrieve the App Token In the format :
53682XXXXXXXXXX|w6F3Ic6L48XXXXXXXXXXXXXXXXX
3 - Use this piece of code :
NSString *token = [[[FBSession activeSession] accessTokenData] accessToken];
NSString *urlString;
if (!userWithFb) {
NSString *token = #"53682XXXXXXXXXX|w6F3Ic6L48XXXXXXXXXXXXXXXXX";
urlString = [NSString stringWithFormat:#"https://graph.facebook.com/%#/events?access_token=%#", pageId,token];
}else{
urlString = [NSString stringWithFormat:#"https://graph.facebook.com/%#/events?access_token=%#", pageId,token];
}
NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
if(data != nil)
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
BUT the data for the normal user is nil. When I put the "urlstring" in my browser I see all the data , I don't know where is the problem. Waiting for solution I say thanks.
I solved my question by adding this piece of code after the if statement :
NSString *encodedURLString = [urlString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
url = [NSURL URLWithString:encodedURLString];
NSData* data = [NSData dataWithContentsOfURL:url];
The Problem is the "|" in the token, it must be replaced with "&".
Hope this help you !

UICollectionView: get profile pictures from Instagram instead of recent media pictures to display

I am a newbie iOS learner. Couldn't find answer to following question after searching for a while. Hence, here it is.
Building on a first app that displays recent pictures from a user's instagram feed, I am trying to display pictures from the follows of that users instead.
To call recent pictures from the Instagram feed, which worked well, I had created the following method "imageForPhoto"
+ (void)imageForPhoto:(NSDictionary *)photo size:(NSString *)size completion:(void(^)(UIImage *image))completion {
if (photo == nil || size == nil || completion == nil) {
return;
}
NSString *key = [[NSString alloc] initWithFormat:#"%#-%#", photo[#"id"], size];
NSURL *url = [[NSURL alloc] initWithString:photo[#"images"][size][#"url"]];
[self downloadURL:url key:key completion:completion];
}
Therefore, I first modified my code to get the data related to the "follows" in my PhotosViewController instead of recent media pictures ( v1/users/3/follows ):
NSURLSession *session = [NSURLSession sharedSession];
NSString *urlString = [[NSString alloc] initWithFormat:#"https://api.instagram.com/v1/users/3/follows?count=99&access_token=%#", self.accessToken];
Then, I created a new method that I called friendAvatarForPhoto to get the follows profile pictures from a photo NSDictionary that is passed in as the only method parameter. I placed this method in my PhotoController class:
+ (void)friendAvatarForPhoto:(NSDictionary *)photo completion:(void(^)(UIImage *image))completion {
if (photo == nil || completion == nil) {
return;
}
NSString *key = [[NSString alloc] initWithFormat:#"avatar-%#", photo[#"user"][#"id"]];
NSURL *url = [[NSURL alloc] initWithString:photo[#"profile_picture"]];
[self downloadURL:url key:key completion:completion];
}
It seems to work. I have manually checked that the pictures that are rendered on my UICollectionView are actually coming from the "profile_picture" key of the responseDictionary I get back from Instagram.
Here is the structure of this dictionary: https://www.dropbox.com/s/ldjqupadqg0j3nq/friends_response_dictionary.png
Specifically, as you can see, I modified both the key and the url:
NSString *key = [[NSString alloc] initWithFormat:#"avatar-%#", photo[#"user"][#"id"]];
NSURL *url = [[NSURL alloc] initWithString:photo[#"profile_picture"]];
but I am not really understanding what I need to initial the *key string with... I kept "avatar" for example but why should I? I am pretty sure it's wrong although the result seems to be fine from what is returned on my UICollectionView.
What should I initiate this string with instead?
What is the function of this key in the overall NSURLSession process?
I'd love to better understand the overall process and better connect the dots with Instagram's API so I can query the right key and be sure I am getting what I am looking for. In a reliable way, not an intuitive one as I have just done app-arently.
Any help from experienced developers in the community would be welcome, I've just started to explore iOS dev a few weeks ago based on rusty C skills from a long time back.
Thank you!
:) Arsene

What is used when we want to bring down and present data from an API, but don't require persistence?

I use Core Data for most of my projects that require data persistence from launch to launch, but what if I just want to pull information down from an API such as Twitter's and present it to the user, and I don't require it to be persistent from launch to launch?
Is the typical solution here to simply use NSMutableArray and store all the objects therein, and create a class for what's stored in it, similar to Core Data's managed objects?
I've had great success using Mantle.
Yes, an NSMutableArray of custom objects will work. An NSMutableArray of NSDictionary's is also a common solution, and works well for simple data sets.
https://developer.apple.com/library/ios/referencelibrary/GettingStarted/RoadMapiOS/ThirdTutorial.html#//apple_ref/doc/uid/TP40011343-CH10-SW1
Assuming that you can request a JSON response from the server, the code to pull information from the server would look something like this. The jsonData object produced by this code consists of nested NSArrays and NSDictionarys. The NSLog at the end will dump the data set returned by the server.
- (void)retrieveJSONdataFromServer:(NSString *)param1
{
NSError *error;
NSString *str = [NSString stringWithFormat:#"http://www.example.com/sometopic/api/json?param1=%#", param1 ];
NSURL *url = [NSURL URLWithString:str];
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
if ( !data )
{
NSLog( #"%#", error );
return;
}
id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if ( !jsonData )
{
NSLog( #"%#", error );
return;
}
// do something with the JSON data here
// for this sample code, we just dump the data to the debug console
NSLog( #"%#", jsonData );
}

iOS NSURL queuing mechansim for multiple requests from file

I am very new to iOS development, but I would like to make an app that has two table view controllers (columns): both are a row of images that act as links. The first would be a column of YouTube videos and the second a column of websites. I would like to have all these listed in a file file.txt listed like so: V, http://youtube.com/example W, http://example.com
There would be a long list of those, the V meaning its a video (for the video column) and W for the websites. Now, I understand how to being the single file in, but what happens afterwards is my concern. Can I read each line into some sort of queue and then fire the NSURL request for each one consecutively? How can that be done with NSURL? Is there perhaps a better approach?
There are two questions for me:
Is a text file really the best format?
I might suggest a plist or archive (if the file is only going to exist only in your app's bundle and/or documents folder) or JSON (if it's going to live on a server before delivering it to the user) instead of a text file. It will make it easier to parse this file than a text file. For example, consider the following dictionary:
NSDictionary *dictionary = #{#"videos" : #[#"http://youtube.com/abc", #"http://vimeo.com/xyz"],
#"websites": #[#"http://apple.com", #"http://microsoft.com"]};
You can save that to a plist with:
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"files.plist"];
[dictionary writeToFile:plistPath atomically:YES];
You can add that file to your bundle or whatever, and then read it at a future date with:
dictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath];
You can, alternatively, write that to a JSON file with:
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonPath = [documentsPath stringByAppendingPathComponent:#"files.json"];
[data writeToFile:jsonPath atomically:YES];
You can read that JSON file with:
data = [NSData dataWithContentsOfFile:jsonPath];
dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
Either way, you can get the list of videos or web sites like so:
NSArray *videos = dictionary[#"videos"];
NSArray *websites = dictionary[#"websites"];
Now that you have your arrays of videos and websites, the question then is how you then use those URLs.
You could do something like:
for (NSString *urlString in videos) {
NSURL *url = [NSURL URLWithString: urlString];
// now do something with the URL
}
The big question is what is the "do something" logic. Because you're dealing with a lot of URLs, you would want to use a NSOperation based solution, not a GCD solution, because NSOperationQueue lets you control the degree of concurrency. I'd suggest a NSOperation-based networking library like AFNetworking. For example, to download the HTML for your websites:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4;
for (NSString *urlString in websites)
{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// convert the `NSData` responseObject to a string, if you want
NSString *string = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
// now do something with it, like saving it in a cache or persistent storage
// I'll just log it
NSLog(#"responseObject string = %#", string);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error = %#", error);
}];
[queue addOperation:operation];
}
Having said that, I'm not sure it makes sense to kick off a ton of network requests. Wouldn't you really prefer to wait until the user taps on one of those cells before retrieving it (and for example, then just open that URL in a UIWebView)? You don't want an app that unnecessarily chews up the user's data plan and battery retrieving stuff that they might not want to retrieve. (Apple has rejected apps that request too much data from a cellular connection.) Or, at the very least, if you want to retrieve stuff up front, only retrieve stuff as you need it (e.g. in cellForRowAtIndexPath), which will retrieve the visible rows, rather than the hundreds of rows that might be in your text/plist/json file.
Frankly, we need a clearer articulation of what you're trying to do, and we might be able to help you with more concise counsel.

Make fetching of images faster from facebook in iOS

Hi, I'm trying to fetch the user's friends profile pic from facebook and load it in my table and it works fine, but however it takes a long time based on number of friends you have I have noticed that in my fetchPeopleimages function the part of [NSData dataWithContentsOfURL:imageURL] is making the delay. I've searched through stackoverflow and it seems that I may have to implement the NSURLConnection sendAsynchronousRequest
method or cache. But there is no proper example. Can anyone please provide a solution to this? If I have to implement those methods please do give a example on how should I implement it in my code.
-(void) fetchPeopleimages
{
if ([listType isEqualToString:#"Facebook"])
{
int count=0;
NSMutableArray *imageArray=[[NSMutableArray alloc]initWithCapacity:[_peopleImageList count]];
for(NSString *imageUrl in _peopleImageList)
{
NSData *imageData = nil;
NSString *imageURLString = imageUrl;
count++;
NSLog(#"URL->%#,count->%d", imageURLString,count);
if (imageURLString)
{ //This block takes time to complete
NSURL *imageURL = [NSURL URLWithString:imageURLString];
imageData = [NSData dataWithContentsOfURL:imageURL];
}
if (imageData)
{
[imageArray addObject:imageData];
}
else
{
[imageArray addObject:[NSNull null]];
}
}
_peopleImageList=imageArray;
NSLog(#"%#",_peopleImageList);
}
}
Never ever use __withContentsOfURL in an iOS app. As you can see, it blocks the thread it runs on, making your app appear jerky or unresponsive. If you're really lucky, the iOS watchdog timer will kill your app if one of those operations takes too long.
You must use an asynchronous network operation for something like this.
Apple has a sample app that does exactly this:
https://developer.apple.com/library/ios/samplecode/LazyTableImages/Introduction/Intro.html

Resources