SirEnder
SirEnder

Reputation: 573

Error archiving CMTime with NSKeyedArchiver in swift4.0 in ios9.3

The following code archivedTimes() builds successfully in swift4. And it runs fine on a device with ios10.3 installed.

typealias Time = CMTime
typealias Times = [Time]

static let times: Times = Array<Int64>.init(1...9).map({ CMTime.init(value: $0, timescale: 100) })

static func archivedTimes() -> Data {
    return archived(times: times)
}

static func archived(times: Times) -> Data {
    let values = times.map({ NSValue.init(time: $0) })
    return NSKeyedArchiver.archivedData(withRootObject: values) // ERROR here

    // -- ideally would instead be:
    // return NSKeyedArchiver.archivedData(withRootObject: times) 
    // -- but probably not compatible with ios 9.3
}

However, while running it on a device with ios9.3 installed, it crashes saying:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs'

My guess is that it may have something to do with some conflict between the new Codable protocol and the old NSCoder protocol. But I don't know what!

Note that the issue has nothing to do with the array. As archiving a simple CMTime also leads to such error. However, I posted it like this, because archiving the array of CMTime is ultimately my objective.

Upvotes: 0

Views: 261

Answers (1)

SirEnder
SirEnder

Reputation: 573

I believe Codable protocol is only available in ios10, therefore on ios9, CMTime does not implement Codable.

So for ios9, I went with a wrapper class for a CMTime, which implements the NSCoding protocol.

This can be done by importing AVFoundation which declares both the extension to NSValue and to NSCoder so as to encode CMTime.

So then I went with an array of WrappedTime.init($0), instead of an array of NSValue.init(time: $0).

class WrappedTime: NSObject, NSCoding {

    enum EncodeKey: String {
        case time = "time"
    }

    let time: CMTime

    // ...

    func encode(with aCoder: NSCoder) {
        aCoder.encode(time, forKey: EncodeKey.time.rawValue)
    }

    required init?(coder aDecoder: NSCoder) {
        time = aDecoder.decodeTime(forKey: EncodeKey.time.rawValue)
    }

    init(_ time: Time) {
        self.time = time
    }
}

Upvotes: 0

Related Questions