0

I had an app which worked perfect with Xcode 8. Now I'm preparing some minor update and faced with unexpected behavior of NSKeyedArchiver. Below the code which worked with no issues before updating to Xcode 9:

func saveChecklist(_ checklist : [SingleSection], name: String){
        let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
        let path: AnyObject = paths[0] as AnyObject
        let fileName = "/" + name + ".qad"
        let arrPath = path.appending(fileName)
        NSKeyedArchiver.setClassName("SingleSection", for: SingleSection.self)
        NSKeyedArchiver.archiveRootObject(checklist, toFile: arrPath)
    }

Now the last line provokes crash with error message:

libc++abi.dylib: terminating with uncaught exception of type NSException

I didn't change this part of code at all and I have no ideas what's going on. I believe that root of the problem is in class definition, but once again, everything was good before updating. Below is a class definition:

class SingleSection: NSCoder {

    var sectionName: String
    var itemsInSection: [String]
    var isChecked: [Bool]
    var itemsComment: [String]
    var finding: [Int]!
    var images: [[String]]!
    var videos: [[String]]!


    override init() {
        sectionName = ""
        itemsInSection = []
        isChecked = []
        itemsComment = []
        finding = []
        images = []
        videos = []
    }

    init (coder aDecoder: NSCoder!){
        self.sectionName = (aDecoder.decodeObject(forKey: "sectionName") as? String)!
        self.itemsInSection = (aDecoder.decodeObject(forKey: "itemsInSection") as? [String])!
        self.isChecked = (aDecoder.decodeObject(forKey: "isChecked") as? [Bool])!
        self.itemsComment = (aDecoder.decodeObject(forKey: "itemsComment") as? [String])!
        self.finding = aDecoder.decodeObject(forKey: "finding") as? [Int]
        self.images = aDecoder.decodeObject(forKey: "images") as? [[String]]
        self.videos = aDecoder.decodeObject(forKey: "videos") as? [[String]]
    }


    func encodeWithCoder(_ aCoder: NSCoder!){

        aCoder.encode(sectionName, forKey: "sectionName")
        aCoder.encode(itemsInSection, forKey: "itemsInSection")
        aCoder.encode(isChecked, forKey: "isChecked")
        aCoder.encode(itemsComment, forKey: "itemsComment")
        aCoder.encode(finding!, forKey: "finding")
        aCoder.encode(images!, forKey: "images")
        aCoder.encode(videos!, forKey: "videos")
    }

}

Main question is what became wrong after updating and how to workaround. Any help appreciated.

UPDATE: The problem's gone when I adopted NSCoding. Thanks for the comments

  • Why is the class a subclass of `NSCoder` rather than adopting `NSCoding`. The method signatures of `NSCoding` in Swift 3+ are wrong and you are using too many question and exclamation marks. Since you are going to decode **all** properties, it's guaranteed that the values are not `nil` and you can safely unwrap them. But if `images` and `videos` are supposed to be optionals use regular optionals (`?`) – vadian Oct 04 '17 at 11:25
  • @vadian, it was done so because of some "historical reasons". I agree that this code should be remade, but this is not an issue. The question is why it worked before and doesn't now. – Grigory Konovalov Oct 04 '17 at 11:38
  • I agree with @vadian, it's better to make you class conform to NSCoding rather than be a subclass of NSCoder. And the force unwrapping of the objects can be a problem here. However, in swift 4 there is a new much nice way of serializing obects, something to consider https://medium.com/monitisemea/swift-4s-codable-one-last-battle-for-serialization-30ceb3ccb051 – Au Ris Oct 04 '17 at 11:43
  • @AuRis Forced unwrapping is no problem at all if you (the developer) are encoding all values as non-optionals. – vadian Oct 04 '17 at 11:50
  • Problem's gone. I updated my initial question. Thanks for the comments. – Grigory Konovalov Oct 04 '17 at 11:51

0 Answers0