I have the following code, used to get the path of an object that has been archived
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let path = paths[0] as String
let archivePath = path.stringByAppendingString("archivePath")
When I run this code, it crashes at the NSSearchPathForDirectoriesInDomains
call with lldb showing
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
In Xcode's Variables View I see the path String set as I would expect. What is the proper way to get a user directory in Swift for archiving/unarchiving objects?
Update:
It appears this is actually crashing on my use of the NSKeyedUnarchiver:
stopwatches = NSKeyedUnarchiver.unarchiveObjectWithFile(archivePath) as Stopwatch []
Stopwatch is a class that implements NSCoding, stopwatches is the datasource (an array of Stopwatches) owned by the view doing the unarchiving.
Update 2:
The object graph being archived is an array of Stopwatches. NSCoding is implemented as follows:
func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeBool(self.started, forKey: "started")
aCoder.encodeBool(self.paused, forKey: "paused")
aCoder.encodeObject(self.startTime, forKey: "startTime")
aCoder.encodeObject(self.pauseTime, forKey: "pauseTime")
aCoder.encodeInteger(self.id, forKey: "id")
}
init(coder aDecoder: NSCoder!) {
self.started = aDecoder.decodeBoolForKey("started")
self.paused = aDecoder.decodeBoolForKey("paused")
self.startTime = aDecoder.decodeObjectForKey("startTime") as NSDate
self.pauseTime = aDecoder.decodeObjectForKey("pauseTime") as NSDate
self.id = aDecoder.decodeIntegerForKey("id")
super.init()
}
Update 3:
With expandTilde
set to true my path is /Users/Justin/Library/Developer/CoreSimulator/Devices/FF808CCD-709F-408D-9416-EE47B306309D/data/Containers/Data/Application/B39CCB84-F335-4B70-B732-5C3C26B4F6AC/Documents/ArchivePath
If I set expandTilde
to false, I don't get the crash, but the file is not archived and unarchived, and the path is @"~/Documents/ArchivePath"
Deleting the Application folder causes the first launch of the application to not crash, but does not allow it to reopen afterwards. Also, after deleting the application folder, I am now able to read the archive path in lldb rather than having to println it.
I experienced a similar problem and it had to do with Swift's name mangling. If you're reading from a file that was generated from encoded Objective-C objects and each object had the Objective-C class name Stopwatch
, you won't be able to directly decode that into Swift objects, because Swift performs name mangling on class and symbol names. What you see as Stopwatch
in Swift code is internally something like T23323234_Stopwatch_3242
.
To get around it, specify that your Swift class should be exported with a specific Objective-C class name, e.g.:
@objc(Stopwatch) class Stopwatch {
...
}
where Stopwatch
matches the class name of the Objective-C objects you've archived.
Edit: In the project code you linked in the comments, there's a second problem here, i.e. trying to encode/decode Swift-specific features. Only Objective-C objects can be archived and unarchived. Sadly, structs, generics, tuples and optionals are not Objective-C compatible and can't work with NSCoding
. The best way to work around this is to encode Swift types as NSDictionary
instances, e.g. (untested):
let encodedLaps = self.laps.map { lap => ["start": lap.start, "end": lap.end] }
aCoder.encodeObject(encodedLaps, forKey: "laps")
Edit 2: Contrary to what I wrote above, this isn't limited to just reading Objective-C instances from Swift. It appears that any usage of NSCoding
requires an Objective-C name for your class. I would imagine this is because Swift name-mangling can change between different runs of the compiler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With