I'm building an app that is using the Instagram API to display photos but I'm running into some trouble. The app is crashing when there is no network connection and I have found the code that is causing the problem.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
self.accessToken = [userDefaults objectForKey:#"accessToken"];
if (self.accessToken == nil) {
[SimpleAuth authorize:#"instagram" options:#{#"scope": #[#"likes"]} completion:^(NSDictionary *responseObject, NSError *error) {
self.accessToken = responseObject[#"credentials"][#"token"];
[userDefaults setObject:self.accessToken forKey:#"accessToken"];
[userDefaults synchronize];
[self refresh];
}];
} else {
[self refresh];
}
I have found that the [self refresh]; is causing the problem in the else block and I tried to replace it with a alert view like this
UIAlertView *networkError = [[UIAlertView alloc] initWithTitle:#"Network Error" message:#"Please connect your device to a network and restart application" delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
[networkError show];
However, with this problem I find that if I open the app with a network connection I still get the alert. Any help would be great because I'm still new to Objective C!
Thank you for the help!
I know this code from Treehouse :).
The thing is that the if (self.accessToken == nil) { /.../ } block will only get execute when the app is not authorized using your Instagram credentials.
Once you logged in successfully, it will always execute the code in the else { /.../ } block. If it has connection to Internet, it will do its work, download, display images etc. If you insert the code to display alert, it will always do that because you actually mean that by that code.
If you want to check if there is some connection, you need to do that before all that code, display an error and return instantly if connection is not available. However, the author tried to keep things simple, assuming there is always Internet connection.
Hope it makes you understand it.
This is the some code you can use for checking if there is connection:
// show some activity indicator
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
NSURL *url = [NSURL URLWithString:#"http://www.apple.com/"];
NSString *s = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
// hide the activity indicator
self.connected = (s != nil);
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
if (self.isConnected)
{
NSLog(#"self.connected == YES");
}
else
{
NSLog(#"self.connected == NO");
NSString *alertMessage = #"In order to load images, you need an active Internet connection. Please try again later!";
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Sorry!"
message:alertMessage
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
});
});
You obviously need to add a property to you class. You can insert this code:
#interface LDPhotosViewController ()
#property (nonatomic, copy, getter = isConnected) NSString *connected;
#end
at the top of LDPhotosViewController.m file, before the #implementation line.
Related
I am making a call to twitters API to load some tweets for a specific section of my app.
A small chunk of users are reporting a crash when loading the tweets view, while the rest have no problem at all.
I have submitted the code to Apple Tech Support and they responded letting me know that NSJSONSerialization can sometimes return a NSArray or NSDictionary.
Obviously it will throw an error is objectAtIndex: is called on an NSDictionary object, which I believe is the culprit for all of my users.
The partial solution is to detect if it is an Array or NSDictionary.
Here is where I am at now:
id feedData = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonError];
if ([feedData isKindOfClass:[NSArray class]]) {
//Is array
} else if ([feedData isKindOfClass:[NSDictionary class]]) {
//is dictionary
}
I basically need an NSArray every single time. So in the is array block, I basically just use the feedData, but in NSDictionary, how can I convert it to an NSArray that will match the structure I need.
Honestly the biggest issue is that I cannot see what the NSDictionary structure looks like because none of my testing devices or simulator return the NSDictionary data, they all return an NSArray.
Here is what the entire getUserFeed method that sends the request to twitter looks like:
// Get the twitter feed
NSURL *requestURL = [NSURL URLWithString:TW_API_TIMELINE];
// Set up proper parameters
NSMutableDictionary *timelineParameters = [[NSMutableDictionary alloc] init];
[timelineParameters setObject:kNumTweets forKey:#"count"];
[timelineParameters setObject:#"1" forKey:#"include_entities"];
// Create the Social Request
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:requestURL parameters:timelineParameters];
postRequest.account = self.delegate.userAccount;
// Perform the request
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
// Check if we reached the reate limit
if ([urlResponse statusCode] == 429) {
// Rate limit reached
// Display an alert letting the user know we have hit the rate limit
UIAlertView *twitterAlert = [[UIAlertView alloc] initWithTitle:kRateLimitTitle
message:kRateLimitMessage
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[twitterAlert show];
// Stop animating the pull to refresh if it is animating
[self.feedTableView.pullToRefreshView stopAnimating];
return;
}
// Check if there was an error
if (error) {
NSLog(#"Error: %#", error.localizedDescription);
// Stop animating the pull to refresh if it is animating
[self.feedTableView.pullToRefreshView stopAnimating];
return;
}
// Check if there is some response data
if (responseData) {
NSError *jsonError = nil;
id feedData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&jsonError];
if ([feedData isKindOfClass:[NSArray class]]) {
//Is array
NSLog(#"It's an Array");
} else if ([feedData isKindOfClass:[NSDictionary class]]) {
//Is dictionary
NSLog(#"It's a Dictionary");
} else {
//is something else
}
if (!jsonError) {
[self gatherTweetsFromArray:feedData];
} else {
// Stop animating the pull to refresh if it is animating
[self.feedTableView.pullToRefreshView stopAnimating];
// Alert the user with the error
UIAlertView *twitterAlert = [[UIAlertView alloc] initWithTitle:kErrorTitle
message:kErrorMessage
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[twitterAlert show];
}
} else {
// Stop animating the pull to refresh if it is animating
[self.feedTableView.pullToRefreshView stopAnimating];
// Alert the user with the error
UIAlertView *twitterAlert = [[UIAlertView alloc] initWithTitle:kErrorTitle
message:kErrorMessage
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[twitterAlert show];
}
});
}];
This is a MAJOR bug and I need to squash it, so any ideas or information will be greatly appreciated! Thank you!
How can I upload a photo to facebook from an iOS app using their new API/SDK? I've already tried and I'm not getting anywhere, just keep running in circles. Here is the code I currently have:
-(void)dataForFaceboo{
self.postParams =
[[NSMutableDictionary alloc] initWithObjectsAndKeys:
self.uploadPhoto.image, #"picture", nil];
}
-(void)uploadToFacebook{
[self dataForFacebook];
NSLog(#"Going to facebook: %#", self.postParams);
// Hide keyboard if showing when button clicked
if ([self.photoCaption isFirstResponder]) {
[self.photoCaption resignFirstResponder];
}
// Add user message parameter if user filled it in
if (![self.photoCaption.text
isEqualToString:kPlaceholderPostMessage] &&
![self.photoCaption.text isEqualToString:#""])
{
[self.postParams setObject:self.photoCaption.text forKey:#"message"];
}
[FBRequestConnection startWithGraphPath:#"me/feed"
parameters:self.postParams
HTTPMethod:#"POST"
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error)
{
NSString *alertText;
if (error) {
alertText = [NSString stringWithFormat:
#"error: domain = %#, code = %d",
error.domain, error.code];
} else {
alertText = [NSString stringWithFormat:
#"Posted action, id: %#",
[result objectForKey:#"id"]];
}
// Show the result in an alert
[[[UIAlertView alloc] initWithTitle:#"Result"
message:alertText
delegate:self
cancelButtonTitle:#"OK!"
otherButtonTitles:nil] show];
}];
}
Your code is fine, some slight changes to be done:
add the image to the dictionary in NSData format, like
[params setObject:UIImagePNGRepresentation(_image) forKey:#"picture"];
and change the graph path to "me/photos" instead of "me/feed"
Make these changes, it worked for me.
Remember you need to use "publish_actions" permissions.
"me/photos" is meant for the photo actually be in the "Photo's" list on your Facebook profile. "me/feed" is just a post on the timeline.
I'm trying to set and a NSURL using grand central dispatch, however it appears that the variable is set and accessible until you try to access it outside of the grand central dispatch block.
-(void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue,^{
self.ubiquitousURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSLog(#"ubiq inside: %#", self.ubiquitousURL);
if (self.ubiquitousURL) {
self.iCloudDocURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#Documents", self.ubiquitousURL]];
self.iCloudDocString = [self.iCloudDocURL absoluteString];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loadFiles) name: UIApplicationDidBecomeActiveNotification object:nil];
} else {
/* change to the main queue if you want to do something with the UI. For example: */
dispatch_async(dispatch_get_main_queue(),^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Please enable iCloud" message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
});
}
});
NSLog(#"ubiq outside: %#", self.ubiquitousURL);
}
The first NSLog which, starts with ubiq inside returns the correct URL, while ubiq outside returns NULL. I'm using ARC, so no need to mention memory or anything similar... this is a GCD problem.
Do you know why self.ubiquitousURL is not accessible outside of the GCD block? Thanks.
You are making async call. So this line NSLog(#"ubiq outside: %#", self.ubiquitousURL); will get executed whether or not your code inside backgroundQueue is done.
You would see the outside log first then inside log.
dispatch_async means "run this later". Thus, the code inside the block doesn't run immediately; it runs at some later time, after the "outside" NSLog call has already been run. If you were to, for instance, put sleep(5) before the NSLog call, you would probably see the value. (You shouldn't really do that in the actual code, though; it would basically freeze the app for five seconds.)
If you want to run more code on the main queue after you've set that property, do something like this:
dispatch_async(backgroundQueue,^{
self.ubiquitousURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSLog(#"ubiq inside: %#", self.ubiquitousURL);
if (self.ubiquitousURL) {
self.iCloudDocURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#Documents", self.ubiquitousURL]];
self.iCloudDocString = [self.iCloudDocURL absoluteString];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loadFiles) name: UIApplicationDidBecomeActiveNotification object:nil];
// ************** NEW HOTNESS HERE **************
dispatch_async(dispatch_get_main_queue(),^{
NSLog(#"ubiq outside: %#", self.ubiquitousURL);
});
} else {
/* change to the main queue if you want to do something with the UI. For example: */
dispatch_async(dispatch_get_main_queue(),^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Please enable iCloud" message:nil delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
});
}
});
Replace that NSLog line with a method call to do actual work if that's what you want to do once you've retrieved the iCloud URL.
i have searched a lot on the web but can not find the actual sample source code that can help me to get started for google chat implementation , the sample code provided with the xmpp framework also does not tell clearly about it, as it have a sample project of Mac desktop application.
I have been able to show all my friends who are online/ofline/away with the help of sample project(iphoneXmpp) which is provided in the xmppframework, but it also doest tell anything about how to initiate a chat.
Please provide me any sample source code so that i can initialize the google chat in my app.
i am really stuck.
thanks in advance
okey i didnt give up and had some solution after looking into the desktop application of xmpp framework and tried to include it in my iphone app..
here is the code to send message to our chat friend on gmail..
-(void)sendMessage
{
messageStr = [NSString stringWithFormat:#"%#",[msgField text] ];
//messageStr = [NSString stringWithString:#"hello ji....."];
BOOL isEmpty = [ self validateIsEmpty:msgField.text];
if([messageStr length] > 0 && isEmpty == NO )
{
NSXMLElementK *body = [NSXMLElementK elementWithName:#"body"];
[body setStringValue:messageStr];
NSXMLElementK *message = [NSXMLElementK elementWithName:#"message"];
[message addAttributeWithName:#"type" stringValue:#"chat"];
[message addAttributeWithName:#"to" stringValue:[[user jid] full]];
[message addChild:body];
[[self xmppStream ] sendElement:message];
}
and in didReceiveMessage , i have following code...
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
NSLog(#"---------- xmppStream:didReceiveMessage: ----------");
NSLog(#"--jid---%#", [[user jid] full]);
NSLog(#"--from----%#", [message from]);
//if(![[[user jid] full] isEqual:[message from]]) return;// important when chatting with 2 or more .. and receiving 2 or more messages...
if([message isChatMessageWithBody])
{
NSString *msg = [[message elementForName:#"body"] stringValue];
NSLog(#"mmmmmmmmmmssssssgggg-%#",msg);
[str appendString:[NSString stringWithFormat:#"%#:%#\n\n", [message from], msg]];
[chatBox setText:str];
}
}
i'm able to send/recieve the chat using these two methods but problem is that some times the person's id which i selected from the table view of available online contacts(to whom we can chat with) does'nt receive the message but any other person receives the message..
Cheers!!
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message {
NSString *msg = [[message elementForName:#"body"] stringValue];
NSString *from = [[message attributeForName:#"from"] stringValue];
if (msg.length==0) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Receiving Message"
message:[NSString stringWithFormat:#"From %#",from]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
}
if (msg.length!=0) {
NSMutableDictionary *m = [[NSMutableDictionary alloc] init];
[m setObject:msg forKey:#"msg"];
[m setObject:from forKey:#"sender"];
NSLog(#"message received : %#", m);
[_messageDelegate newMessageReceived:m];
}
}
This works great for you, and it will also give you the alert who is sending the message and who wants to chat with you, However I'm just stuck from where should I implement the Logout for the user through which I logged in to iOS SDK.
This tutorial should do the trick : http://mobile.tutsplus.com/tutorials/iphone/building-a-jabber-client-for-ios-server-setup/
I have been trying to implement the GameKit Framework for bluetooth connection and want to use a Server/Client relationship to reduce lag and be able to distinguish between to connected devices. I found this thread and it is similar to what I am trying to do, but the code doesn't work for me. Here is what I have:
Connect Method:
-(IBAction) btnConnect:(id) sender {
if(sender == server){
[self.currentSession initWithSessionID:#"BT" displayName:nil sessionMode:GKSessionModeServer];
currentSession.available == YES;
NSLog(#"Setup Server");
}else{
[self.currentSession initWithSessionID:#"BT" displayName:nil sessionMode:GKSessionModeClient];
currentSession.available == YES;
NSLog(#"Setup Client");
}
currentSession.delegate = self;
currentSession.disconnectTimeout = 0;
[currentSession setDataReceiveHandler:self withContext:nil];
[client setHidden:YES];
[server setHidden:YES];
[disconnect setHidden:NO];
}
didChangeState:
- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state {
NSLog(#"didChangeState was called with status: %#.", state);
switch (state)
{
case GKPeerStateConnected:
NSLog(#"connected");
break;
case GKPeerStateDisconnected:
NSLog(#"disconnected");
[self.currentSession release];
currentSession = nil;
[connect setHidden:NO];
[disconnect setHidden:YES];
break;
case GKPeerStateAvailable:
NSLog(#"Server is Available, Presenting UIALert...");
NSLog(#"%#", peerID);
peerName = [session displayNameForPeer:peerID];
NSLog(#"%#", peerName);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Server Available!" message:[NSString stringWithFormat:#"The Server %# is Available, Would you like to Connect?", peerName] delegate:self cancelButtonTitle:#"Decline" otherButtonTitles:#"Accept", nil];
[alert show];
[alert release];
if(selection == #"accept"){
[session connectToPeer:peerID withTimeout:15];
session.available = NO;
}else{
}
break;
}
}
didReceiveConnectionRequest:
- (void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID{
NSLog(#"Recieved Connection Request");
NSLog(#"%#", peerID);
peerName = [session displayNameForPeer:peerID];
NSLog(#"%#", peerName);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Connection Request" message:[NSString stringWithFormat:#"The Client %# is trying to connect.", peerName] delegate:self cancelButtonTitle:#"Decline" otherButtonTitles:#"Accept", nil];
[alert show];
[alert release];
if(selection == #"accept"){
[session acceptConnectionFromPeer:peerID error:nil];
}else{
[session denyConnectionFromPeer:peerID];
}
}
I think I have this all setup right, but the didChangeState isn't getting called to inform the user that another device is available. Am I missing something or should I try to use a different method. Thanks for any help
currentSession.disconnectTimeout = 0;
The disconnect timeout is a time in seconds that peers should wait before disconnecting unresponsive peers. You don't want this to be 0. The default is 20 seconds, you should leave it there or say like 10 seconds. I actually don't set this in my GameKit code and it works well.
Also, it might help to post your entire implementation class somewhere. We'll need to make sure you are implementing GKSessionDelegate, e.g.:
#interface SomeObject : NSObject <GKSessionDelegate>
Also, you're setting up a Peer-2-Peer above. You said you were trying to do client/server. If so you should start the client session with a mode of GKSessionModeClient and server as GKSessionModePeer.
Lastly...are you testing this on actual devices or with a device and simulator? Don't forget that simulator and first gen iPhones and touches do not support bluetooth. So you'll have to have everyone involved connected to the same wireless network for anything to happen.
What are you seeing in the console when you start up your debug session?