├── .gitignore ├── LICENSE ├── README.md └── Serializable.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sean Heber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serializable 2 | 3 | A Swift 3 implementation of an NSCoder-inspired serialization API 4 | 5 | # Usage 6 | 7 | Using Serializable is pretty easy - simply make your struct or class conform to the `Serializable` protocol which is very simple: 8 | 9 | ```Swift 10 | protocol Serializable { 11 | init(with coder: Decoder) throws 12 | func encode(with coder: Encoder) 13 | } 14 | ``` 15 | 16 | For example, let’s say we have the following structure: 17 | 18 | ```Swift 19 | struct Dog { 20 | let name: String 21 | let isCute: Bool 22 | var isWaggingTail: Bool 23 | } 24 | ``` 25 | 26 | First we conform it to `Serializable`: 27 | 28 | ```Swift 29 | extension Dog : Serializable { 30 | init(with coder: Decoder) throws { 31 | name = try coder.decode(forKey: "name") 32 | isCute = try coder.decode(forKey: "isCute") 33 | isWaggingTail = try coder.decode(forKey: "isWaggingTail") 34 | } 35 | 36 | func encode(with coder: Encoder) { 37 | coder.encode(name, forKey: "name") 38 | coder.encode(isCute, forKey: "isCute") 39 | coder.encode(isWaggingTail, forKey: "isWaggingTail") 40 | } 41 | } 42 | ``` 43 | 44 | Then we must tell the system about our new `Serializable` type (since Swift has no way that I know of to discover conforming types dynamically). This only needs to be done once - perhaps during app launch: 45 | 46 | ```Swift 47 | Dog.enableSerialization() 48 | ``` 49 | 50 | Many basic Swift and Foundation types such as `Int` and `UInt` (and the 8, 16, 32, and 64 variants) along with `Float`, `Double`, `Float80`, `String`, `Bool`, `Data`, and `Date` already conform to `Serializable` and are automatically registered. 51 | 52 | Once you have your types conformed to `Serializable` and registered, it is a simple matter of making an `Encoder` and encoding a value into it: 53 | 54 | ```Swift 55 | let myDog = Dog(name: "Fido", isCute: true, isWaggingTail: false) 56 | let aCoder = Encoder() 57 | aCoder.encode(myDog, forKey: "currentDog") 58 | ``` 59 | 60 | When you’re ready to save everything you've encoded to a file or send it over the network, generate a `Data` object from the encoder: 61 | 62 | ```Swift 63 | let codedData = aCoder.makeData() 64 | ``` 65 | 66 | Decoding the serialized data is just as easy (of course you should catch the errors and not just ignore them like this simple sample): 67 | 68 | ```Swift 69 | let aDecoder = try! Decoder(from: codedData) 70 | let decodedDog: Dog = try! aDecoder.decode(forKey: "currentDog") 71 | ``` 72 | 73 | Tada! 74 | 75 | If you have a collection to serialize such as: 76 | 77 | ```Swift 78 | var bestFriends: Array = [ 79 | Dog(name: "Fido", isCute: true, isWaggingTail: false), 80 | Dog(name: "Ruff", isCute: true, isWaggingTail: true), 81 | Dog(name: "Rex", isCute: true, isWaggingTail: true) 82 | ] 83 | ``` 84 | 85 | ...then you must also register an array of `Dog`s as a serializable type: 86 | 87 | ```Swift 88 | Array.enableSerialization() 89 | ``` 90 | 91 | `Array`, `Set` and `Dictionary` conform to `Serializable` out of the box - but they are not automatically registered since they’re generic. You must register any combination that you intend to serialize so the system knows what to do with them when it encounters them. If you accidentally forget to register a type, you’ll get a `fatalError()` noting the missing type when you try to test your serialization code - so they're easy to find and fix. 92 | 93 | If you want to eliminate some boilerplate, use the `AutomaticallyEncodedSerializable` protocol which has a default implementation of `encode(with:)` that uses `Mirror` to automatically name and encode all of the properties it finds. Using this, the example from above can be shortened to: 94 | 95 | ```Swift 96 | extension Dog : AutomaticallyEncodedSerializable { 97 | init(with coder: Decoder) throws { 98 | name = try coder.decode(forKey: "name") 99 | isCute = try coder.decode(forKey: "isCute") 100 | isWaggingTail = try coder.decode(forKey: "isWaggingTail") 101 | } 102 | } 103 | ``` 104 | 105 | There is also the `RestorableSerializable` protocol: 106 | 107 | ```Swift 108 | protocol RestorableSerializable : Serializable { 109 | mutating func restored(with coder: Decoder) throws 110 | } 111 | ``` 112 | 113 | For types that conform to this protocol, the `restored(with:)` function is called after `init(with:)` has succeeded. This is most useful for class instances where you might need to restore a circular reference to `self` or something like that. 114 | 115 | Finally, there is the `makeClone()` function implemented in an extension for `Serializable`. This grants any `Serializable` value the ability to make a deep copy by encoding and then decoding itself and returning the result. The `makeClone()` function is more efficient than encoding to data and then decoding since it copies the internal state of the encoder directly into the decoder - so if you want a deep copy, this is a decent way to get one. 116 | 117 | 118 | # Notes 119 | 120 | * The binary data format is pretty compact - it only stores string symbols once and, of course, only stores a single copy of a reference type instance. In my tests, it also seems to compress remarkably well. 121 | 122 | * The binary format should be cross platform - it always writes integers in little endian format, and always stores strings and symbols as UTF-8. The `Int` and `UInt` types (which can be either 32 or 64 bit) are both always stored as 64 bit. 123 | 124 | * If serializing classes, then superclasses and subclasses effectively share a flat namespace for the key names. Key name collisions when encoding or decoding will almost certainly result in a `fatalError()` due to an internal precondition explicitly designed to detect this sort of subtle bug before it becomes a problem for you later. 125 | 126 | * This is not yet well tested, of course, but it seems to work! Good luck! -------------------------------------------------------------------------------- /Serializable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Errors that can be thrown by the serialization system. 4 | public enum CoderError : Error { 5 | case missingValue(forKey: String) 6 | case typeMistmatch(forKey: String) 7 | case unknownType(String) 8 | case invalidInput 9 | } 10 | 11 | fileprivate enum Primitive { 12 | case bytes([UInt8]) 13 | case symbol(UInt64) 14 | case reference(UInt64) 15 | case list([Primitive]) 16 | case table([(Primitive, Primitive)]) 17 | case value(UInt64, [UInt64 : Primitive]) 18 | } 19 | 20 | fileprivate extension UInt8 { 21 | static let bytesKey: UInt8 = 1 22 | static let symbolKey: UInt8 = 2 23 | static let valueKey: UInt8 = 3 24 | static let referenceKey: UInt8 = 4 25 | static let listKey: UInt8 = 5 26 | static let tableKey: UInt8 = 6 27 | static let versionKey: UInt8 = 128 28 | static let symbolTableKey: UInt8 = 129 29 | static let objectsKey: UInt8 = 130 30 | static let rootsKey: UInt8 = 131 31 | } 32 | 33 | // copy the raw bytes of a value 34 | fileprivate func unsafeBytes(for value: T) -> [UInt8] { 35 | var value = value 36 | return withUnsafeBytes(of: &value) { Array($0) } 37 | } 38 | 39 | // use raw bytes to recreate a value 40 | fileprivate func unsafeValue(from bytes: [UInt8]) -> T { 41 | return bytes.withUnsafeBytes { $0.baseAddress!.load(as: T.self) } 42 | } 43 | 44 | /// This class provides implementation support for both `Encoder` and `Decoder` and is not meant to be used on its own. 45 | public class Coder { 46 | fileprivate var roots: [UInt64 : Primitive] 47 | fileprivate var objects: [Primitive] 48 | fileprivate var symbols: [String] 49 | fileprivate var symbolIndexes: [String : UInt64] 50 | 51 | fileprivate init() { 52 | roots = [:] 53 | objects = [] 54 | symbols = [] 55 | symbolIndexes = [:] 56 | } 57 | 58 | /// Creates an `Encoder` or `Decoder` instance from previously encoded data, or throws on error. 59 | public convenience init(from encodedData: Data) throws { 60 | self.init() 61 | 62 | let input = InputStream(data: encodedData) 63 | input.open() 64 | 65 | func readBytes(_ count: UInt64) throws -> [UInt8] { 66 | var buffer: [UInt8] = Array(repeating: 0, count: Int(count)) 67 | guard input.read(&buffer, maxLength: buffer.count) == buffer.count else { throw CoderError.invalidInput } 68 | return buffer 69 | } 70 | 71 | func readByte() throws -> UInt8 { 72 | var byte: UInt8 = 0 73 | guard input.read(&byte, maxLength: 1) == 1 else { throw CoderError.invalidInput } 74 | return byte 75 | } 76 | 77 | func readNumber() throws -> UInt64 { 78 | let raw = try readBytes(UInt64(MemoryLayout.size)) 79 | return unsafeValue(from: raw) 80 | } 81 | 82 | func readPrimitive() throws -> Primitive { 83 | switch try readByte() { 84 | case UInt8.bytesKey: 85 | return try .bytes(readBytes(readNumber())) 86 | 87 | case UInt8.symbolKey: 88 | return try .symbol(readNumber()) 89 | 90 | case UInt8.referenceKey: 91 | return try .reference(readNumber()) 92 | 93 | case UInt8.listKey: 94 | let values = try (0.. Data { 145 | var output = Data() 146 | 147 | func writeByte(_ byte: UInt8) { 148 | output.append(byte) 149 | } 150 | 151 | func writeBytes(_ bytes: [UInt8]) { 152 | output.append(contentsOf: bytes) 153 | } 154 | 155 | func writeNumber(_ value: UInt64) { 156 | writeBytes(unsafeBytes(for: value)) 157 | } 158 | 159 | func writePrimitive(_ primitive: Primitive) { 160 | switch primitive { 161 | case let .bytes(bytes): 162 | writeByte(.bytesKey) 163 | writeNumber(UInt64(bytes.count)) 164 | writeBytes(bytes) 165 | 166 | case let .symbol(index): 167 | writeByte(.symbolKey) 168 | writeNumber(index) 169 | 170 | case let .reference(index): 171 | writeByte(.referenceKey) 172 | writeNumber(index) 173 | 174 | case let .list(list): 175 | writeByte(.listKey) 176 | writeNumber(UInt64(list.count)) 177 | list.forEach(writePrimitive) 178 | 179 | case let .table(table): 180 | writeByte(.tableKey) 181 | writeNumber(UInt64(table.count)) 182 | table.forEach { 183 | writePrimitive($0) 184 | writePrimitive($1) 185 | } 186 | 187 | case let .value(typeIdentifier, parts): 188 | writeByte(.valueKey) 189 | writeNumber(typeIdentifier) 190 | writeNumber(UInt64(parts.count)) 191 | parts.forEach { 192 | writeNumber($0) 193 | writePrimitive($1) 194 | } 195 | } 196 | } 197 | 198 | // write the current version of the serialization format 199 | writeByte(.versionKey) 200 | writeNumber(1) 201 | 202 | // write all of the symbols as utf8 203 | writeByte(.symbolTableKey) 204 | writeNumber(UInt64(symbols.count)) 205 | symbols.forEach { 206 | let bytes = Array($0.utf8) 207 | writeNumber(UInt64(bytes.count)) 208 | writeBytes(bytes) 209 | } 210 | 211 | // write the objects 212 | writeByte(.objectsKey) 213 | writeNumber(UInt64(objects.count)) 214 | objects.forEach(writePrimitive) 215 | 216 | // write the root key/values 217 | writeByte(.rootsKey) 218 | writeNumber(UInt64(roots.count)) 219 | roots.forEach { 220 | writeNumber($0) 221 | writePrimitive($1) 222 | } 223 | 224 | return output 225 | } 226 | } 227 | 228 | /// The serialization encoder. 229 | /// Use an instance of this class to encode values and objects and then call `makeData()` to generate an archive suitable for saving. 230 | /// All `Serialization` types that will be encountered during encoding must have been registered with `enableSerialization()`. 231 | public final class Encoder : Coder { 232 | private var typeIdentifierCache: [ObjectIdentifier : UInt64] 233 | private var encodedObjectIndexes: [ObjectIdentifier : UInt64] 234 | 235 | /// Creates a new, empty encoder. 236 | public override init() { 237 | self.typeIdentifierCache = [:] 238 | self.encodedObjectIndexes = [:] 239 | super.init() 240 | } 241 | 242 | /// Creates a new `Encoder` with the given value encoded as a root using an empty key. 243 | public convenience init(with value: Serializable) { 244 | self.init() 245 | encode(value) 246 | } 247 | 248 | // adds a new symbol if needed, returns the index of the symbol in the table. 249 | private func symbolIndex(_ identifier: String) -> UInt64 { 250 | if let existingIndex = symbolIndexes[identifier] { 251 | return existingIndex 252 | } 253 | 254 | // add it to the table 255 | let index = UInt64(symbols.count) 256 | symbols.append(identifier) 257 | symbolIndexes[identifier] = index 258 | return index 259 | } 260 | 261 | private func primitive(for rawValue: Any) -> Primitive { 262 | let type = type(of: rawValue) 263 | 264 | guard let value = rawValue as? Serializable else { 265 | fatalError("\(type) does not conform to Serializable") 266 | } 267 | 268 | // fetch the index number of the symbol that matches the type of the value 269 | func getTypeIdentifierIndex() -> UInt64 { 270 | let typeIdentifier = ObjectIdentifier(type) 271 | 272 | if let cachedIdentifierIndex = typeIdentifierCache[typeIdentifier] { 273 | return cachedIdentifierIndex 274 | } 275 | 276 | guard let name = serializationTypes.first(where: { ObjectIdentifier($0.1) == typeIdentifier })?.0 else { 277 | fatalError("\(type) conforms to Serializable but was not registered with \(type).enableSerialization()") 278 | } 279 | 280 | let typeIdentifierIndex = symbolIndex(name) 281 | typeIdentifierCache[typeIdentifier] = typeIdentifierIndex 282 | return typeIdentifierIndex 283 | } 284 | 285 | // actually encodes the value 286 | func makePrimitive() -> Primitive { 287 | let previous = roots 288 | roots = [:] 289 | 290 | value.encode(with: self) 291 | 292 | let encodedParts = roots 293 | roots = previous 294 | 295 | return .value(getTypeIdentifierIndex(), encodedParts) 296 | } 297 | 298 | // test if we're dealing with a reference type or not - if not, encode it and we're done! 299 | guard type is AnyClass else { 300 | return makePrimitive() 301 | } 302 | 303 | // since we have a reference type, we want to encode the instance only once and then reference it everywhere else 304 | // so we will need to check and update the objects table accordingly 305 | let id = ObjectIdentifier(value as AnyObject) 306 | 307 | // ensure we do not already have an entry in the objects table for this instance - if we do, then just return a reference 308 | if let index = encodedObjectIndexes[id] { 309 | return .reference(index) 310 | } 311 | 312 | let index = UInt64(objects.count) 313 | let reference = Primitive.reference(index) 314 | 315 | // this should prevent issues with circular references while encoding 316 | objects.append(reference) 317 | encodedObjectIndexes[id] = index 318 | 319 | // actually encode the object as a value and store it in the object table 320 | objects[Int(index)] = makePrimitive() 321 | 322 | // return a reference to the newly encoded object 323 | return reference 324 | } 325 | 326 | /// Encode any `Serializable` value. The type (and all types that type encodes) must first have been 327 | /// registered with `enableSerialization()` or else this will result in a fatal error. If the value is 328 | /// `nil`, nothing will be encoded and `key` is ignored. 329 | public func encode(_ optionalValue: Serializable?, forKey key: String = "") { 330 | guard let value = optionalValue else { return } 331 | precondition(roots.updateValue(primitive(for: value), forKey: symbolIndex(key)) == nil) 332 | } 333 | 334 | /// Encode raw bytes. 335 | public func encodeBytes(_ bytes: [UInt8], forKey key: String = "") { 336 | precondition(roots.updateValue(.bytes(Array(bytes)), forKey: symbolIndex(key)) == nil) 337 | } 338 | 339 | /// Encode the `String` directly as a symbol primitive which ensures only a single copy of the given string is stored in the archive. 340 | public func encodeSymbol(_ symbol: String, forKey key: String = "") { 341 | precondition(roots.updateValue(.symbol(symbolIndex(symbol)), forKey: symbolIndex(key)) == nil) 342 | } 343 | 344 | /// Encode any sequence as a raw "list". A list is expected to consist only of values that are `Serializable`. The list 345 | /// does not need to be flat - it can be arbitrarily complex or nested as long as all of the encoded types are registered. 346 | public func encodeList(_ list: T, forKey key: String = "") { 347 | let values = list.map(primitive(for:)) 348 | precondition(roots.updateValue(.list(values), forKey: symbolIndex(key)) == nil) 349 | } 350 | 351 | /// Encode a sequence of pairs as a "table". The notes in `encodeList(_:forKey:)` apply here as well. This is useful 352 | /// for serializing data structures such as multimaps. Note that `Dictionary` already conforms to `Serializable` and uses 353 | /// a table as the underlying encoding mechanism, but you will need to register your specific type of Dictionary by using 354 | /// enableSerialization() on the type. 355 | public func encodeTable(_ table: T, forKey key: String = "") where T.Iterator.Element == (A, B) { 356 | let values = table.map { (primitive(for: $0), primitive(for: $1)) } 357 | precondition(roots.updateValue(.table(values), forKey: symbolIndex(key)) == nil) 358 | } 359 | } 360 | 361 | /// The serialization decoder. 362 | /// Use an instance of this class initialized with `init(from:)` and then call the suitable `decode(forKey:)` functions. 363 | /// Almost all functions in this class can throw errors since there are so many things that can go wrong. 364 | public final class Decoder : Coder { 365 | private let decodedTypes: [UInt64 : Serializable.Type] = [:] 366 | private var decodedObjects: [UInt64 : Serializable] = [:] 367 | 368 | /// Creates a new `Decoder` directly from the current state of `encoder`. 369 | public convenience init(from encoder: Encoder) { 370 | self.init() 371 | roots = encoder.roots 372 | objects = encoder.objects 373 | symbols = encoder.symbols 374 | symbolIndexes = encoder.symbolIndexes 375 | } 376 | 377 | // ensures there is a primitive with the given name in the current roots structure 378 | private func primitive(forKey key: String) throws -> Primitive { 379 | guard let index = symbolIndexes[key], let result = roots[index] else { throw CoderError.missingValue(forKey: key) } 380 | return result 381 | } 382 | 383 | // safely ensures the symbol index is in range before attempting to access it 384 | private func symbol(at index: UInt64) throws -> String { 385 | guard index >= 0 && index < UInt64(symbols.count) else { throw CoderError.invalidInput } 386 | return symbols[Int(index)] 387 | } 388 | 389 | private func serializable(from primitive: Primitive, stashingValueInObjectTableAt objectIndex: UInt64? = nil) throws -> Serializable { 390 | if case let .reference(index) = primitive { 391 | if let value = decodedObjects[index] { 392 | return value 393 | } 394 | 395 | return try serializable(from: objects[Int(index)], stashingValueInObjectTableAt: index) 396 | } 397 | 398 | guard case let .value(index, parts) = primitive else { 399 | throw CoderError.invalidInput 400 | } 401 | 402 | func valueType() throws -> Serializable.Type { 403 | if let cachedType = decodedTypes[index] { 404 | return cachedType 405 | } 406 | 407 | let encodedSymbol = try symbol(at: index) 408 | guard let knownType = serializationTypes.first(where: { $0.0 == encodedSymbol })?.1 else { 409 | throw CoderError.unknownType(encodedSymbol) 410 | } 411 | 412 | return knownType 413 | } 414 | 415 | let previous = roots 416 | roots = parts 417 | 418 | let type = try valueType() 419 | var newValue = try type.init(with: self) 420 | 421 | if let idx = objectIndex { 422 | precondition(type is AnyClass) 423 | decodedObjects[idx] = newValue 424 | } 425 | 426 | if var restorable = newValue as? RestorableSerializable { 427 | try restorable.restored(with: self) 428 | newValue = restorable 429 | } 430 | 431 | roots = previous 432 | return newValue 433 | } 434 | 435 | /// Decode an optional 'Serializable' value of type `T` or return `nil` if `key` cannot be found. 436 | /// If there is a record for `key` and the type cannot be cast to `T`, then it will throw instead. 437 | public func decode(forKey key: String = "") throws -> T? { 438 | guard let codedValue = try? primitive(forKey: key) else { return nil } 439 | guard let value = try serializable(from: codedValue) as? T else { throw CoderError.typeMistmatch(forKey: key) } 440 | return value 441 | } 442 | 443 | /// Decode a non-optional `Serializable` value of type `T`. 444 | public func decode(forKey key: String = "") throws -> T { 445 | let codedValue = try primitive(forKey: key) 446 | guard let value = try serializable(from: codedValue) as? T else { throw CoderError.typeMistmatch(forKey: key) } 447 | return value 448 | } 449 | 450 | /// Decode an array of raw bytes. 451 | public func decodeBytes(forKey key: String = "") throws -> [UInt8] { 452 | let codedBytes = try primitive(forKey: key) 453 | guard case let .bytes(bytes) = codedBytes else { throw CoderError.typeMistmatch(forKey: key) } 454 | return bytes 455 | } 456 | 457 | /// Decode a `String` that was previously encoded as a symbol primitive. 458 | public func decodeSymbol(forKey key: String = "") throws -> String { 459 | let codedSymbol = try primitive(forKey: key) 460 | guard case let .symbol(index) = codedSymbol else { throw CoderError.typeMistmatch(forKey: key) } 461 | return try symbol(at: index) 462 | } 463 | 464 | /// Decode a list of `Serializable` values that were previously encoded using `Encoder`'s `encodeList(_:forKey:)` function. 465 | public func decodeList(forKey key: String = "") throws -> [T] { 466 | let codedValue = try primitive(forKey: key) 467 | guard case let .list(parts) = codedValue else { throw CoderError.typeMistmatch(forKey: key) } 468 | return try parts.map({ try serializable(from: $0) as! T }) 469 | } 470 | 471 | /// Decodes a table of `Serializable` values that was previously encoded using `Encoder`'s `encodeTable(_:forKey:)` function. 472 | public func decodeTable(forKey key: String = "") throws -> [(A, B)] { 473 | let codedValue = try primitive(forKey: key) 474 | guard case let .table(parts) = codedValue else { throw CoderError.typeMistmatch(forKey: key) } 475 | return try parts.map { try (serializable(from: $0) as! A, serializable(from: $1) as! B) } 476 | } 477 | } 478 | 479 | /// Types that conform to `Serializable` can be encoded by an `Encoder` and decoded by a `Decoder`. 480 | public protocol Serializable { 481 | /// Create a new instance of `Self` and restore state from the encoded values available in `coder`. 482 | /// If you are implementing this for a class, be sure to call super from subclasses or you'll have a bad time. 483 | init(with coder: Decoder) throws 484 | 485 | /// Encode the current state of `Self`'s instance by encoding values into `coder`. 486 | /// If you are implementing this for a class, be sure to call super from subclasses or you'll have a bad time. 487 | func encode(with coder: Encoder) 488 | } 489 | 490 | /// Types that conform to `RestorableSerializable` will have their `restored(with:)` function called after 491 | /// the normal `init(with:)` initializer has returned. This can be used for restoring references that might be circular. 492 | public protocol RestorableSerializable : Serializable { 493 | mutating func restored(with coder: Decoder) throws 494 | } 495 | 496 | /// A type that conforms to `AutomaticallyEncodedSerializable` will gain a default implementation of `encode(with:)` 497 | /// which uses reflection to automatially encode all named properties it can find. Every found property must be of a type 498 | /// that conforms to `Serializable` or else a fatal error will occur that will print out the offending type so you can fix it. 499 | /// 500 | /// Unfortunately due to Swift's limitations, it is not really possible to automatically implement `init(with:)` and so 501 | /// that is still your responsibily - but hey, it's better than having to implement both of the `Serializable` requirements 502 | /// yourself! 503 | public protocol AutomaticallyEncodedSerializable : Serializable { 504 | } 505 | 506 | public extension Serializable { 507 | /// This function is how you register a custom type for serialization. This must be called prior to attempting to encode 508 | /// or decode any instances of that type. Failure to do so will almost always cause a fatal error which will print out the 509 | /// type that was missing so it should be easy to fix. 510 | /// 511 | /// This is unfortunately necessary due to limitations of Swift's current implementation. I could not find any other way to 512 | /// do this that would work for non-Objective-C types since Swift has no way (that I know of) to instantiate an instance 513 | /// from a string of a type's name and there's no way to ask the runtime for a list of types that conform to a given protocol. 514 | /// If either of those problems could be solved, this requirement could probably be eliminated. 515 | /// 516 | /// Many standard common types such as `Int` and `UInt` (and the 8, 16, 32, and 64 variants) along with `Float`, `Double`, 517 | /// `Float80`, `String`, `Bool`, `Data`, and `Date` are all automatically registered as `Serializable`. 518 | /// 519 | /// Standard containers such as `Array`, `Dictionary`, and `Set` conform to `Serializable` out of the box, but they are not 520 | /// automatically registered because they are generic. You will need to register a number of such types prior to serialization 521 | /// depending on which combinations of containers and contained types you want to serialize. For example: 522 | /// 523 | /// Array.enableSerialization() 524 | /// Dictionary.enableSerialization() 525 | /// 526 | /// By default, the type's internal name (Module.TypeName) will be used as the type's internal identifier, but if you wish, 527 | /// you can specify any name you want using `identifier` as long as it is unique and that the name matches the same type when 528 | /// both encoding and decoding. 529 | static func enableSerialization(as identifier: String? = nil) { 530 | let record = serializationRecord(as: identifier) 531 | precondition(!serializationTypes.contains(where: { record.0 == $0.0 || record.1 == $0.1 })) 532 | serializationTypes.append(record) 533 | } 534 | 535 | /// This makes a clone (a deep copy) of the `Serializable` instance by doing an encode and then decode. 536 | /// Returns a new instance or `nil` if decoding failed for some reason. 537 | func makeClone() -> Self? { 538 | let encoder = Encoder() 539 | encoder.encode(self) 540 | 541 | let decoder = Decoder(from: encoder) 542 | return try? decoder.decode() 543 | } 544 | } 545 | 546 | /// Super clever way to detect when an `Any` instance is actually an `Optional` without using a `Mirror`. 547 | /// This is used by `AutomaticallyEncodedSerializable`'s implementation of `encode(with:)`. 548 | fileprivate protocol SerializingOptional { 549 | var serializable: Serializable? { get } 550 | } 551 | 552 | extension Optional : SerializingOptional { 553 | var serializable: Serializable? { 554 | if case let .some(value) = self { 555 | guard let unwrappedSerializable = value as? Serializable else { 556 | fatalError("\(type(of: value)) does not conform to Serializable") 557 | } 558 | return unwrappedSerializable 559 | } else { 560 | return nil 561 | } 562 | } 563 | } 564 | 565 | public extension AutomaticallyEncodedSerializable { 566 | func encode(with coder: Encoder) { 567 | var mirror: Mirror? = Mirror(reflecting: self) 568 | 569 | while mirror != nil { 570 | for (name, property) in mirror!.children where name != nil { 571 | if let optional = property as? SerializingOptional { 572 | coder.encode(optional.serializable, forKey: name!) 573 | } else if let serializable = property as? Serializable { 574 | coder.encode(serializable, forKey: name!) 575 | } else { 576 | fatalError("\(type(of: self)) property '\(name!)' does not conform to Serializable") 577 | } 578 | } 579 | 580 | mirror = mirror?.superclassMirror 581 | } 582 | } 583 | } 584 | 585 | fileprivate extension Serializable { 586 | static func serializationRecord(as identifier: String? = nil) -> (String, Serializable.Type) { 587 | return (identifier ?? String(reflecting: self), self) 588 | } 589 | } 590 | 591 | extension Int : Serializable { 592 | public init(with coder: Decoder) throws { try self.init(truncatingBitPattern: Int64(littleEndian: unsafeValue(from: coder.decodeBytes()) as Int64)) } 593 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: Int64(self).littleEndian)) } 594 | } 595 | 596 | extension Int8 : Serializable { 597 | public init(with coder: Decoder) throws { try self.init(unsafeValue(from: coder.decodeBytes()) as Int8) } 598 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self)) } 599 | } 600 | 601 | extension Int16 : Serializable { 602 | public init(with coder: Decoder) throws { try self.init(littleEndian: unsafeValue(from: coder.decodeBytes())) } 603 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self.littleEndian)) } 604 | } 605 | 606 | extension Int32 : Serializable { 607 | public init(with coder: Decoder) throws { try self.init(littleEndian: unsafeValue(from: coder.decodeBytes())) } 608 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self.littleEndian)) } 609 | } 610 | 611 | extension Int64 : Serializable { 612 | public init(with coder: Decoder) throws { try self.init(littleEndian: unsafeValue(from: coder.decodeBytes())) } 613 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self.littleEndian)) } 614 | } 615 | 616 | extension UInt : Serializable { 617 | public init(with coder: Decoder) throws { try self.init(truncatingBitPattern: UInt64(littleEndian: unsafeValue(from: coder.decodeBytes()) as UInt64)) } 618 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: UInt64(self).littleEndian)) } 619 | } 620 | 621 | extension UInt8 : Serializable { 622 | public init(with coder: Decoder) throws { try self.init(unsafeValue(from: coder.decodeBytes()) as UInt8) } 623 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self)) } 624 | } 625 | 626 | extension UInt16 : Serializable { 627 | public init(with coder: Decoder) throws { try self.init(littleEndian: unsafeValue(from: coder.decodeBytes())) } 628 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self.littleEndian)) } 629 | } 630 | 631 | extension UInt32 : Serializable { 632 | public init(with coder: Decoder) throws { try self.init(littleEndian: unsafeValue(from: coder.decodeBytes())) } 633 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self.littleEndian)) } 634 | } 635 | 636 | extension UInt64 : Serializable { 637 | public init(with coder: Decoder) throws { try self.init(littleEndian: unsafeValue(from: coder.decodeBytes())) } 638 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self.littleEndian)) } 639 | } 640 | 641 | extension Float : Serializable { 642 | public init(with coder: Decoder) throws { try self.init(unsafeValue(from: coder.decodeBytes()) as Float) } 643 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self)) } 644 | } 645 | 646 | extension Double : Serializable { 647 | public init(with coder: Decoder) throws { try self.init(unsafeValue(from: coder.decodeBytes()) as Double) } 648 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self)) } 649 | } 650 | 651 | extension Float80 : Serializable { 652 | public init(with coder: Decoder) throws { try self.init(unsafeValue(from: coder.decodeBytes()) as Float80) } 653 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self)) } 654 | } 655 | 656 | extension String : Serializable { 657 | public init(with coder: Decoder) throws { try self.init(coder.decodeSymbol())! } 658 | public func encode(with coder: Encoder) { coder.encodeSymbol(self) } 659 | } 660 | 661 | extension Bool : Serializable { 662 | public init(with coder: Decoder) throws { try self.init(unsafeValue(from: coder.decodeBytes()) as Bool) } 663 | public func encode(with coder: Encoder) { coder.encodeBytes(unsafeBytes(for: self)) } 664 | } 665 | 666 | extension Data : Serializable { 667 | public init(with coder: Decoder) throws { try self.init(bytes: coder.decodeBytes()) } 668 | public func encode(with coder: Encoder) { coder.encodeBytes(Array(self))} 669 | } 670 | 671 | extension Date : Serializable { 672 | public init(with coder: Decoder) throws { try self.init(timeIntervalSinceReferenceDate: coder.decode()) } 673 | public func encode(with coder: Encoder) { coder.encode(timeIntervalSinceReferenceDate) } 674 | } 675 | 676 | /// This protocol is handy for adding quick `Serializable` conformance for types that are sequences 677 | /// and have (or can easily be given) a simple sequence initializer. It includes default implementations of 678 | /// `init(with:)` and 'encode(with:)`. This is used by `Array` and `Set` to conform to 'Serializable' almost for free. 679 | public protocol SerializableSequence : Serializable, Sequence { 680 | init(_ s: S) where S.Iterator.Element == Iterator.Element 681 | } 682 | 683 | public extension SerializableSequence { 684 | init(with coder: Decoder) throws { 685 | try self.init(coder.decodeList()) 686 | } 687 | 688 | func encode(with coder: Encoder) { 689 | coder.encodeList(self) 690 | } 691 | } 692 | 693 | extension Array : SerializableSequence {} 694 | 695 | extension Set : SerializableSequence {} 696 | 697 | extension Dictionary : Serializable { 698 | public init(with coder: Decoder) throws { 699 | self.init() 700 | for (key, value) in try coder.decodeTable() as [(Key, Value)] { 701 | precondition(updateValue(value, forKey: key) == nil) 702 | } 703 | } 704 | 705 | public func encode(with coder: Encoder) { 706 | coder.encodeTable(map({ ($0, $1) })) 707 | } 708 | } 709 | 710 | fileprivate var serializationTypes = [ 711 | Bool.serializationRecord(), 712 | 713 | Int.serializationRecord(), 714 | Int8.serializationRecord(), 715 | Int16.serializationRecord(), 716 | Int32.serializationRecord(), 717 | Int64.serializationRecord(), 718 | 719 | UInt.serializationRecord(), 720 | UInt8.serializationRecord(), 721 | UInt16.serializationRecord(), 722 | UInt32.serializationRecord(), 723 | UInt64.serializationRecord(), 724 | 725 | Float.serializationRecord(), 726 | Double.serializationRecord(), 727 | Float80.serializationRecord(), 728 | 729 | String.serializationRecord(), 730 | 731 | Data.serializationRecord(), 732 | Date.serializationRecord(), 733 | ] 734 | --------------------------------------------------------------------------------