Archiving objects within objects in objective-c

416 views Asked by At

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];
1

There are 1 answers

8
smyrgl On

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?