Migrating iCloud store to local - objective-c

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.)

Related

FBSession.session.accessTokenData.userID always nil

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);
}
}];
}
}];
}
}
}];

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;

NSError and activating it while polling JSON data - Objective C

Trying to understand how/why NSError works in this case. I'm trying to do a simple UIAlertview when the JSON query returns no data (either the server is sending it or the user is not on internet). I have looked at about a dozen different answers and am just confusing myself more and more.
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
kLatestChartDataURL];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
});
}
- (void)fetchedData:(NSData *)responseData {
NSError *error = nil;
NSArray *theArray = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSDictionary *dict0 = [theArray objectAtIndex:147];
if (NO) {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Data failed to load."
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:#"Retry",nil];
[message show];
}
I've also tried:
if (!theArray) {
and
BOOL results = [NSArray etc...
Any thoughts on this would be appreciated, I've also looked at the developer docs and Cocoa is Mygirlfriend examples, and my brain has turned to mush at this point.
Try this
NSError *error = nil;
NSArray *theArray = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
if (!theArray) { // DO THIS CHECK
NSLog(#"error: %#", error);
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Data failed to load."
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:#"Retry",nil];
[message show];
}
else
{
NSLog(#"parsed data");
    NSDictionary *dict0 = [theArray objectAtIndex:147];
}
If any error occurs while getting json from NSData then it will be recorded in object of NSError. So if this object is nil means there is no error while parsing json, and if that object holds value means there is some issue while parsing.

Pull more than 1 echo from PHP

OK, I have a login screen that sends username and password to a PHP page. The PHP page echo's Yes or No based on the login status, but I would like it to echo the user id from the database for further use. Do I have to use JSON for this? I dont know how to retrieve more than one echo's. Currently the code is:
- (IBAction) login: (id) sender
{
NSString *post =[NSString stringWithFormat:#"username=%#&password=%#",usernameField.text, passwordField.text];
NSString *hostStr = #"https://www.mysite.com/login.php?";
hostStr = [hostStr stringByAppendingString:post];
NSData *dataURL = [NSData dataWithContentsOfURL: [ NSURL URLWithString: hostStr ]];
NSString *serverOutput = [[NSString alloc] initWithData:dataURL encoding: NSASCIIStringEncoding];
if([serverOutput isEqualToString:#"Yes"]){
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"Business Manager" message:#"Login Successful"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertsuccess show];
[alertsuccess release];
RootViewController *viewMenu = [[RootViewController alloc]
initWithNibName:#"RootViewController" bundle:[NSBundle mainBundle]];
self.rootViewController = viewMenu;
[viewMenu release];
[self.navigationController pushViewController:self.rootViewController animated:YES];
} else {
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Username or Password Incorrect"
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertsuccess show];
[alertsuccess release];
}
}
Now I just want to pull one more echo called "ID". Forming JSON for this seems arbitrary.
Technically you can use whatever response format you want -- though it's best to stick with something standard as you'll be able to find libraries made for you already. A REST service with a JSON response is a pretty easy way to facilitate communication between a server and client, so it would probably be a good idea to start there.

Authenticating Iphone Login with PHP/MySQL and HTTP Responses

Im trying to create a iPhone Objective C login page using PHP/MySQL to authenticate. Im trying to use HTTP responses as a way of authenticating. Right now it always fails to login , regardless of what you enter.
`- (IBAction) login: (id) sender
{
// this part isnt really implemented yet!
NSString *post =[NSString stringWithFormat:#"username=%#&password=%#",usernameField.text, passwordField.text];
NSURL *url = [NSURL URLWithString:#"MY URL HERE.php"];
NSURLRequest *request = [NSURLRequest requestWithURL: url];
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)request;
responseStatusCode = [httpResponse statusCode];
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:response error:nil];
if ([responseStatusCode statusCode] == 200) {
//do something
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"success" message:#"Your have logged in" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alertsuccess show];
[alertsuccess release];
}
else {
NSLog(#"Login failed.");
//do something else
UIAlertView *alertfail = [[UIAlertView alloc] initWithTitle:#"fail" message:#"login fail" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alertfail show];
[alertfail release];
}
`
and the PHP looks like
<?
session_start();
require("iphoneconnection.php");
$u = $_POST['username'];
$pw = $_POST['password'];
$check = "SELECT username, password FROM iphoneusers WHERE username='$u' AND password= '$pw'";
$login = mysql_query($check, $connect) or die(mysql_error());
// check user level and store in $row
if (mysql_num_rows($login) == 1) {
$row = mysql_fetch_assoc($login);
//$_SESSION['level'] = $row['level'];
$_SESSION['username'] = $u;
echo 'login success';
header("HTTP/1.1 200 OK");
//header("Location: index.php");
} else {
//header("Location: login.php");
//echo '403 Forbidden';
header("403 Forbidden");
}
mysql_close($connect);
?>
Anyone got any pointers? Even if its just to tell me if Im formatting my repsonses correctly in the PHP.
Thanks in advance.
Thanks for the response. I got it to work using:
- (IBAction) login: (id) sender
{
NSString *post =[NSString stringWithFormat:#"username=%#&password=%#",usernameField.text, passwordField.text];
NSString *hostStr = #"MY URL.php?";
hostStr = [hostStr stringByAppendingString:post];
NSData *dataURL = [NSData dataWithContentsOfURL: [ NSURL URLWithString: hostStr ]];
NSString *serverOutput = [[NSString alloc] initWithData:dataURL encoding: NSASCIIStringEncoding];
if([serverOutput isEqualToString:#"Yes"]){
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"success" message:#"You are authorized"
delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alertsuccess show];
[alertsuccess release];
} else {
UIAlertView *alertsuccess = [[UIAlertView alloc] initWithTitle:#"Fail" message:#"Invalid Access"
delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[alertsuccess show];
[alertsuccess release];
}
loginIndicator.hidden = FALSE;
[loginIndicator startAnimating];
loginButton.enabled = FALSE;
}
According to the sample you've given, I don't see that you're actually sending the username & password with the request. You're creating a string "post", but never including it in the request.
Also, this cast:
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)request;
doesn't look right. Why are you typecasting the request object to a response?