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

> You wrote: UUID is completely different in the read and write. Is that OK? That would make the filename different.

> I wrote: If so, ask yourself - why can't you read the file? Are you correctly specifying the same filename? What is the meaning of:

randomFilename = UUID().uuidString


Can you answer your own question ....."Is that OK?"

Yes it is different, because it is randomly generated !


Set a filename differently, keep track of it to reuse, and everything will work.

OK I got the filename right now. The problem is this line:

if let data = NSMutableData(contentsOf: theURL)

It always contains nil even though theURL contains the correct directory and filename.

show your code.

let theURL = getDocumentsDirectory().appendingPathComponent(randomFilename)

let data = NSMutableData()

let archiver = NSKeyedArchiver(forWritingWith: data)

archiver.encode(tarray, forKey: "starray")

archiver.finishEncoding()

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

print("success \(success)")


randomFilename = UUID().uuidString

let theURL = getDocumentsDirectory().appendingPathComponent(randomFilename)

if let data = NSMutableData(contentsOf: theURL) {

let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)

if let readArray = unarchiver.decodeObject(forKey: "starray") as? [Float] {

unarchiver.finishDecoding()

tarray = readArray

}

How does that work? Doesn't line 01 have to be taken out? They will have different filenames.

The code above has the problem pointed out in earlier posts by me and Claude31; you want to use the same filename for writing and reading the file. Each time you do a UUID().uuidString you generate a new unique name - in your case you generate a new filename each time you go to read (!!!) the file. There are two ways to solve this:


1) define the filename once before storing (not reading) the file and use it again when you try to read (not store) the file.

move: randomFilename = UUID().uuidString

to the first line


2) just use a standard name - after all why do you need a random filename?

In two places:

replace: let theURL = getDocumentsDirectory().appendingPathComponent(randomFilename)

with: let theURL = getDocumentsDirectory().appendingPathComponent("myDatafile")

and delete:

randomFilename = UUID().uuidString

Yes, you should completely change the way you handle filename when you read it back. You need to make sure that you reuse the file name used for creating the file.


The purpose of my post was to instrument the code to see that the names did not match, it was not the solution to the problem but a way to find it out.


It seems you reply to a post without reading the more recent one (Sep 12, 2018 9:59 AM), so that makes the discussion a bit hectic.

Hey I finally I got it going. The app is working great. I want to thank both you guys for your help. I couldn't have done it without you.

Thanks again.

Great,

in a word, what did you change ?


And don't forget to close the (long) thread.

Good luck for continuation.

I had typos. I was backing up 2 files. I wrote the files with this:


let theURL = getDocumentsDirectory().appendingPathComponent("tarfile")

let data = NSMutableData()

let archiver = NSKeyedArchiver(forWritingWith: data)

archiver.encode(tarray, forKey: "starray")

archiver.finishEncoding()

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

let theURL2 = getDocumentsDirectory().appendingPathComponent("tipfile")

let data2 = NSMutableData()

let archiver2 = NSKeyedArchiver(forWritingWith: data2)

archiver2.encode(tipray, forKey: "stipray")

archiver2.finishEncoding()

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


I read them with this:


let theURL = getDocumentsDirectory().appendingPathComponent("tarfile")

if let data = NSMutableData(contentsOf: theURL) {

let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)

if let readArray = unarchiver.decodeObject(forKey: "starray") as? [Float] {

unarchiver.finishDecoding()

tarray = readArray

}

let theURL2 = getDocumentsDirectory().appendingPathComponent("tipfile")

if let data2 = NSMutableData(contentsOf: theURL2) {

let unarchiver2 = NSKeyedUnarchiver(forReadingWith: data2 as Data)

if let readArray2 = unarchiver2.decodeObject(forKey: "stipray") as? [Float] {

unarchiver2.finishDecoding()

tipray = readArray2

}

}

}

Do you see the problem? The original problem was the filename.

Thanks again for all your help.

I think there is an error.


You write

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


That will write over the first file.


I think you mean

        let success2 = data.write(to: theURL2, atomically: true)


BTW, you should have got an alert that theURL2 was written but never used…


If all works OK after this, don't forget to close the thread by marking the correct answer.

Very good but there is still a mistake. When I saw that I thought that was the problem. I got SUCCESS = TRUE on both writes but the second write wasn't successful. It never wrote over the first file. There was never a warning or an alert. I only knew there was a problem because I couldn't read the second file back. Let me know if you can see the other typo.

You were writing both files to theURL but you were then trying to read one file from theURL and the other file from theURL2.

Accepted Answer

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)
            }
        }
NSKeyArchiver
 
 
Q