0

Thankyou for reading,

PS: I am a beginner so I am not too good at this unfortunetaly, but any help would be very appreciated

So basically I want to archive a big array which contains Account objects, which they themselves contain:

  • 1.a username in form of a NSString,

  • 2.an encrypted password array filled with NSNumbers, and

  • 3.a data array filled with service data objects.

The service data objects have the following:

    1. encrypted serviceType (NSArray filled with NSNumbers) (whatever service the username and password is for)
    1. encrypted username (NSArray filled with NSNumbers)
    1. encrypted password (NSArray filled with NSNumbers)

Now weirdly when trying to archive and save this, I get two errors. One time it won't let me add service data objects to the data array in the Account class anymore, with the following error message (or at least they dont show up in the NSTableView I have, however it does say they exsist):

[<ServiceData 0x60000023bfa0> valueForUndefinedKey:]: this class is not key value
coding-compliant for the key service.

and two, when I try to login in the the username and password from the Account class, it retrieves the username and the first couple and last couple NSNumbers of my password correctly, but the rest of the NSNumbers for the password are in the trillions or something, so I'm wondering what is going wrong, any help would be greatly appreciated.

Here is the code for my instance variables, how I used the NSKeyedArchiver and unarchiver, and how I went about saving and loading the files. Again, please help me, I have been stuck on this for a while and this is kind-of my last resort. I have no idea what is happening!


ACCOUNT CLASS:

H file:

@interface Account : NSObject <NSCoding>
{
@private
    NSString *username;
    NSMutableArray *password;
    NSMutableArray *accData;
}

@property NSString *username;
@property NSArray *password;

FULL M file:

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if(self)
    {
        username = [aDecoder decodeObjectForKey:@"username"];
        password = [aDecoder decodeObjectForKey:@"password"];
        accData = [aDecoder decodeObjectForKey:@"data"];
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:username forKey:@"username"];
    [aCoder encodeObject:password forKey:@"password"];
    [aCoder encodeObject:accData forKey:@"data"];
}

SERVICEDATA CLASS:

H file:

@interface ServiceData : NSObject <NSCoding>
{
@private
    NSArray* serviceData;
    NSArray* usernameData;
    NSArray* passwordData;
}

@property NSArray* serviceData; 
@property NSArray* usernameData;
@property NSArray* passwordData;

M file:

#import "Account.h"
#import "Crypt.h"

NSMutableArray *accounts;
NSInteger accountNumber = -1;


@implementation Account

@synthesize username;
@synthesize password;

- (id)initWithUsername:(NSString *)name withPassword:(NSMutableArray *)key
{
    self = [super init];
    if (self)
    {
        username = name;
        password = key;
        accData = [[NSMutableArray alloc]init];
    }
    return self;
}


/*
setters and getters
*/

-(NSString*)getUsername;
{
    return username;
}
-(NSArray*)getPassword;
{
    return password;
}
-(void)changePassword:(NSMutableArray*)newPassword;
{
    NSInteger sizeOldPass = [password count];
    NSInteger sizeNewPass = [newPassword count];
    int changeXObjects = (int)(sizeNewPass - sizeOldPass);
    int changeSize = abs(changeXObjects);
    //adjusts size differences
    if (changeXObjects < 0)
    {
        for(int i = 0; i < changeSize; i++)
        {
            [password removeLastObject];
        }
    }
    else if (changeXObjects > 0)
    {
        for(int i = 0; i < changeSize; i++)
        {
            NSNumber *value = [NSNumber numberWithInt:0];
            [password addObject:value];
        }
    }

    //change password
    NSInteger sizePass = [password count];
    for (int k = 0; k < sizePass; k++)
    {
        [password replaceObjectAtIndex:k withObject:newPassword[k]];
    }
}

-(NSMutableArray*)getAccData;
{
    return accData;
}
-(void)setAccData:(NSMutableArray*)input
{
    [input setArray: accData];
}

+(NSMutableArray*)getAccounts
{
    return accounts;
}

+(NSInteger)getAccountNumber
{
    return accountNumber;
}

+(void)setAccounts:(id)accs
{
    accounts = accs;
}

+(void)setAccountNumber:(NSInteger)number
{
    accountNumber = number;
}


/*
other methods
*/

+(void)addAccount:(id)acc
{
    [accounts addObject:acc];
}
+(void)deleteAccount:(NSInteger)index
{
    [accounts removeObjectAtIndex:index];
}


-(void)addAccData:(id)input
{
    [accData addObject:input];
}

-(void)deleteAccDataAt:(NSInteger)index
{
    [accData removeObjectAtIndex:index];
}

+(bool)checkPassword:(NSString*)passwordIn accountNumber:(NSInteger)index
{
    NSMutableArray *passwordInputCrypt = [Crypt encrypt:passwordIn];
    NSMutableArray *passwordCrypt = [accounts[index] getPassword];

    NSInteger lengthPassword = [passwordInputCrypt count];

    bool correctPassword = true;
    if([passwordCrypt count] == [passwordInputCrypt count])
    {
        for(int i = 0; i < lengthPassword; i++)
        {
            if(passwordCrypt[i]!=passwordInputCrypt[i])
                correctPassword = false;
        }
    }
    else
    {
        correctPassword = false;
    }

    if(correctPassword == true)
    {
        return true;
    }
    return false;
}


-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if(self)
    {
        username = [aDecoder decodeObjectForKey:@"username"];
        password = [aDecoder decodeObjectForKey:@"password"];
        accData = [aDecoder decodeObjectForKey:@"data"];
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:username forKey:@"username"];
    [aCoder encodeObject:password forKey:@"password"];
    [aCoder encodeObject:accData forKey:@"data"];
}
@end

LOADING FILE(filePath is given):

NSData *data = [[NSFileManager defaultManager] contentsAtPath:filePath];

if(data != nil)
{
    NSArray *arrayFromData = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    NSMutableArray *initArray = [NSMutableArray arrayWithArray:arrayFromData];
    [Account setAccounts:initArray];
}
else
{
    NSMutableArray *accountsInit = [[NSMutableArray alloc] init];
    [Account setAccounts:accountsInit];
}

SAVING FILE:

NSArray *accounts = [Account getAccounts];
NSString *filePath = [AppController getFilePath];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:accounts];

[data writeToFile:filePath atomically:YES];
jundl77
  • 476
  • 2
  • 15

1 Answers1

0

A few things:

  1. You should not be archiving password data to disk (even if you are encrypting it). That's what the keychain is for. Have a look at SSKeychain for a good wrapper class.
  2. The Key-value coding error you are getting suggests you are trying to reference your serviceData array as just "service" somewhere. Check your valueForKey and setValueForKey statements.

Can you post the rest of the Account class? That method setAccounts looks like it might be relevant.

Also is there a reason you are using Keyed Archiving instead of Core Data?

smyrgl
  • 864
  • 6
  • 12
  • I am a very beginner, I was not aware that there is such a thing as Core Data, and the setAccounts method just connects one account to the other via pointers, it doesn't actually copy it, but yes I will update it, and thankyou very much. – jundl77 Jan 25 '14 at 06:23
  • Also with the just "servie", wouldn't that entail a compile error?, because it does compile – jundl77 Jan 25 '14 at 06:36
  • No, the principle behind key-value coding is that it adds dynamic typing of sorts so you can set any value you like, even if the receiver doesn't exist. That's why you are getting that error--what is happening is that somewhere in your code there is something trying to either set or get a key on the ServiceData class with the key "service". It's almost certainly a typo in your code somewhere. – smyrgl Jan 25 '14 at 08:46
  • By the way, you are using some very strange techniques there that you really shouldn't be. Firstly you don't need to generate ivars like that when you have properties...if you want to make it so that they can only be set by the class in question then the right way to do that is to set them as readonly in the header file and then declare them again in an interface extension in the .m file but as readwrite. You also won't need to synthesize anymore as properties have auto-sythesized ivars you can access using _propertyName. – smyrgl Jan 25 '14 at 08:51
  • Oh I see, well thankyou very much! You are very helpfull, I will see if I can find the error. – jundl77 Jan 25 '14 at 15:13
  • Ok so turns out the key-identifier in my NSTable view wasnt updated to serviceData, and was instead still at service, so I can add service data objects again and that error message doesnt pop up anymore, but when I save it, the NSArchiver still messes up the content of my arrays :/ – jundl77 Jan 25 '14 at 17:32
  • Is it something in your "Crypt" class that could be causing the issue? Sounds like you may not be encrypting/decrypting correctly. However I still recommend you forget dealing with encryption directly and use the keychain...it can handle both a username/password and you just need to store the ref ID to disk to look it up with. – smyrgl Jan 25 '14 at 18:37
  • well the Crypt class works fine, as when I create a new account and login in with that, it works, its just that the previously made accounts that were saved to a file dont have the same encryption anymore after loading it back into the program. Also, with thankyou with the SSKeychain, I will try to implement it once this works haha, but for now I would like to know what is wrong right now, so that in the future when this happens, I know what to do if that makes sense! But thankyou, you've been very helpful so far!! – jundl77 Jan 25 '14 at 20:48