FBSession.session.accessTokenData.userID always nil - objective-c

Been using FBConnect for selecting friends, but the FBSession's accessTokenData never has the userID of the user that started the session (also isn't present in their login demo). FBFriendPickerViewController returns friend users with their Ids just fine, but I don't see anything about the current user. Is it possible to get the current user's Id from the FB active session, accessTokenData, or some other means?
EDIT:
Is it possible to get the user's Id returned from this call?
[FBSession openActiveSessionWithReadPermissions:#[#"public_profile", #"user_friends"]
It'll be much nicer to do it all in one step.

You have to request to "/me" to get an 'user_id' since access_token property is encrypted and you couldn't access it from that way.

Here's what I ended up doing:
if (!FBSession.activeSession.isOpen) {
// if the session is closed, then we open it here, and establish a handler for state changes
[FBSession openActiveSessionWithReadPermissions:#[#"public_profile", #"user_friends"]
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
if (error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
else if (session.isOpen) {
if ([session.declinedPermissions containsObject:#"user_friends"]) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"friends"
message:#"Must allow accessing friends"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
else {
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
if (error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
else {
NSLog(#"name %# id %#", user.first_name, user.objectID);
[[FBRequest requestForMyFriends] startWithCompletionHandler: ^(FBRequestConnection *connection,
NSDictionary* result,
NSError *error) {
NSArray* friends = [result objectForKey:#"data"];
NSLog(#"Found: %i friends", friends.count);
for (NSDictionary<FBGraphUser>* friend in friends) {
NSLog(#"I have a friend named %# with id %#", friend.name, friend.objectID);
}
}];
}
}];
}
}
}];

Related

Terminating app due to uncaught exception 'NSInvalidArgumentException' 5

i am getting this error while running my app:
my code is:
- (IBAction)yes:(id)sender {
UIAlertView *msgbox=[[UIAlertView alloc]initWithTitle:#"Warrning" message:nil delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
if ([_usernametextbox.text length]>0)
{
if ([_passwordtextfield.text length]>0)
{
NSMutableString *ms=[[NSMutableString alloc]initWithFormat:url,_usernametextbox.text,_passwordtextfield];
NSURL *serviceurl=[[NSURL alloc] initWithString:ms];
NSError *error=nil;
NSData *data=[NSData dataWithContentsOfURL:serviceurl options:NSDataReadingUncached error:&error];
NSMutableDictionary *dic=[[NSMutableDictionary alloc]init];
NSString *jsondata=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
SBJSON *parsedata=[[SBJSON alloc] init];
dic=[parsedata objectWithString:jsondata error:nil];
if([dic count]>0)
{
NSMutableString *st=[[NSMutableString alloc]initWithString:[dic objectForKey:#"loginResult:#""+"]]; //#" " withString:#"+"
if ([st isEqualToString:#"YES"])
{
UIAlertView * alert=[[UIAlertView alloc] initWithTitle:#"accept" message:#"correct username" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}
else
{
UIAlertView * alert=[[UIAlertView alloc] initWithTitle:#"Error" message:#"Invalid UserName and Password" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}
}
}
else
{
msgbox.message=#"Please Enter Password...";
[msgbox show];
}
}
else
{
msgbox.message=#"Please Enter Username...";
[msgbox show];
}
I get the error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '* -[NSConcreteData
initWithContentsOfURL:options:error:]: nil URL argument'
on the line:
NSMutableString *st=[[NSMutableString alloc]initWithString:[dic objectForKey:#"loginResult:#""+"]]; //#" " withString:#"+" if ([st isEqualToString:#"YES"])

sign in/ Login Via twitter iOS7

it has been two weeks and I couldn't find a useful answer for my query, I've tried a lot of codes nothing had worked for me, Actually i am trying to build a log in page that able to user to sign in/log in from their Facebook or Twitter, its totally work for Facebook but the problem with twitter.
I've tried this code below please advice and help as I am still getting an error and the error is
(property twitterHandle not found on object of type "ViewController")
here is the code I've used.Note that I use Parse for my database.
*I've created an action button in the header called Twitter
-(IBAction)Twitter:(id)sender;
*and I had this code in the m file as showing below:
- (IBAction)Twitter:(id)sender {
{
// borrowed from: http://eflorenzano.com/blog/2012/04/18/using-twitter-ios5-integration-single-sign-on/
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *twitterType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[store requestAccessToAccountsWithType:twitterType options:nil completion:^(BOOL granted, NSError *error)
{
if(granted) {
// Access has been granted, now we can access the accounts
// Remember that twitterType was instantiated above
NSArray *twitterAccounts = [store accountsWithAccountType:twitterType];
// If there are no accounts, we need to pop up an alert
if(twitterAccounts != nil && [twitterAccounts count] == 0)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No Twitter Accounts"
message:#"There are no Twitter accounts configured. You must add or create a Twitter separately."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
} else {
ACAccount *account = [twitterAccounts objectAtIndex:0];
// Do something with their Twitter account
NSURL *url = [NSURL URLWithString:#"http://api.twitter.com/1/account/verify_credentials.json"];
SLRequest *req = [SLRequest requestForServiceType:SLServiceTypeTwitter
requestMethod:SLRequestMethodPOST
URL:url
parameters:nil];
// Important: attach the user's Twitter ACAccount object to the request
req.account = account;
[req performRequestWithHandler:^(NSData *responseData,
NSHTTPURLResponse *urlResponse,
NSError *error)
{
// If there was an error making the request, display a message to the user
if(error != nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Twitter Error"
message:#"There was an error talking to Twitter. Please try again later."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
return;
}
// Parse the JSON response
NSError *jsonError = nil;
id resp = [NSJSONSerialization JSONObjectWithData:responseData
options:0
error:&jsonError];
// If there was an error decoding the JSON, display a message to the user
if(jsonError != nil)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Twitter Error"
message:#"Twitter is not acting properly right now. Please try again later."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
return;
}
NSString *screenName = [resp objectForKey:#"screen_name"];
self.twitterHandle = screenName;
PFUser *currentUser = [PFUser currentUser];
PFQuery *query = [PFQuery queryWithClassName:#"_User"];
[query whereKey:#"username" equalTo:currentUser.username];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// Do something with the found objects
for (PFObject *object in objects)
{
object[#"TwitterHandle"] = self.twitterHandle;
[object saveInBackground];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}];
}
}
}];
}
}
Please Please Please help :(
(property twitterHandle not found on object of type "ViewController")
is telling you that somewhere, you are trying to access a property called twitterHandle on an object of type ViewController which has no such property.
I suspect the issue is in this line:
self.twitterHandle = screenName;
You just need to add a property to your view controller interface like this:
#property (nonatomic, strong) NSString *twitterHandle;

Migrating iCloud store to local

Migration works fine on Simulator. However on a device, I see no error messages but migrated store is empty.
NSDictionary *iCloudOptions = #{
NSPersistentStoreUbiquitousContentNameKey : #"iCloudNimbleStore",
NSPersistentStoreUbiquitousContentURLKey : #"transactions_logs",
NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES
};
NSDictionary *localOptions = #{NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES
};
if (![[NSFileManager defaultManager]fileExistsAtPath:self.storeURL.path]) {
#synchronized(#"Migration")
{
// thread-safe code
if ([[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]) {
NSLog(#"iCloud");
[self migrateStoreFromURL:[self nb_URLToStoreWithFilename:[self nb_appName]]options:iCloudOptions];
}else{
[self migrateStoreFromURL:[self nb_URLToStoreWithFilename:[NSString stringWithFormat:#"%#.sqlite", [self nb_appName]]] options:localOptions];
//
[self migrateStoreFromURL:[self nb_URLToOldStoreWithFilename] options:localOptions];
}
}
}
NSDictionary *options = #{
NSMigratePersistentStoresAutomaticallyOption:#YES
,NSInferMappingModelAutomaticallyOption:#YES
};
NSError *error = nil;
[_coordinator lock];
_store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self storeURL] options:options error:&error];
[_coordinator unlock];
if (!_store) {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Loading Fail" message:[NSString stringWithFormat:#"Failed to add store. Error: %#", error] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
NSLog(#"Failed to add store. Error: %#", error);abort();
} else {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Loading Success" message:[NSString stringWithFormat:#"Successfully added store: %#", _store] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
NSLog(#"Successfully added store: %#", _store);
if (_store && !error) {
// Encrypt the password database
NSError *encrError;
NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:self.storeURL.path error:&encrError]){
NSLog(#"Unresolved error with password store encryption %#, %#", encrError, [encrError userInfo]);
abort();
}else {NSLog(#"Encrypted");}
}
}
Here is migration procedure:
- (void)migrateStoreFromURL:(NSURL *)oldStoreURL options:(NSDictionary *)oldOptions{
if (debug==1) {
TFLog(#"Running %# '%#'", self.class, NSStringFromSelector(_cmd));
}
if (_store)
{
NSLog(#"NOT NEEDED");
return;
}
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Migration" message:[NSString stringWithFormat:#"Found old store at %#",oldStoreURL.path] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:self.storeURL.path]) {
NSDictionary *options =
#{
NSMigratePersistentStoresAutomaticallyOption:#YES
,NSInferMappingModelAutomaticallyOption:#YES
};
NSError *error = nil;
[_coordinator lock];
NSPersistentStore *srcPS = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:oldStoreURL
options:oldOptions
error:&error];
_store = [_coordinator migratePersistentStore:srcPS
toURL:self.storeURL
options:options
withType:NSSQLiteStoreType
error:&error];
[_coordinator unlock];
if (_store && !error) {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Migration Success" message:[NSString stringWithFormat:#"Old store successfully migrated from %#",oldStoreURL.path] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
// Encrypt the password database
NSError *encrError;
NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:self.storeURL.path error:&encrError]){
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Encryption Error" message:[NSString stringWithFormat:#"Unresolved error with password store encryption %#, %#", encrError, [encrError userInfo]] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
}else{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Migration Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
}
Upd.: I checked size of the newly migrated store and it's 0. Most strange is that _store && !error is true. I also tried to add NSPersistentStoreRemoveUbiquitousMetadataOption: #YES to migration options but it doesn't change anything.
Upd. 2 I think that on a device iCloud store url is nil before its loaded. I need some workaround to wait until its finished.
I'm not 100% sure I understand what you are trying to do with the migrations. It is common to seed data in an empty store with a migration, but it looks like you are trying to migrate data out of iCloud into your local store. Is that right? You should not need to do that. iCloud should automatically add the data from other devices to your store.
This line also doesn't look right:
NSPersistentStoreUbiquitousContentURLKey : #"transactions_logs",
I think you want to use a URL there that points to the transaction log directory inside the iCloud container. Eg.
NSURL *containerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSURL *url = [containerURL URLByAppendingPathComponent:#"transactions_logs"];
When working with iCloud, it is important to realize that data does not transfer instantaneously. It can take a while to arrive, and your app doesn't really have any way to know for sure if there is data coming. You can monitor metadata with metadata queries, but even that often arrives some time after data on other devices has already been generated.
So simply looking in the ubiquity container for data will not help much, because there may or may not be data available. You just do not know, and you have to develop your approach with that assumption in mind, so that it can handle any delays.
The migrations required to get iCloud sync working with Core Data are messy and unnecessary. You are probably much more likely to get things working well with a framework that does that stuff automatically, such as Core Data Ensembles. (Disclosure: I am the developer of Ensembles.)

CLGeocoder Multiple Addresses?

I need to geocode two different addresses but I need to have the results inside the same scope/method. How can I direct the same variables location1 and location2 inside the same block.
Here is my current code...
- (IBAction)calculateDistance {
[textFieldDestination resignFirstResponder];
NSString *origin = [textFieldOrigin text];
if (origin) {
CLGeocoder *geoCode = [[CLGeocoder alloc] init];
[geoCode geocodeAddressString:origin completionHandler: ^(NSArray *placemarks, NSError *error) {
if (!error) {
CLPlacemark *place = [placemarks objectAtIndex:0];
CLLocation *location = place.location;
CLLocation *location1 = [[CLLocation alloc] initWithLatitude:location.coordinate.latitude longitude:location.coordinate.longitude];
} else {
NSString *string = [NSString stringWithFormat:#"%# - also make sure you have inputted a valid city", [error localizedDescription]];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:string delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
}];
}
NSString *destination = [textFieldDestination text];
if (destination) {
CLGeocoder *geoCode2 = [[CLGeocoder alloc] init];
[geoCode2 geocodeAddressString:destination completionHandler:^(NSArray *placemarks, NSError *error) {
if (!error) {
CLPlacemark *place = [placemarks objectAtIndex:0];
CLLocation *location = place.location;
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:location.coordinate.latitude longitude:location.coordinate.longitude];
} else {
NSString *string = [NSString stringWithFormat:#"%# - also make sure you have inputted a valid city", [error localizedDescription]];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:string delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
}];
}
CLLocationDistance distanceM = [location1 distanceFromLocation:location2]; // obviously error so you can see that I need location1 and location2 in the same block.... but how!?
[metreDistance setText:[NSString stringWithFormat:#"%f", distanceM]];
}
I found out how to accomplish this. For each geocoding block, I directed to a method that had specific parameters. Pretty simple, just needed to use logic!

Method gets called two times

I am doing a NSURLConnection that downloads a file only if there is a new one (checked with the last-modified date). But to accomplish this I am using two methods with two different NSURLRequests and NSURLConnection. Well, they do the same.
- (IBAction)uppdatera:(id)sender
{
checkHeaders = YES;
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.forellgatan.se/site/ftp_files/Kapareskolan.zip"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection)
{
self.receivedData = [[NSMutableData data] retain];
}
else
{
UIAlertView *connectFailMessage = [[UIAlertView alloc] initWithTitle:#"Ingen internetanslutning! 1" message:#"Anslut dig till internet för att ladda ner!" delegate: self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[connectFailMessage show];
[connectFailMessage release];
}
}
- (void)downloadNewFile
{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.forellgatan.se/site/ftp_files/Kapareskolan.zip"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *theConnection2 = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection2)
{
self.receivedData = [[NSMutableData data] retain];
}
else
{
UIAlertView *connectFailMessage = [[UIAlertView alloc] initWithTitle:#"Ingen internetanslutning! 2" message:#"Anslut dig till internet för att ladda ner!" delegate: self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[connectFailMessage show];
[connectFailMessage release];
}
checkHeaders = NO;
self.progressView.hidden = NO;
}
It goes through the didReceiveResponse method:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if (checkHeaders == YES)
{
NSHTTPURLResponse *test = (NSHTTPURLResponse *)response;
if ([test respondsToSelector:#selector(allHeaderFields)])
{
NSDictionary *metaData = [test allHeaderFields];
NSString *lastModifiedString = [metaData objectForKey:#"Last-Modified"];
NSString *savedString = [[NSUserDefaults standardUserDefaults] stringForKey:#"LastModified"];
if (![lastModifiedString isEqualToString:savedString])
{
[self downloadNewFile];
}
else if ([lastModifiedString isEqualToString:savedString])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Ingen uppdatering tillgänglig" message:#"Det finns ingen uppdatering att hämta just nu." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
[standardUserDefaults setObject:lastModifiedString forKey:#"LastModified"];
[standardUserDefaults synchronize];
}
}
[self.receivedData setLength:0];
self.fileSize = [[NSNumber numberWithLong: [response expectedContentLength]] retain];
}
Last the connectionDidFinishLaunching method:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (checkHeaders == NO)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
[self.receivedData writeToFile:[basePath stringByAppendingPathComponent:#"Kapareskolan.zip"] atomically:YES];
[self unzipDownloadedFile];
self.progressView.hidden = YES;
NSLog(#"Finished...");
}
[connection cancel];
}
I know the didFinishLaunching method gets called twice, but I want to know how I could get so the method doesn't get called twice if there is an update?
I know it's a lot asked and much code here but just give me a hint and I'll be very thankful.
If you're done with the first connection object in the didReceiveResponse method you should cancel it then. Otherwise it's going to make it to the connectionDidFinishLoading method.
I think this is what you want:
if (![lastModifiedString isEqualToString:savedString])
{
[connection cancel];
[self downloadNewFile];
}
Also It looks like you're setting checkHeaders to NO after you start the second request which could cause a race condition.
According to the programming guide, a connection "can be canceled any time before the delegate receives a connectionDidFinishLoading: or connection:didFailWithError: message by sending the connection a cancel message".
so why not try moving
[connection cancel];
from the connectionDidFinishLoading method to just after your if-else block in the didReceiveResponse delegate method? You want to cancel the "checkHeaders==YES" connection in either case; either you're about to kick off a new connection, or you already know all you need to know about the current connection.
UPDATED as requested:
if (![lastModifiedString isEqualToString:savedString]) {
[self downloadNewFile];
} else { // you've already implicitly checked for equality above
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Ingen uppdatering tillgänglig" message:#"Det finns ingen uppdatering att hämta just nu." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
// you've used the connection for everything that you need, so cancel it in either case
[connection cancel];
as downloadNewFile kicks off its NSURLConnection asynchronously, this should be okay in the event that the two strings are equal. Slightly safer would be to move the cancel method call to just prior to the if-else check.