0

Hey first of just to let you all know i'm new to Core-Data so be essay with me, Ok I have a very basic login system which I have an entity named "Account" and 2 attribute named: "email", "password".

Let's start from the problem and move on from there:

Now I was able to upload/insert data into sqlite database and also use "NSpredicate" to authorize users and passwords when user try to login in. The app have 3 windows Home,Login,SignUp; My problem now is: to retrieve the specific user (that is logged in) "email" to be displayed into "UIlabel" on the Home screen.

What I did for that is created an array to store the results just look here (This is on the "Home" screen):

NSArray *results = [context executeFetchRequest:request error:&error];

for (AccountBase *account in results) {

        _label.text = [NSString stringWithFormat:@"%@", account.email];

    }

The problem:

I only get the last user who Signup "email" and if for instance, now new user signup and is email is "mail.com" then, he would see an older mail named "gmail.com" for example.

My Code for signup:

-(IBAction)signup:(id)sender{

NSManagedObjectContext *context = [self managedObjectContext];

if (self.contactdb) {
    // Update existing device
   // [self.contactdb setValue:self.fullname.text forKey:@"fullname"];
    //[self.contactdb setValue:self.email.text forKey:@"email"];
    //[self.contactdb setValue:self.phone.text forKey:@"phone"];

} else {
    // Create a new device
    NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:@"Account" inManagedObjectContext:context];
    [newDevice setValue:self.email.text forKey:@"email"];
    [newDevice setValue:self.password.text forKey:@"password"];

    NSLog(@"%@",newDevice);
    //[newDevice setValue:self.phone.text forKey:@"phone"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
    NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}

[self dismissViewControllerAnimated:YES completion:nil];

}

The problem:

No problem, as far as I know, The info is been send very good to the database.

My code for login:

    - (IBAction)processLogin:(id)sender {



     NSManagedObjectContext *context = [self managedObjectContext];

    // CORE DATA
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Account" inManagedObjectContext:context];

    // set entity for request
    [request setEntity:entity];

    // filter results using a predicate
    NSPredicate *pred =[NSPredicate predicateWithFormat:(@"email = %@"), _emailfld.text];
    NSPredicate *pred2 =[NSPredicate predicateWithFormat:(@"password = %@"), _passwordfld.text];

    // set predicate for the request
    [request setPredicate:pred];
    [request setPredicate:pred2];

    NSError *error = nil;

    // store DB usernames in results array
    NSArray *results = [context executeFetchRequest:request error:&error];

    NSLog(@"The returned results are %@",results);

    // check text field against results stored in DB
    for (Account *anAccount in results) {
        if ([anAccount.email isEqualToString:_emailfld.text] || [anAccount.password isEqualToString:_passwordfld.text]){
           // NSLog(@"Your username exists");
             NSLog(@"Your pin is correct"); 
 [self performSegueWithIdentifier:@"showhome" sender:sender];

        }

        else if (![anAccount.email isEqualToString:_emailfld.text] || [anAccount.password isEqualToString:_passwordfld.text]){

          [self dismissViewControllerAnimated:YES completion:nil];

           NSLog(@"Your username is bad"); 
        }

        else{
            [self dismissViewControllerAnimated:YES completion:nil];
           NSLog(@"Username dosent exitst");
        }

    }


}

The problem:

No problem code is working if user enter the right info he is been moved to the home screen.

But and its a big But, the problem is that I need a unique identifier to be working with inside core data and I know its not like mysql database with the primary key. Also I heard about Object-ID but I have no idea on how to use it. Found zero info that would help on google and zero examples.

The main Problem:

What I need is after the user is login to get is object id of is email and then in the Home screen to get this id back and then print is email into a "UIlabel".

Now I don't now how exactly core data work with unique identifier's so I need a method to accomplish this task and to make the different between all "email's" in my sqlite database to the right user.

  • You can just store the Account object into a property of your AppDelegate and then you can access it from anywhere in your app. No need to mess around with objectids - you have the Account object so you can just access its email address property – Paulw11 Jan 24 '15 at 19:45
  • Can you please show it to me how to do this i'm really new on core-data. Its like Chenese what you just said – dani_jon133 Jan 24 '15 at 19:47
  • Hey how do I make the predicate to check both (email and password)? and sorry but I can't vote yet i'm too new to the site.. – dani_jon133 Jan 24 '15 at 21:13
  • NSPredicate *pred =[NSPredicate predicateWithFormat:(@"email = %@ and password=%@"), self.emailfld.text,self.passwordfld.text]; Get into the habit of using `self.` and not `_` unless you specifically want to bypass a setter/getter – Paulw11 Jan 24 '15 at 21:18
  • OHHH dummy me :) thank you worked great I will now move on to fix the password and keychain thank you. – dani_jon133 Jan 24 '15 at 21:21

2 Answers2

1

You have some problems you aren't aware of. Primarily, you shouldn't be storing passwords in core data because it isn't secure. Also, when you set a predicate on a fetch request it replaces any existing predicate so your login check only looks at the password, not the email address (because that predicate is replaced).

For your first stated problem, you don't see the last user to sign up unless you have a date or a count and a sort descriptor on the fetch request. You actually see a random user. What you should really be doing is using a table view to display all user email addresses (or not showing any) for the login.

If the fetch for login returns something, you don't then need to check it because you have already predicated that it's correct.

After login, you should return the user or store it somewhere. The other answer says in the app delegate, which will work, but is bad practice. You might want to create a singleton user controller which holds the logged in user (and controls access to it).

If you need a unique I'd in the app for each user then you should create a UUID and add it to a new attribute on your entity.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • You right this is just my basic experiencing/ Trial and error with the Core-Data, I know the password should be In the keychain first and only then in the core data I would also take your'e advices very seriously thank you!!. – dani_jon133 Jan 24 '15 at 20:28
  • No problem. Remember that you can vote and tick answers which help and solve your problem ;-) – Wain Jan 24 '15 at 20:59
0

You can add a property to your AppDelegate class to store the Account object for the logged in user -

AppDelegate.h

@property (strong,nonatomic) Account *currentUser;

Then you can set this when you have authenticated the user -

- (IBAction)processLogin:(id)sender {



     NSManagedObjectContext *context = [self managedObjectContext];

    // CORE DATA
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Account" inManagedObjectContext:context];

    // set entity for request
    [request setEntity:entity];

    // filter results using a predicate
    NSPredicate *pred =[NSPredicate predicateWithFormat:(@"email = %@"), _emailfld.text];
    NSPredicate *pred2 =[NSPredicate predicateWithFormat:(@"password = %@"), _passwordfld.text];

    // set predicate for the request
    [request setPredicate:pred];
    [request setPredicate:pred2];

    NSError *error = nil;

    // store DB usernames in results array
    NSArray *results = [context executeFetchRequest:request error:&error];

    NSLog(@"The returned results are %@",results);

    // check text field against results stored in DB
    for (Account *anAccount in results) {
        if ([anAccount.email isEqualToString:_emailfld.text] || [anAccount.password isEqualToString:_passwordfld.text]){
           // NSLog(@"Your username exists");
             NSLog(@"Your pin is correct"); 
           AppDelegate *app=(AppDelegate *)[UIApplication sharedApplication].delegate;
           app.currentUser=anAccount;
 [self performSegueWithIdentifier:@"showhome" sender:sender];

        }

        else if (![anAccount.email isEqualToString:_emailfld.text] || [anAccount.password isEqualToString:_passwordfld.text]){

          [self dismissViewControllerAnimated:YES completion:nil];

           NSLog(@"Your username is bad"); 
        }

        else{
            [self dismissViewControllerAnimated:YES completion:nil];
           NSLog(@"Username dosent exitst");
        }

    }

}

Then, anywhere you need to access the current user you can use

*AppDelegate *app=(AppDelegate *)[UIApplication sharedApplication].delegate;
self.sometextfield.text=app.currentUser.email;
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Hey Paulw can you help me with something I just noticed now that only when users are pressing "Login" then they can see their "email" so basically using the app delegate is not such a good idea because users can't see their "email" in their "Profile" unless they tap on login but what if I want to be able to show them their profile without have them to login each time over and over again what should I do to pass that? – dani_jon133 Feb 10 '15 at 21:02
  • I don't understand your comment - You can access the app delegate from any object in your app using the code in my answer. If you want to store a user's successful login state between executions of your app then you will need to use some local storage to do so - NSUserDefaults (not very secure) or the Keychain (More secure) – Paulw11 Feb 10 '15 at 22:05
  • I have a -bool in NSUserDefaults for when user logged in but thats not the problem, and I can access the app delegate also successfully. Sorry let me explain my self better. The problem is only when you actually already logged in to the app and passed the "login" screen now you have you're profile screen. You can see your'e "email" in the profile screen but after the app quits and re open the app only remember's that you are logged in since the -bool for the nsuserdfuealts and you're info in the profile screen is not been saved. – dani_jon133 Feb 11 '15 at 05:51
  • You can save the URI of the CoreData profile object in NSUserDefaults - http://www.cocoawithlove.com/2008/08/safely-fetching-nsmanagedobject-by-uri.html – Paulw11 Feb 11 '15 at 05:57
  • I can't seem to understand nothing from this tutorial. I tried doing this in the login screen : NSURL *uri = [[app.currentUser objectID] URIRepresentation]; NSData *uriData = [NSKeyedArchiver archivedDataWithRootObject:uri]; but I am not so sure about this I just have no idea what I need to do and which object id to find/get . – dani_jon133 Feb 11 '15 at 18:54
  • You can get the URI for your `app.currentUser` object and store this in NSUserDefaults. Then when your app restarts you can use this URI to retrieve the user's profile form coreData and restore the `applcurrentUser` property without requiring the user to log in again. – Paulw11 Feb 11 '15 at 19:56
  • http://pastebin.com/Ysq1U5Da this is what I did have a look, its not working still return nil data in the profile vc – dani_jon133 Feb 12 '15 at 21:50
  • I can see where you retrieve the object with ` NSManagedObject *object = [self.managedObjectContext objectWithID:objectId];` but I can't see where you are doing anything with the data you have retrieved. – Paulw11 Feb 12 '15 at 21:53
  • Its since I don't know what to data with that data now should I save it in array or nsdata? – dani_jon133 Feb 12 '15 at 22:09
  • You should do with it whatever you did with the object in the login method - put it in the app delegate or whatever. It would probably e cleaner to have this code in your AppDelegate `didFinishLaunchingWithOptions` - if the uri is in user defaults then restore the profile. If not then the user needs to login. You can then implement a "logout" by simply deleting the key from user defaults. – Paulw11 Feb 12 '15 at 22:11
  • sorry to bother you so much I still don't get it here is what I did http://pastebin.com/54DZma2r I get the object id back in the log but how do I tell the app hey this is this user use it and show is profile! I just can't seem to understand this part I am missing something here for sure – dani_jon133 Feb 12 '15 at 23:01
  • Once you have `object` you can access the data from it the same as you did when you got it from the login query. – Paulw11 Feb 12 '15 at 23:07
  • Hey do you by saying "the same as you did when you got it from the login query" to do this in the login query "app.objectid = anAccount;" because I still don't get it can you please give me an example i'm still stack with this until now. – dani_jon133 Feb 13 '15 at 17:12
  • Once you have retrieved object, just assign it `app.currentUser=object` – Paulw11 Feb 13 '15 at 20:50