0

I know a number of downcasting from Cocoa to Swift questions have been asked and there are some bugs, but I have tried a lot of the methods found here and I cannot get them to work, hoping someone can lend a hand.

I am new to programming. I am making a database program for iOS in swift.

I have a person class:

class Person : NSObject {
    var firstName : String
    var lastName : String

    init (firstName : String, lastName : String) {
        self.firstName = firstName
        self.lastName = lastName
    }

    func encodeWithCoder(aCoder: NSCoder!) {
        aCoder.encodeObject(firstName, forKey:"firstName")
        aCoder.encodeObject(lastName, forKey:"lastName")
}

    init (coder aDecoder: NSCoder!) {
        self.firstName = aDecoder.decodeObjectForKey("firstName") as String
        self.lastName = aDecoder.decodeObjectForKey("lasName") as String
    }

}

I declare an array of the class at the top of my view controller:

    var peopleArray = [Person]()

I then fill the array by declaring some sample users and append it to the array:

    var nateB = Person(firstName: "Nate", lastName: "Birkholz")
    var nateC = Person(firstName: "Nate", lastName: "Carson")
    var nateD = Person(firstName: "Nate", lastName: "Donnelly")
    self.peopleArray.append(nateB)
    self.peopleArray.append(nateC)
    self.peopleArray.append(nateD)

I then try to save the data to a plist file:

let fileManager = (NSFileManager.defaultManager())
        let directorys : [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory,NSSearchPathDomainMask.AllDomainsMask, true) as? [String]

        println("value of directorys is \(directorys)")

        if (directorys != nil){
            let directories:[String] = directorys!;
            let pathToFile = directories[0]; //documents directory

            let plistfile = "PeopleArray.plist"
            let plistpath = pathToFile.stringByAppendingPathComponent(plistfile);

            if !fileManager.fileExistsAtPath(plistpath){  //writing Plist file

                self.createInitialPeople()

                println("Declaring cocoaArray")
                var cocoaArray : NSArray = [NSKeyedArchiver.archivedDataWithRootObject(peopleArray)]
                println("writing to path")
                cocoaArray.writeToFile(plistpath, atomically: true)
                let tellMe = cocoaArray.writeToFile(plistpath, atomically: true)
                println("Return of write is \(tellMe)")
            } 

A Plist file with inscrutable data is created.

I close the app and start it again, i then try to load the file:

         else {            
            println("\n\nPlist file found at \(plistpath)")

            let cocoaArray = NSMutableArray.arrayWithContentsOfFile(plistpath)
            peopleArray = cocoaArray as Array
        }
    }

And I fail because I cannot downcast "AnyObject is not identical to 'Person'. I have tried downcasting it in several ways I have found listed here on StackOverflow and just cannot do so successfully. This is really frustrating.

Update with the final code:

func createPeoplePlist() {

    let fileManager = (NSFileManager.defaultManager())
    let directorys : [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory,NSSearchPathDomainMask.AllDomainsMask, true) as? [String]

    println("value of directorys is \(directorys)")

    if (directorys != nil){
        let directories:[String] = directorys!;
        let pathToFile = directories[0]; //documents directory

        let plistfile = "PeopleArray.plist"
        plistpath = pathToFile.stringByAppendingPathComponent(plistfile);

        if !fileManager.fileExistsAtPath(plistpath){  //writing Plist file

            self.createInitialPeople()

            println("Saving to Plist")

            [NSKeyedArchiver.archiveRootObject(peopleArray, toFile: plistpath)]

            println("writing to path \(plistpath)")


        } else {            //Reading Plist file
            println("\n\nPlist file found at \(plistpath)")

            peopleArray = NSKeyedUnarchiver.unarchiveObjectWithFile(plistpath) as [Person]



        }
    }


}
Nate Birkholz
  • 2,739
  • 4
  • 20
  • 29
  • How does this differ from [your other question](http://stackoverflow.com/questions/25345076/ios-swift-saving-an-array-of-custom-classes)? While it is a well-written question, it seems to be largely the same. – jtbandes Aug 18 '14 at 05:27
  • @jtbandes The other question was about the file not saving. It now saves, but the data can't be read back in. The person who answered that question recommended I ask a new question for the new problem I am having. – Nate Birkholz Aug 18 '14 at 05:36
  • I'm not up for writing a full answer now, but your array still contains `NSData` when you read it back in. However, I would question why you are even putting the data in another array before writing to disk. There's a unnecessary layer here. You should simply write the archived data directly to a file, then read the file and unarchive it the same way. – jtbandes Aug 18 '14 at 05:40
  • In truth, you should read the [Archives and Serializations Programming Guide](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Archiving/Archiving.html#//apple_ref/doc/uid/10000047-SW1) and [Property List Programming Guide](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PropertyLists/Introduction/Introduction.html#//apple_ref/doc/uid/10000048i). – jtbandes Aug 18 '14 at 05:42
  • Thanks. Well, I am doing it this way based on advice and lots of reading tutorials. I want to use the Swift variables and collections and Swift functions and archiving/serializing seems to require the use of the Cocoa object types. And I'm afraid I do not yet have enough context or knowledge to interpret those guides for Swift. Thank you, however, for the input. – Nate Birkholz Aug 18 '14 at 05:53
  • On the contrary, I think [Encoding and Decoding Objects](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Archiving/Articles/codingobjects.html#//apple_ref/doc/uid/20000948-BCIHBJDE) will be (relatively) intelligible even with no ObjC knowledge; you've basically done it already. For the rest you can just browse the [NSKeyedArchiver](https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Classes/NSKeyedArchiver_Class/index.html) documentation which includes Swift snippets. You should probably use `archiveRootObject(_:toFile:)` directly. – jtbandes Aug 18 '14 at 05:58
  • 1
    Namely, `peopleArray` itself can be the root object. There is no need for an extra array wrapping it. Then you can simply use `archiveRootObject(_:toFile:)` and `unarchiveObjectWithFile(_:)`. – jtbandes Aug 18 '14 at 06:00
  • @jtbandes That worked great, thanks. Writing to file is now [NSKeyedArchiver.archiveRootObject(peopleArray, toFile: plistpath)] and loading is peopleArray = NSKeyedUnarchiver.unarchiveObjectWithFile(plistpath) as [Person]. Uh, any way i can mark it as answered? I appreciate the help. – Nate Birkholz Aug 18 '14 at 06:33
  • Guess it's time to post a real answer... – jtbandes Aug 18 '14 at 06:36

1 Answers1

1

Your array still contains NSData when you read it back in (the data you created when archiving it in the first place). However, I would question why you are even putting the data in another array before writing to disk. peopleArray itself can be the root object; there is no need for an extra array wrapping it. Then you can simply use archiveRootObject(_:toFile:) and unarchiveObjectWithFile(_:).

I would also recommend reading the Archives and Serializations Programming Guide and Property List Programming Guide, and the NSKeyedArchiver docs, for further information.

jtbandes
  • 115,675
  • 35
  • 233
  • 266