NSKeyArchiver

How do I use NSKeyArchiver and NSKeyunArchiver to backup and restore an array of floats? Is there any sample code anywhere?

Answered by Claude31 in 331127022

I only knew there was a problem because I couldn't read the second file back.

So what is the situation ?


- do you write both files and get succes and success2 true ?

- Can you now read both files or not ?


Note: it normal you got it true even when you overwrote the first file.


Why don't you have a symmetric hanling for reading both ? The second is inside the if let of the first.

I would better write like this (and instrument the code to check what's going on.

Thanks to report result:


        let theURL = getDocumentsDirectory().appendingPathComponent("tarfile")
        if let data = NSMutableData(contentsOf: theURL) {
            print("data read on", theURL)
            let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)
            if let readArray = unarchiver.decodeObject(forKey: "starray") as? [Float]  {
                unarchiver.finishDecoding()
                tarray = readArray
                print("tarray read ", tarray)
            }
        }               // MOVE CLOSING CURLY BRACE
       
        let theURL2 = getDocumentsDirectory().appendingPathComponent("tipfile")
        if let data2 = NSMutableData(contentsOf: theURL2) {
            print("data2 read on", theURL2)
            let unarchiver2 = NSKeyedUnarchiver(forReadingWith: data2 as Data)
            if let readArray2 = unarchiver2.decodeObject(forKey: "stipray") as? [Float]  {
               unarchiver2.finishDecoding()
                tipray = readArray2
                print("tipray read ", tipray)
            }
        }

Just encode the array:


    let data = NSMutableData()
    let archiver = NSKeyedArchiver(forWritingWith: data)
    archiver.encode(myArray, forKey: "myArray")
    archiver.finishEncoding()

and then write to the file


     let success =  data.write(to: theURL, atomically: true)


To read back


    if let data = NSMutableData(contentsOf: theURL) {
        let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)
        if let readArray = unarchiver.decodeObject(forKey: "myArray") as? [Float]  { 
            unarchiver.finishDecoding()
// Do what you need of myArray
          }

A note - NSKeyedArchiver is being modified in iOS12. These methods are 'deprecated' - that means sometime in the next 3 (?) years you will need to modify the code in order for it to run on iOS13 (?) or iOS14 (?). Unfortunately the undeprecated commands do not work in iOS11 so the deprecated commands will have to do for now (i.e. iOS11).


In Objective C:

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
    NSString *dataPath = [rootPath stringByAppendingPathComponent:@"myDatafile"];
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myArray];
    [data writeToFile:dataPath atomically:YES];
         // other stuff
}




- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    myArray=[[NSMutableArray alloc] init];
    NSString *dataPath;
    NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
    dataPath = [rootPath stringByAppendingPathComponent:@"myDatafile"];
    if ([[NSFileManager defaultManager] fileExistsAtPath:dataPath]) {
        NSData *data = [[NSFileManager defaultManager] contentsAtPath:dataPath];
        [myArray addObjectsFromArray:[NSKeyedUnarchiver unarchiveObjectWithData:data]];
    }else{
        //  initialize myArray with default values
    }
    // other stuff
}

Thank you very much for your help. Is it correct readArray should be equal to myArray after " if let readArray statement"?

myarray has data but readArray is always empty. What am I doing wrong? Please help. Thank you again.

Thank you very much for your help. I'm getting an error on the first line. it says ! Expected declaration. Please help. Thanks again.

ReadArray should not be empty after unarchiving. And at this stage, myArray only contains what was entered in it before.


After if readArray, nothing has changed yet for myArray.


you need to assign

myArray = readArray.

I just noticed those deprecation after IOS 12.


But I was not able to find what are the new API. Is it to use codable instead ?

The new API is only in beta-iOS12 and only available using the beta version of Xcode. I think deprecating these commands in iOS11 rather than waiting to deprecate them in non-beta-iOS12 and a non-beta Xcode was an error.


I wrote above:

"Unfortunately the undeprecated commands do not work in iOS11 so the deprecated commands will have to do for now (i.e. iOS11)."

It should have been:

"Unfortunately the undeprecated new commands do not work in iOS11 so the deprecated commands will have to do for now (i.e. iOS11).

You wrote: " I'm getting an error on the first line. it says ! Expected declaration."


Are you referring to this line:

- (void)applicationDidEnterBackground:(UIApplication *)application { 


If not, please explain 'first line'.

If so, you want to read and write the file at an appropriate time. I was expecting that you would put this code into the AppDelegate and read the file on launch and write the file whenever the app leaves foreground. The application calls this method "applicationDidEnterBackground" (in ObjectiveC codding) whenever it leaves the foreground so that is a good time to store your array. But if you want to put it elsewhere in your application (i.e. not in the AppDelegate) you can do that. Also, the read stuff was placed in the method of the AppDelegate didFinishLaunchingWithOptions: so it would be done on launch - again, you can put that wherever you want.

readArray is empty. myArray has data. It looks like MyArray gets archived but when it gets unarchived to readArray it is empty. After that assignment( myArray = readArray) both arrays are empty. Please advise. Thanks again for your help.

yes that's the line I'm referring to. I moved it to AppDelegate. I'm still getting the same error. Please help. Thanks again.

Is your code in objC or Swift ?

I’m using Xcode with swift .

Hence the error, you need to write this in Swift:


    func applicationDidEnterBackground(_ application: UIApplication) {
          // The proposed code here, in Swift
}

Did my previous proposal work ?

Did you repond to the wrong question? I was talking to you about the empty array. Anyway if I use func aplication instead of void I get rid of the error on the first line however I get 4 more errors. I get three of these on the first 3 lines '*' is not a prefix unary operator referring to *rootPath, *datapath and *data. Thanks again for all your help.

As noted in my first post above, this is code in Objective C, not Swift. If you want to use it in a Swift class you will need to translate into Swift.

NSKeyArchiver
 
 
Q