blob: f9b473b36887fee27a3005f9eaf2fdb9862ca410 [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
import CoreFoundation
// Archives created using the class method archivedRootDataWithObject used this key for the root object in the hierarchy of encoded objects. The NSKeyedUnarchiver class method unarchiveObjectWithData: will look for this root key as well. You can also use it as the key for the root object in your own archives.
public let NSKeyedArchiveRootObjectKey: String = "root"
internal let NSKeyedArchiveNullObjectReference = _NSKeyedArchiverUID(value: 0)
internal let NSKeyedArchiveNullObjectReferenceName: String = "$null"
internal let NSKeyedArchivePlistVersion = 100000
internal let NSKeyedArchiverSystemVersion : UInt32 = 2000
internal func escapeArchiverKey(_ key: String) -> String {
if key.hasPrefix("$") {
return "$" + key
} else {
return key
}
}
internal let NSPropertyListClasses : [AnyClass] = [
NSArray.self,
NSDictionary.self,
NSString.self,
NSData.self,
NSDate.self,
NSNumber.self
]
open class NSKeyedArchiver : NSCoder {
struct ArchiverFlags : OptionSet {
let rawValue : UInt
init(rawValue : UInt) {
self.rawValue = rawValue
}
static let none = ArchiverFlags(rawValue: 0)
static let finishedEncoding = ArchiverFlags(rawValue : 1)
static let requiresSecureCoding = ArchiverFlags(rawValue: 2)
}
private class EncodingContext {
// the object container that is being encoded
var dict = Dictionary<String, Any>()
// the index used for non-keyed objects (encodeObject: vs encodeObject:forKey:)
var genericKey : UInt = 0
}
private static var _classNameMap = Dictionary<String, String>()
private static var _classNameMapLock = NSLock()
private var _stream : AnyObject
private var _flags = ArchiverFlags(rawValue: 0)
private var _containers : Array<EncodingContext> = [EncodingContext()]
private var _objects : Array<Any> = [NSKeyedArchiveNullObjectReferenceName]
private var _objRefMap : Dictionary<AnyHashable, UInt32> = [:]
private var _replacementMap : Dictionary<AnyHashable, Any> = [:]
private var _classNameMap : Dictionary<String, String> = [:]
private var _classes : Dictionary<String, _NSKeyedArchiverUID> = [:]
private var _cache : Array<_NSKeyedArchiverUID> = []
open weak var delegate: NSKeyedArchiverDelegate?
open var outputFormat = PropertyListSerialization.PropertyListFormat.binary {
willSet {
if outputFormat != PropertyListSerialization.PropertyListFormat.xml &&
outputFormat != PropertyListSerialization.PropertyListFormat.binary {
NSUnimplemented()
}
}
}
open class func archivedData(withRootObject rootObject: Any) -> Data {
let data = NSMutableData()
let keyedArchiver = NSKeyedArchiver(forWritingWith: data)
keyedArchiver.encode(rootObject, forKey: NSKeyedArchiveRootObjectKey)
keyedArchiver.finishEncoding()
return data._swiftObject
}
open class func archiveRootObject(_ rootObject: Any, toFile path: String) -> Bool {
var fd : Int32 = -1
var auxFilePath : String
var finishedEncoding : Bool = false
do {
(fd, auxFilePath) = try _NSCreateTemporaryFile(path)
} catch _ {
return false
}
defer {
do {
if finishedEncoding {
try _NSCleanupTemporaryFile(auxFilePath, path)
} else {
try FileManager.default.removeItem(atPath: auxFilePath)
}
} catch _ {
}
}
let writeStream = _CFWriteStreamCreateFromFileDescriptor(kCFAllocatorSystemDefault, fd)!
if !CFWriteStreamOpen(writeStream) {
return false
}
let keyedArchiver = NSKeyedArchiver(output: writeStream)
keyedArchiver.encode(rootObject, forKey: NSKeyedArchiveRootObjectKey)
keyedArchiver.finishEncoding()
finishedEncoding = keyedArchiver._flags.contains(ArchiverFlags.finishedEncoding)
CFWriteStreamClose(writeStream)
return finishedEncoding
}
public override init() {
NSUnimplemented()
}
private init(output: AnyObject) {
self._stream = output
super.init()
}
public convenience init(forWritingWith data: NSMutableData) {
self.init(output: data)
}
private func _writeXMLData(_ plist : NSDictionary) -> Bool {
var success = false
if let data = self._stream as? NSMutableData {
let xml : CFData?
xml = _CFPropertyListCreateXMLDataWithExtras(kCFAllocatorSystemDefault, plist)
if let unwrappedXml = xml {
data.append(unwrappedXml._swiftObject)
success = true
}
} else {
success = CFPropertyListWrite(plist, self._stream as! CFWriteStream,
kCFPropertyListXMLFormat_v1_0, 0, nil) > 0
}
return success
}
private func _writeBinaryData(_ plist : NSDictionary) -> Bool {
return __CFBinaryPlistWriteToStream(plist, self._stream) > 0
}
/// If encoding has not yet finished, then invoking this property will call finishEncoding and return the data. If you initialized the keyed archiver with a specific mutable data instance, then it will be returned from this property after finishEncoding is called.
open var encodedData: Data {
NSUnimplemented()
}
open func finishEncoding() {
if _flags.contains(ArchiverFlags.finishedEncoding) {
return
}
var plist = Dictionary<String, Any>()
var success : Bool
plist["$archiver"] = NSStringFromClass(type(of: self))
plist["$version"] = NSKeyedArchivePlistVersion
plist["$objects"] = self._objects
plist["$top"] = self._containers[0].dict
if let unwrappedDelegate = self.delegate {
unwrappedDelegate.archiverWillFinish(self)
}
let nsPlist = plist._bridgeToObjectiveC()
if self.outputFormat == PropertyListSerialization.PropertyListFormat.xml {
success = _writeXMLData(nsPlist)
} else {
success = _writeBinaryData(nsPlist)
}
if let unwrappedDelegate = self.delegate {
unwrappedDelegate.archiverDidFinish(self)
}
if success {
let _ = self._flags.insert(ArchiverFlags.finishedEncoding)
}
}
open class func setClassName(_ codedName: String?, for cls: AnyClass) {
let clsName = String(describing: type(of: cls))
_classNameMapLock.synchronized {
_classNameMap[clsName] = codedName
}
}
open func setClassName(_ codedName: String?, for cls: AnyClass) {
let clsName = String(describing: type(of: cls))
_classNameMap[clsName] = codedName
}
open override var systemVersion: UInt32 {
return NSKeyedArchiverSystemVersion
}
open override var allowsKeyedCoding: Bool {
return true
}
private func _validateStillEncoding() -> Bool {
if self._flags.contains(ArchiverFlags.finishedEncoding) {
fatalError("Encoder already finished")
}
return true
}
private class func _supportsSecureCoding(_ objv : Any?) -> Bool {
var supportsSecureCoding : Bool = false
if let secureCodable = objv as? NSSecureCoding {
supportsSecureCoding = type(of: secureCodable).supportsSecureCoding
}
return supportsSecureCoding
}
private func _validateObjectSupportsSecureCoding(_ objv : Any?) {
if let objv = objv, self.requiresSecureCoding &&
!NSKeyedArchiver._supportsSecureCoding(objv) {
fatalError("Secure coding required when encoding \(objv)")
}
}
private func _createObjectRefCached(_ uid : UInt32) -> _NSKeyedArchiverUID {
if uid == 0 {
return NSKeyedArchiveNullObjectReference
} else if Int(uid) <= self._cache.count {
return self._cache[Int(uid) - 1]
} else {
let objectRef = _NSKeyedArchiverUID(value: uid)
self._cache.insert(objectRef, at: Int(uid) - 1)
return objectRef
}
}
/**
Return a new object identifier, freshly allocated if need be. A placeholder null
object is associated with the reference.
*/
private func _referenceObject(_ objv: Any?, conditional: Bool = false) -> _NSKeyedArchiverUID? {
var uid : UInt32?
if objv == nil {
return NSKeyedArchiveNullObjectReference
}
let value = _SwiftValue.store(objv)!
uid = self._objRefMap[value]
if uid == nil {
if conditional {
return nil // object has not been unconditionally encoded
}
uid = UInt32(self._objects.count)
self._objRefMap[value] = uid
self._objects.insert(NSKeyedArchiveNullObjectReferenceName, at: Int(uid!))
}
return _createObjectRefCached(uid!)
}
/**
Returns true if the object has already been encoded.
*/
private func _haveVisited(_ objv: Any?) -> Bool {
if objv == nil {
return true // always have a null reference
} else {
return self._objRefMap[_SwiftValue.store(objv)] != nil
}
}
/**
Get or create an object reference, and associate the object.
*/
private func _addObject(_ objv: Any?) -> _NSKeyedArchiverUID? {
let haveVisited = _haveVisited(objv)
let objectRef = _referenceObject(objv)
if !haveVisited {
_setObject(objv!, forReference: objectRef!)
}
return objectRef
}
private func _pushEncodingContext(_ encodingContext: EncodingContext) {
self._containers.append(encodingContext)
}
private func _popEncodingContext() {
self._containers.removeLast()
}
private var _currentEncodingContext : EncodingContext {
return self._containers.last!
}
/**
Associate an encoded object or reference with a key in the current encoding context
*/
private func _setObjectInCurrentEncodingContext(_ object : Any?, forKey key: String? = nil, escape: Bool = true) {
let encodingContext = self._containers.last!
var encodingKey : String
if key != nil {
if escape {
encodingKey = escapeArchiverKey(key!)
} else {
encodingKey = key!
}
} else {
encodingKey = _nextGenericKey()
}
if encodingContext.dict[encodingKey] != nil {
NSLog("*** NSKeyedArchiver warning: replacing existing value for key '\(encodingKey)'; probable duplication of encoding keys in class hierarchy")
}
encodingContext.dict[encodingKey] = object
}
/**
The generic key is used for objects that are encoded without a key. It is a per-encoding
context monotonically increasing integer prefixed with "$".
*/
private func _nextGenericKey() -> String {
let key = "$" + String(_currentEncodingContext.genericKey)
_currentEncodingContext.genericKey += 1
return key
}
/**
Update replacement object mapping
*/
private func replaceObject(_ object: Any, withObject replacement: Any?) {
if let unwrappedDelegate = self.delegate {
unwrappedDelegate.archiver(self, willReplace: object, with: replacement)
}
self._replacementMap[_SwiftValue.store(object)] = replacement
}
/**
Returns true if the type cannot be encoded directly (i.e. is a container type)
*/
private func _isContainer(_ objv: Any?) -> Bool {
// Note that we check for class equality rather than membership, because
// their mutable subclasses are as object references
guard let obj = objv else { return false }
if obj is String { return false }
guard let nsObject = obj as? NSObject else { return true }
return !(type(of: nsObject) === NSString.self || type(of: nsObject) === NSNumber.self || type(of: nsObject) === NSData.self)
}
/**
Associates an object with an existing reference
*/
private func _setObject(_ objv: Any, forReference reference : _NSKeyedArchiverUID) {
let index = Int(reference.value)
self._objects[index] = objv
}
/**
Returns a dictionary describing class metadata for a class
*/
private func _classDictionary(_ clsv: AnyClass) -> Dictionary<String, Any> {
func _classNameForClass(_ clsv: AnyClass) -> String? {
var className : String?
className = classNameForClass(clsv)
if className == nil {
className = NSKeyedArchiver.classNameForClass(clsv)
}
return className
}
var classDict : [String:Any] = [:]
let className = NSStringFromClass(clsv)
let mappedClassName = _classNameForClass(clsv)
if mappedClassName != nil && mappedClassName != className {
// If we have a mapped class name, OS X only encodes the mapped name
classDict["$classname"] = mappedClassName
} else {
var classChain : [String] = []
var classIter : AnyClass? = clsv
classDict["$classname"] = className
repeat {
classChain.append(NSStringFromClass(classIter!))
classIter = _getSuperclass(classIter!)
} while classIter != nil
classDict["$classes"] = classChain
if let ns = clsv as? NSObject.Type {
let classHints = ns.classFallbacksForKeyedArchiver()
if classHints.count > 0 {
classDict["$classhints"] = classHints
}
}
}
return classDict
}
/**
Return an object reference for a class
Because _classDictionary() returns a dictionary by value, and every
time we bridge to NSDictionary we get a new object (the hash code is
different), we maintain a private mapping between class name and
object reference to avoid redundantly encoding class metadata
*/
private func _classReference(_ clsv: AnyClass) -> _NSKeyedArchiverUID? {
let className = NSStringFromClass(clsv)
var classRef = self._classes[className] // keyed by actual class name
if classRef == nil {
let classDict = _classDictionary(clsv)
classRef = _addObject(classDict._bridgeToObjectiveC())
if let unwrappedClassRef = classRef {
self._classes[className] = unwrappedClassRef
}
}
return classRef
}
/**
Return the object replacing another object (if any)
*/
private func _replacementObject(_ object: Any?) -> Any? {
var objectToEncode : Any? = nil // object to encode after substitution
// nil cannot be mapped
if object == nil {
return nil
}
// check replacement cache
objectToEncode = self._replacementMap[object as! AnyHashable]
if objectToEncode != nil {
return objectToEncode
}
// object replaced by NSObject.replacementObjectForKeyedArchiver
// if it is replaced with nil, it cannot be further replaced
if let ns = objectToEncode as? NSObject {
objectToEncode = ns.replacementObjectForKeyedArchiver(self)
if objectToEncode == nil {
replaceObject(object!, withObject: nil)
return nil
}
}
if objectToEncode == nil {
objectToEncode = object
}
// object replaced by delegate. If the delegate returns nil, nil is encoded
if let unwrappedDelegate = self.delegate {
objectToEncode = unwrappedDelegate.archiver(self, willEncode: objectToEncode!)
replaceObject(object!, withObject: objectToEncode)
}
return objectToEncode
}
/**
Internal function to encode an object. Returns the object reference.
*/
private func _encodeObject(_ objv: Any?, conditional: Bool = false) -> NSObject? {
var object : Any? = nil // object to encode after substitution
var objectRef : _NSKeyedArchiverUID? // encoded object reference
let haveVisited : Bool
let _ = _validateStillEncoding()
haveVisited = _haveVisited(objv)
object = _replacementObject(objv)
// bridge value types
if let bridgedObject = object as? _ObjectBridgeable {
object = bridgedObject._bridgeToAnyObject()
}
objectRef = _referenceObject(object, conditional: conditional)
guard let unwrappedObjectRef = objectRef else {
// we can return nil if the object is being conditionally encoded
return nil
}
_validateObjectSupportsSecureCoding(object)
if !haveVisited {
var encodedObject : Any
if _isContainer(object) {
guard let codable = object as? NSCoding else {
fatalError("Object \(object) does not conform to NSCoding")
}
let innerEncodingContext = EncodingContext()
_pushEncodingContext(innerEncodingContext)
codable.encode(with: self)
let ns = object as? NSObject
let cls : AnyClass = ns?.classForKeyedArchiver ?? type(of: object!) as! AnyClass
_setObjectInCurrentEncodingContext(_classReference(cls), forKey: "$class", escape: false)
_popEncodingContext()
encodedObject = innerEncodingContext.dict
} else {
encodedObject = object!
}
_setObject(encodedObject, forReference: unwrappedObjectRef)
}
if let unwrappedDelegate = self.delegate {
unwrappedDelegate.archiver(self, didEncode: object)
}
return unwrappedObjectRef
}
/**
Encode an object and associate it with a key in the current encoding context.
*/
private func _encodeObject(_ objv: Any?, forKey key: String?, conditional: Bool = false) {
if let objectRef = _encodeObject(objv, conditional: conditional) {
_setObjectInCurrentEncodingContext(objectRef, forKey: key, escape: key != nil)
}
}
open override func encode(_ object: Any?) {
_encodeObject(object, forKey: nil)
}
open override func encodeConditionalObject(_ object: Any?) {
_encodeObject(object, forKey: nil, conditional: true)
}
open override func encode(_ objv: Any?, forKey key: String) {
_encodeObject(objv, forKey: key, conditional: false)
}
open override func encodeConditionalObject(_ objv: Any?, forKey key: String) {
_encodeObject(objv, forKey: key, conditional: true)
}
open override func encodePropertyList(_ aPropertyList: Any) {
if !NSPropertyListClasses.contains(where: { $0 == type(of: aPropertyList) }) {
fatalError("Cannot encode non-property list type \(type(of: aPropertyList)) as property list")
}
encode(aPropertyList)
}
open func encodePropertyList(_ aPropertyList: Any, forKey key: String) {
if !NSPropertyListClasses.contains(where: { $0 == type(of: aPropertyList) }) {
fatalError("Cannot encode non-property list type \(type(of: aPropertyList)) as property list")
}
encode(aPropertyList, forKey: key)
}
open func _encodePropertyList(_ aPropertyList: Any, forKey key: String? = nil) {
let _ = _validateStillEncoding()
_setObjectInCurrentEncodingContext(aPropertyList, forKey: key)
}
internal func _encodeValue<T: NSObject>(_ objv: T, forKey key: String? = nil) where T: NSCoding {
_encodePropertyList(objv, forKey: key)
}
private func _encodeValueOfObjCType(_ type: _NSSimpleObjCType, at addr: UnsafeRawPointer) {
switch type {
case .ID:
let objectp = unsafeBitCast(addr, to: UnsafePointer<Any>.self)
encode(objectp.pointee)
break
case .Class:
let classp = unsafeBitCast(addr, to: UnsafePointer<AnyClass>.self)
encode(NSStringFromClass(classp.pointee)._bridgeToObjectiveC())
break
case .Char:
let charp = unsafeBitCast(addr, to: UnsafePointer<CChar>.self)
_encodeValue(NSNumber(value: charp.pointee))
break
case .UChar:
let ucharp = unsafeBitCast(addr, to: UnsafePointer<UInt8>.self)
_encodeValue(NSNumber(value: ucharp.pointee))
break
case .Int, .Long:
let intp = unsafeBitCast(addr, to: UnsafePointer<Int32>.self)
_encodeValue(NSNumber(value: intp.pointee))
break
case .UInt, .ULong:
let uintp = unsafeBitCast(addr, to: UnsafePointer<UInt32>.self)
_encodeValue(NSNumber(value: uintp.pointee))
break
case .LongLong:
let longlongp = unsafeBitCast(addr, to: UnsafePointer<Int64>.self)
_encodeValue(NSNumber(value: longlongp.pointee))
break
case .ULongLong:
let ulonglongp = unsafeBitCast(addr, to: UnsafePointer<UInt64>.self)
_encodeValue(NSNumber(value: ulonglongp.pointee))
break
case .Float:
let floatp = unsafeBitCast(addr, to: UnsafePointer<Float>.self)
_encodeValue(NSNumber(value: floatp.pointee))
break
case .Double:
let doublep = unsafeBitCast(addr, to: UnsafePointer<Double>.self)
_encodeValue(NSNumber(value: doublep.pointee))
break
case .Bool:
let boolp = unsafeBitCast(addr, to: UnsafePointer<Bool>.self)
_encodeValue(NSNumber(value: boolp.pointee))
break
case .CharPtr:
let charpp = unsafeBitCast(addr, to: UnsafePointer<UnsafePointer<Int8>>.self)
encode(NSString(utf8String: charpp.pointee))
break
default:
fatalError("NSKeyedArchiver.encodeValueOfObjCType: unknown type encoding ('\(type.rawValue)')")
break
}
}
open override func encodeValue(ofObjCType typep: UnsafePointer<Int8>, at addr: UnsafeRawPointer) {
guard let type = _NSSimpleObjCType(UInt8(typep.pointee)) else {
let spec = String(typep.pointee)
fatalError("NSKeyedArchiver.encodeValueOfObjCType: unsupported type encoding spec '\(spec)'")
}
if type == .StructBegin {
fatalError("NSKeyedArchiver.encodeValueOfObjCType: this archiver cannot encode structs")
} else if type == .ArrayBegin {
let scanner = Scanner(string: String(cString: typep))
scanner.scanLocation = 1 // advance past ObJCType
var count : Int = 0
guard scanner.scanInteger(&count) && count > 0 else {
fatalError("NSKeyedArchiver.encodeValueOfObjCType: array count is missing or zero")
}
guard let elementType = _NSSimpleObjCType(scanner.scanUpToString(String(_NSSimpleObjCType.ArrayEnd))) else {
fatalError("NSKeyedArchiver.encodeValueOfObjCType: array type is missing")
}
encode(_NSKeyedCoderOldStyleArray(objCType: elementType, count: count, at: addr))
} else {
return _encodeValueOfObjCType(type, at: addr)
}
}
open override func encode(_ boolv: Bool, forKey key: String) {
_encodeValue(NSNumber(value: boolv), forKey: key)
}
open override func encode(_ intv: Int32, forKey key: String) {
_encodeValue(NSNumber(value: intv), forKey: key)
}
open override func encode(_ intv: Int64, forKey key: String) {
_encodeValue(NSNumber(value: intv), forKey: key)
}
open override func encode(_ realv: Float, forKey key: String) {
_encodeValue(NSNumber(value: realv), forKey: key)
}
open override func encode(_ realv: Double, forKey key: String) {
_encodeValue(NSNumber(value: realv), forKey: key)
}
open override func encode(_ intv: Int, forKey key: String) {
_encodeValue(NSNumber(value: intv), forKey: key)
}
open override func encode(_ data: Data) {
// this encodes as a reference to an NSData object rather than encoding inline
encode(data._nsObject)
}
open override func encodeBytes(_ bytesp: UnsafePointer<UInt8>?, length lenv: Int, forKey key: String) {
// this encodes the data inline
let data = NSData(bytes: bytesp, length: lenv)
_encodeValue(data, forKey: key)
}
/**
Helper API for NSArray and NSDictionary that encodes an array of objects,
creating references as it goes
*/
internal func _encodeArrayOfObjects(_ objects : NSArray, forKey key : String) {
var objectRefs = [NSObject]()
objectRefs.reserveCapacity(objects.count)
for object in objects {
let objectRef = _encodeObject(_SwiftValue.store(object))!
objectRefs.append(objectRef)
}
_encodeValue(objectRefs._bridgeToObjectiveC(), forKey: key)
}
/**
Enables secure coding support on this keyed archiver. You do not need to enable
secure coding on the archiver to enable secure coding on the unarchiver. Enabling
secure coding on the archiver is a way for you to be sure that all classes that
are encoded conform with NSSecureCoding (it will throw an exception if a class
which does not NSSecureCoding is archived). Note that the getter is on the superclass,
NSCoder. See NSCoder for more information about secure coding.
*/
open override var requiresSecureCoding: Bool {
get {
return _flags.contains(ArchiverFlags.requiresSecureCoding)
}
set {
if newValue {
let _ = _flags.insert(ArchiverFlags.requiresSecureCoding)
} else {
_flags.remove(ArchiverFlags.requiresSecureCoding)
}
}
}
// During encoding, the coder first checks with the coder's
// own table, then if there was no mapping there, the class's.
open class func classNameForClass(_ cls: AnyClass) -> String? {
let clsName = String(reflecting: cls)
var mappedClass : String?
_classNameMapLock.synchronized {
mappedClass = _classNameMap[clsName]
}
return mappedClass
}
open func classNameForClass(_ cls: AnyClass) -> String? {
let clsName = String(reflecting: cls)
return _classNameMap[clsName]
}
}
extension NSKeyedArchiverDelegate {
func archiver(_ archiver: NSKeyedArchiver, willEncode object: Any) -> Any? {
// Returning the same object is the same as doing nothing
return object
}
func archiver(_ archiver: NSKeyedArchiver, didEncode object: Any?) { }
func archiver(_ archiver: NSKeyedArchiver, willReplace object: Any?, with newObject: Any?) { }
func archiverWillFinish(_ archiver: NSKeyedArchiver) { }
func archiverDidFinish(_ archiver: NSKeyedArchiver) { }
}
public protocol NSKeyedArchiverDelegate : class {
// Informs the delegate that the object is about to be encoded. The delegate
// either returns this object or can return a different object to be encoded
// instead. The delegate can also fiddle with the coder state. If the delegate
// returns nil, nil is encoded. This method is called after the original object
// may have replaced itself with replacementObjectForKeyedArchiver:.
// This method is not called for an object once a replacement mapping has been
// setup for that object (either explicitly, or because the object has previously
// been encoded). This is also not called when nil is about to be encoded.
// This method is called whether or not the object is being encoded conditionally.
func archiver(_ archiver: NSKeyedArchiver, willEncode object: Any) -> Any?
// Informs the delegate that the given object has been encoded. The delegate
// might restore some state it had fiddled previously, or use this to keep
// track of the objects which are encoded. The object may be nil. Not called
// for conditional objects until they are really encoded (if ever).
func archiver(_ archiver: NSKeyedArchiver, didEncode object: Any?)
// Informs the delegate that the newObject is being substituted for the
// object. This is also called when the delegate itself is doing/has done
// the substitution. The delegate may use this method if it is keeping track
// of the encoded or decoded objects.
func archiver(_ archiver: NSKeyedArchiver, willReplace object: Any?, withObject newObject: Any?)
// Notifies the delegate that encoding is about to finish.
func archiverWillFinish(_ archiver: NSKeyedArchiver)
// Notifies the delegate that encoding has finished.
func archiverDidFinish(_ archiver: NSKeyedArchiver)
}