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

No the array is always empty. My code is in SWIFT.

I was replying to your point : error on first line.


But if you keep objC code, you'll get error on 2nd, 3rd and 4th line as well in PBK objC code !


My first reply on Sep 7, 2018 11:54 AM was trying to answer your original post. But the discussion has gone in other directions, seemingly forgetting this original question.

Oh now I understand. You're right I get errors on the other lines. I want to keep the SWIFT code. I'm was trying it both ways. Thanks for responding. I guess I should concentrate on the original way where the array is empty. Anymore help would be appreciated.

Read again my initial answer for encoding and decoding and tell if there is a problem.

Yes there is a problem.

let theURL = getDocumentsDirectory().appendingPathComponent(randomFilename)

if let data = NSMutableData(contentsOf: theURL) {

let unarchiver = NSKeyedUnarchiver(forReadingWith: (data as Data))

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


it doesn't take the first if

if let data = NSMutableData(contentsOf: theURL). It contains nil.

The array with data is always backed up first. NSMutableData(contentsOf: theURL) usually contains nil. Does that mean the write was unsuccessful? Once in a while it returns the data. So the array is not always empty.

You have to check theUrl.


How did you define it ?

I save the array with this:

randomFilename = UUID().uuidString

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)


I read it back with this:


randomFilename = UUID().uuidString

let theURL = getDocumentsDirectory().appendingPathComponent(randomFilename)

if let data = NSMutableData(contentsOf: theURL) {

let unarchiver = NSKeyedUnarchiver(forReadingWith: (data as Data))

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

unarchiver.finishDecoding()

}

}

I get this warning:


Value 'tarray' was defined but never used; consider replacing with boolean

Thanks again for your help.

As I've written, I don't do Swift but consider the meaning of this:


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


Because you use "let" the compiler creates a whole new array "tarray" and sets that equal to the unarchiver object. Then you never use that new tarray. That is why you get back the warning "was defined but never used". What you want to do is use your established global tarray by removing that "let".


Then think about the structure based on the "if tarray". What is the purpose? It covers the possibility that the decoding will fail for one of a bunch of reasons. Use that by replacing the "}" with:

   }else{
       //  initialize tarray
   }


This will allow you to 'read' the archived file the first time you run the program - there will be no file, the decoder will create a 'nil' object and the "if" will default to }else{



By the way - Objective C is much easier to code, read and debug. Apple may disagree - but they are wrong, wrong, wrong.

Thanks for your response. The problem is data is equal to nil. It never gets to the tarray = statement.


randomFilename = UUID().uuidString

let theURL = getDocumentsDirectory().appendingPathComponent(randomFilename)

if let data = NSMutableData(contentsOf: theURL) {

let unarchiver = NSKeyedUnarchiver(forReadingWith: (data as Data))

tarray = (unarchiver.decodeObject(forKey: "starray") as? [Float])!

unarchiver.finishDecoding()

}

If your value for "success" is YES then you correctly wrote the file. Is it???


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

I save the array with this:

       randomFilename = UUID().uuidString
        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)

I read it back with this:


            randomFilename = UUID().uuidString
            let theURL = getDocumentsDirectory().appendingPathComponent(randomFilename)
              if let data = NSMutableData(contentsOf: theURL) {
              let unarchiver = NSKeyedUnarchiver(forReadingWith: (data as Data))
               if let tarray = unarchiver.decodeObject(forKey: "starray") as? [Float]  {
              unarchiver.finishDecoding()
               }
            }


I get this warning:

Value 'tarray' was defined but never used; consider replacing with boolean


Effectively, you never use the tArray you declare with let elsewhere in your code


So, you should write

               if let tempArray = unarchiver.decodeObject(forKey: "starray") as? [Float]  {
                    tarray = tempArray
                   unarchiver.finishDecoding()
               }



In addition, to check everything works correctly, add some print and check what you get on console:


       randomFilename = UUID().uuidString
        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) {
                  print("data", data)
                   let unarchiver = NSKeyedUnarchiver(forReadingWith: (data as Data))
                    if let tarray = unarchiver.decodeObject(forKey: "starray") as? [Float]  {
                         tarray = tempArray 
                         print("tArray", tarray)
                         unarchiver.finishDecoding()
                    }
            }

Yes success is equal to TRUE so I successfully wrote the file. If I put the read right after the write it works. I have the write in the AppDelegate. The read is in the view controller. in the view controller everything is nil.

I'm not sure what :


randomFilename = UUID().uuidString


does. I copied it from somewhere. It seems to be working.

Should test the value of uuid each time you use it, and see it is not the same!


And that's normal :


UUID

A universally unique value that can be used to identify types, interfaces, and other items.

Creating UUIDs

init()

Initializes a new UUID with RFC 4122 version 4 random bytes.

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

NSKeyArchiver
 
 
Q