Cereal is a serialization framework built for Swift

Related tags

Utility Cereal
Overview

Cereal is a serialization framework built for Swift. Its intended as a substitution for NSCoding to allow advanced Swift features. With NSCoding, you cannot encode or decode a Swift struct, enum, or generic class. Cereal solves this issue through deferred decoding with generics, and a protocol that doesn't depend on NSObjectProtocol.

Please note that the data stored in Cereal 2.0 is different from Cereal 1.3, and while the API is the same they are not compatible. Do not expect data written by 1.3 to be readable by 2.0.

Carthage compatible

Features

  • Encode and decode String, Bool, Int, Int64, Float, Double, NSDate and NSURL
  • Encode and decode Arrays, Arrays of Dictionaries, Dictionaries, and Dictionaries of Arrays
  • Encode and decode your own enum types
  • Encode and decode your own struct types
  • Encode and decode your own class types, even if they don't inherit from NSObject
  • Encode and decode as a protocol
  • Encode and decode types with generics
  • Encode and decode RawRepresentable types, whose RawValue's are also encodable/decodable
  • Enforce decoding of the same type as encoded with
  • Comprehensive test suite

Requirements

  • iOS 8.0+ / Mac OS X 10.9 / watchOS 2
  • Xcode 9.0+

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects that handles much of the integration for you.

CocoaPods 0.38 is required to integrate Cereal into basic projects; if you'd like to include Cereal in both your watchOS target and iOS target, you'll need to use 0.39 to resolve a bug.

Basic integration

For basic integration, add this to your Podfile:

pod 'Cereal', '~> 4.0'

Multiple Targets

If you want to incorporate Cereal with your watchOS target and your iOS target, you'll need something like this:

def shared_pods 
  pod 'Cereal', '~> 4.0'
end

target :"iOS App Target" do
    platform :ios, '8.0'
    shared_pods
end

target :"watchOS Extension Target" do
    platform :watchos, '2.0'
    shared_pods
end

Carthage

Carthage is a decentralized dependency manager. Add the following to your Cartfile to build Cereal:

4.0.0">
github "Weebly/Cereal" ~> 4.0.0

Manually

If you'd like to add Cereal to your project manually you can copy the .swift files contained in the Cereal directory into your project and have them compile on all the necessary targets.

Usage

Cereal works similarly to NSKeyedArchiver. For encoding, you create a CerealEncoder, and encode the objects you'd like stored in the resulting data. Here is an example of encoding various primitive types:

var encoder = CerealEncoder()
try encoder.encode(5, forKey: "myInt")
try encoder.encode([1,2,3], forKey: "myIntArray")
try encoder.encode(["Hello,", "World!"], forKey: "myStringArray")
try encoder.encode(["Foo": "Bar"], forKey: "myStringToStringDictionary")
let data = encoder.toData()

From there you can store your data anywhere you'd like; be it state restoration, user defaults, to a file, or communicating to the users watch with WatchConnectivity. To decode the object on the other end:

let decoder = CerealDecoder(data: data)
let myInt: Int? = try decoder.decode("myInt")
let myIntArray: [Int]? = try decoder.decode("myIntArray")
let myStringArray: [String]? = try decoder.decode("myStringArray")
let myStringToStringDictionary: [String: String]? = try decoder.decode("myStringToStringDictionary")

Due to the complexity of generic code used with dictionaries and arrays, you cannot deeply nest dictionaries/arrays into themselves. However, support for dictionaries of arrays and arrays of dictionaries are included. I strongly recommend you serialize your own custom types instead of dictionaries whenever possible. Here is an example from our example iOS project:

// employees is a [Employee]; Employee is a Swift struct.
let data = try CerealEncoder.data(withRoot: employees) // Returns an Data object

And to decode that data

do {
    employees = try CerealDecoder.rootCerealItems(with: storedEmployeeData)
} catch let error {
    NSLog("Couldn't decode employees due to error: \(error)")
}

The above examples are using a shorthand to encode and decode at the root level. Typically you encode items to a particular key.

Serializing your concrete types

Your custom types should adopt CerealType (or its subprotocol, IdentifyingCerealType) to be encoded and decoded. Here is the Employee struct:

struct Employee {
    var name = ""
    var age = 18
    var gender: Gender = .Female

    init() { }
}

extension Employee: CerealType {
    private struct Keys {
        static let name = "name"
        static let age = "age"
        static let gender = "gender"
    }

    init(cereal: CerealDecoder) throws {
        name = try cereal.decode(Keys.name) ?? ""
        age = try cereal.decode(Keys.age) ?? 0
        gender = try cereal.decodeCereal(Keys.gender) ?? .Female
    }

    func encodeWithCereal(inout cereal: CerealEncoder) throws {
        try cereal.encode(name, forKey: Keys.name)
        try cereal.encode(age, forKey: Keys.age)
        try cereal.encode(gender, forKey: Keys.gender)
    }
}

You may notice above that there are two decoding methods; decode and decodeCereal. This is because the compiler cannot tell which version of decode to use just from the return type (since primitives and custom types must conform to CerealRepresentable, our internal protocol which should not be conformed to in your code), so there are a few different methods for decoding.

Serializing your protocols

Protocol-Oriented Programming was a concept Cereal had to support out of the box. Protocols don't give instantiatable type data for our generic decoders, though, so we needed a way to identify the concrete type under the protocol abstraction.

In order for your types to support encoding behind a protocol we register the concrete type using Cereal.register, which takes the .Type of your type. Here's an example of a protocol-oriented type:

protocol Vehicle: IdentifyingCerealType {
    var make: String { get }
    var model: String { get }
    var description: String { get }
}

// Used for other implementers of Vehicle
private struct SharedKeys {
    static let make = "make"
    static let model = "model"
}

enum VehicleError: ErrorType {
    case MissingData
}

struct Car: Vehicle {
    private struct Keys {
        static let cylinders = "cylinders"
    }

    let make: String
    let model: String
    let cylinders: Int

    var description: String {
        return "\(model) by \(make) w/ \(cylinders) cylinders"
    }

    static let initializationIdentifier = "car"

    init(cereal: CerealDecoder) throws {
        guard let make: String = try cereal.decode(SharedKeys.make) else { throw VehicleError.MissingData }
        self.make = make

        guard let model: String = try cereal.decode(SharedKeys.model) else { throw VehicleError.MissingData }
        self.model = model

        guard let cylinders: Int = try cereal.decode(Keys.cylinders) else { throw VehicleError.MissingData }
        self.cylinders = cylinders
    }

    init(make: String, model: String, cylinders: Int) {
        self.make = make
        self.model = model
        self.cylinders = cylinders
    }

    func encodeWithCereal(inout cereal: CerealEncoder) throws {
        try cereal.encode(make, forKey: SharedKeys.make)
        try cereal.encode(model, forKey: SharedKeys.model)
        try cereal.encode(cylinders, forKey: Keys.cylinders)
    }
}

// This is where the magic happens!
Cereal.register(Car.self)

Phew! Thats how we can create an object to use in protocol referenced decoding, but how do we actually decode and encode it?

let vehicle: Vehicle = Car(make: "Foo", model: "Bar", cylinders: 8)

// Encoding
let encoder = CerealEncoder()
try encoder.encode(vehicle, forKey: "vehicle")

// Decoding
let decoder = CerealDecoder(data: encoder.toData())
let decodedVehicle: Vehicle = try decoder.decodeCereal("vehicle") as! Vehicle

Secure decoding with IdentifyingCerealType

In addition to allowing you to encode and decode protocols, you can use IdentifyingCerealType as a method to ensure the same type gets decoded as was encoded.

Handling dictionaries and arrays with protocol types

When working with protocol collections, such as [Vehicle], the compiler has issues encoding and decoding the types. To support these theres an extension on Array and Dictionary, CER_casted() which will cast the type into the appropriately expected type:

let decoder = try CerealDecoder(data: storedVehicleData)
let vehicles: [Vehicles] = try decoder.decodeIdentifyingCerealArray("vehicles")?.CER_casted() ?? []

Encoding requires a similar treatment:

var encoder = CerealEncoder()
try encoder.encodeIdentifyingItems(vehicles.CER_casted(), forKey: "vehicles")

There's also deepCast(_: [[KeyType: ValueType]) and deepArrayCast(_: [KeyType: [ValueType]) to handle arrays of dictionaries and dictionaries of arrays, respectively.

Serializing generics

Your generic types work the same was as the other serializable types. However, your generic types should constrain to CerealRepresentable:

struct Stack<ItemType: CerealRepresentable>: CerealType {
    private items: [ItemType]
    // ... rest of the implementation ...
}

From there you can encode and decode the object using the same methods.

Registering generic types

If your generic type is using IdentifyingCerealType you'll need to register the specialization in Cereal so it knows how to properly initialize your type. For example, if Stack had conformed to IdentifyingCerealType:

Cereal.register(Stack<Int>.self)

When you implement initializationIdentifier in your generic type it should also be based on the generic:

struct Stack<ItemType: CerealRepresentable>: IdentifyingCerealType {
    static var initializationIdentifier: String {
        return "Stack-\(ItemType.self)"
    }
    // ... rest of the implementation ...
}

RawRepresentable support

Adding serialization to RawRepresentable types never been easier, since they may be represented with their raw value. So, if you have an enum, or option set with a raw value of type that supports CerealRepresentable (like String, Bool, Int), then all you have to do is to add CerealRepresentable conformance to your RawRepresentable type and you're done. For example this RawRepresentable types:

enum Gender: Int {
    case Female
    case Male
}

struct EmployeeResponsibilites : OptionSetType {
    let rawValue: Int

    static let None           = OptionSet(rawValue: 0)
    static let CleanWorkplace = OptionSet(rawValue: 1 << 0)
    static let OpenWindow     = OptionSet(rawValue: 1 << 1)
    static let BringCofee     = OptionSet(rawValue: 1 << 2)
}

may be easily encoded and decoded by just adding CerealRepresentable conformance:

extension Gender: CerealRepresentable {}
extension EmployeeResponsibilites: CerealRepresentable {}

And encode/decode may looks something like this:

struct Employee {
    var gender: Gender = .Female
    var responsibilities: EmployeeResponsibilites = [.CleanWorkplace, .OpenWindow]
    init() { }
}

extension Employee: CerealType {
    private struct Keys {
        static let gender = "gender"
        static let responsibility = "responsibility"
    }

    init(decoder: CerealDecoder) throws {
        gender = try decoder.decode(Keys.gender) ?? .Female
        responsibility = try decoder.decode(Keys.responsibility) ?? .None
    }

    func encodeWithCereal(inout cereal: CerealEncoder) throws {
        try cereal.encode(gender, forKey: Keys.gender)
        try cereal.encode(responsibility, forKey: Keys.responsibility)
    }
}

Further reading

There are a lot of permutations that can be done with Cereal. I strongly encourage you to read the API, and if you have any questions about usage, check out our comprehensive test suite. It covers everything.

LICENSE

Copyright (c) 2017, Weebly All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Weebly nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Weebly, Inc BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Comments
  • Extremely slow encoding/decoding on a large number of items

    Extremely slow encoding/decoding on a large number of items

    I wrote an extension to serialize/deserialize dictionaries of cereal objects easily:

    extension Dictionary where Key: CerealRepresentable, Value: CerealType {
        func serialize(to filePath: String) throws {
            var encoder = CerealEncoder()
            try encoder.encode(self, forKey: "dictionary")
            let data = encoder.toData()
            try data.writeToFile(filePath, options: [.AtomicWrite])
        }
    
        mutating func deserialize(from filePath: String) throws {
            guard let data = NSData(contentsOfFile: filePath) else { throw SerializationError.FileNotExists }
    
            let decoder = try CerealDecoder(data: data)
            guard let result: [Key: Value] = try decoder.decodeCereal("dictionary") else { throw SerializationError.IncorrectData }
            self = result
        }
    }
    

    And I faced with a very frustrating fact: when number of items to be serialized/deserialized counts in the thousands, Cereal consumes CPU a lot. For example, when I serialize 8k records, it takes about 5 seconds to run on my 6+ device: instruments3 2016-04-13 00-19-38

    When I replaced all string[index1..<index2] with string.substringWithRange(index1..<index2) + prepared dictionary's capacity (very empirical estimation: assuming number of commas equals number of subitems):

    private static func parseEncodedCerealDictionaryString<DecodedKeyType: protocol<Hashable, CerealRepresentable>, DecodedValueType: CerealType>(encodedString: String) throws -> [DecodedKeyType: DecodedValueType] {
        let scanner = NSScanner(string: encodedString)
        scanner.charactersToBeSkipped = nil
        var scanResult: NSString?
        var approximateCapacity = 0
        while !scanner.atEnd {
            scanner.scanUpToString(",", intoString: &scanResult)
            approximateCapacity += 1
            scanner.scanString(",", intoString: nil)
        }
        var decodedItems = Dictionary<DecodedKeyType, DecodedValueType>(minimumCapacity: approximateCapacity)
        try encodedString.iterateEncodedValuesWithInstantationHandler { keyType, keyValue, type, value in
            let decodedKey: DecodedKeyType = try CerealDecoder.instantiate(keyValue, ofType: keyType)
            decodedItems[decodedKey] = try CerealDecoder.instantiateCereal(value, ofType: type) as DecodedValueType
        }
    
        return decodedItems
    }
    

    the picture changed a little bit, showing the real reason of high load: instruments3 2016-04-13 00-29-54

    There's too many CerealDecoder instantiations. Should cereal move to another implementation, maybe the one that is based on NSScanner, with a very little string reallocations count, with a decoder reuse instead of reallocating.

    Couldn't invent a way how to do that with backward compatibility with the existing format, maybe you can help me in pointing to the right way?

    Right now thinking of making a transformer to NSKeyedArchiver/from NSKeyedUnarchiver since the items in CerealEncoder are stored as strings.

    opened by Sega-Zero 21
  • Encoding optionals and NSDate

    Encoding optionals and NSDate

    I have following struct

    struct ItemDetail {
        var message:String?
        var date:NSDate?
        var location:String?
        var status:ItemStatus //enum
    
        init(message:String?, date:NSDate?, location:String?, status:ItemStatus) {
            self.message = message
            self.date = date
            self.location = location
            self.status = status
        }
    }
    

    However Cereal doesn't allow me to encode optionals. I don't want to check for nil or use default values. How can I use Cereal to encode/decode above struct? Thanks.

    enhancement 
    opened by tosbaha 12
  • Massive changes to improve encode/decode speed

    Massive changes to improve encode/decode speed

    Related to #16. This pull request breaks the backward compatibility, so there should be created a v2.0.0 tag.

    What have been done:

    1. Refactor encoder, use an indirect enum with associated values as a backing storage for data being encoded
    2. Remove all string-related code from both CerealEncoder and CerealDecoder and refactor all the code to use a new enum
    3. To improve the speed, internal dictionaries are replaced with arrays of tuples. This adds a bit more data into a result NSData if there is a value replacements during an encoding, but a decoder is guaranteed to decode only the last value.
    4. Introduced a new logic layer, CerealSerialization. It's function is to serialize/deserialize CoderTreeValue enum to/from NSData object. Right now, to achieve a better speed, it works with a raw-byte TLV structured data, but may be extended to any kind of data in the future: xml, json, or maybe even old string format to introduce a backward compatibility with 1.x versions.
    5. All the tests are rewritten to use a new TLV byte-arrays

    Known issues: Since the length of Int,Float and Double differs on x32 (Int==Int32) and x64 (Int==Int64) platforms, the result is incompatible between platforms. IMHO, there is no need in supporting this, all the new Apple devices are x64, the x32 devices will no longer be supported in a couple of years. Those who need a complete compatibility should use the 1.x version. The tests are written for an x64 devices.

    opened by Sega-Zero 5
  • Debugging serialization failure

    Debugging serialization failure

    I'm using Cereal to wire up some simple Swift struct serialization and deserialization to disk (in UserDefaults).

    Before I write the objects to disk, I began testing serialization in-memory, and am getting a weird failure from Cereal:

    // deviceRecordIn is a Swift struct which conforms to `CerealType`
    
            // When:
            // We serialize one in-memory.
            let encodingKey = "deviceRecord"
            var encoder = CerealEncoder()
            do {
                try encoder.encode(deviceRecordIn, forKey: encodingKey)
            } catch {
                XCTAssertTrue(false, "Error encoding data. \(error)")
            }
            let data = encoder.toData()
            XCTAssertNotNil(data)
    
            // Then:
            // We should be able to de-serialize it:
            do {
                let decoder = try CerealDecoder(data: data)
                let aRecord = try decoder.decode(encodingKey)
    
                // And it should match our serialized record.
                XCTAssertNotNil(aRecord)
                guard let encodedRecord = aRecord else {
                    return
                }
            } catch {
                XCTAssertTrue(false, "Error encoding data. \(error)")
            }
    

    My unit test fails at let aRecord = try decoder.decode(encodingKey), with:

    error: -[InspireTests.BluetoothPairingTest testThatWeCanSerializeAPairingRecord] : XCTAssertTrue failed - Error encoding data. InvalidEncoding("Failed to instantiate CerealTypeIdentifier with k")
    

    The call stack (sorry, this one is pretty verbose):

    (lldb) bt
    * Thread 1 (tid 0x43e9f0, queue "com.apple.main-thread"): 0x0000000115dade40 swift_willThrow
        Stopped by breakpoint 27.1
        #0  0x0000000115dade40 swift_willThrow
      * #1  0x000000011573720e static CerealDecoder.(encodedString="k,4:uuid:s,36:16B6F74C-8336-48A0-B833-F920C367D0CF:k,9:sensorUID:i,1:0:k,14:connectionDate:T,16:477003543.503419", index=Swift.String.Index @ 0x00007fff550b24c0, $error=ErrorType @ 0x00007fff550b2e60)(String, startingAtIndex : String.CharacterView.Index) throws -> (type : CerealTypeIdentifier, indexPassedValue : String.CharacterView.Index) + 1694 at CerealDecoder.swift:1149
        #2  0x0000000115738341 static CerealDecoder.(encodedString="k,4:uuid:s,36:16B6F74C-8336-48A0-B833-F920C367D0CF:k,9:sensorUID:i,1:0:k,14:connectionDate:T,16:477003543.503419", index=Swift.String.Index @ 0x00007fff550b2780, $error=ErrorType @ 0x00007fff550b2e60)(String, startingAtIndex : String.CharacterView.Index) throws -> (type : CerealTypeIdentifier, value : String, endIndex : String.CharacterView.Index) + 289 at CerealDecoder.swift:1159
        #3  0x000000011572880c String.iterateEncodedValuesWithInstantationHandler(instantiationHandler=0x000000011573f260 Cereal`partial apply forwarder for static Cereal.CerealDecoder.((parseEncodedArrayString in _5CB6B52C5402593B67F608A3C7D8E4BB) (Swift.String) throws -> Swift.Array<Cereal.CerealRepresentable>).(closure #1) at CerealDecoder.swift, self="k,4:uuid:s,36:16B6F74C-8336-48A0-B833-F920C367D0CF:k,9:sensorUID:i,1:0:k,14:connectionDate:T,16:477003543.503419", $error=ErrorType @ 0x00007fff550b2e60) throws -> ()) throws -> () + 636 at CerealDecoder.swift:1313
        #4  0x00000001157276f5 static CerealDecoder.(encodedString="k,4:uuid:s,36:16B6F74C-8336-48A0-B833-F920C367D0CF:k,9:sensorUID:i,1:0:k,14:connectionDate:T,16:477003543.503419", $error=ErrorType @ 0x00007fff550b2e60)(String) throws -> [CerealRepresentable] + 181 at CerealDecoder.swift:1176
        #5  0x0000000115727563 CerealDecoder.decode(key="deviceRecord", self=Cereal.CerealDecoder @ 0x00007fff550b2c70, $error=ErrorType @ 0x00007fff550b2e60) throws -> [CerealRepresentable]? + 563 at CerealDecoder.swift:137
        #6  0x00000001156f6f54 BluetoothPairingTest.testThatWeCanSerializeAPairingRecord(self=0x00007fa9fa5a7b70) -> () + 884 at BluetoothPairingTest.swift:138
        #7  0x00000001156f7992 @objc BluetoothPairingTest.testThatWeCanSerializeAPairingRecord() -> () + 34 at BluetoothPairingTest.swift:0
        #8  0x000000010b5b989c __invoking___ + 140
        #9  0x000000010b5b96ee -[NSInvocation invoke] + 286
        #10  0x000000010abdb5a7 __24-[XCTestCase invokeTest]_block_invoke_2 + 362
        #11  0x000000010ac0fb2b -[XCTestContext performInScope:] + 190
        #12  0x000000010abdb42c -[XCTestCase invokeTest] + 169
        #13  0x000000010abdba56 -[XCTestCase performTest:] + 459
        #14  0x000000010abd94cb -[XCTestSuite performTest:] + 396
        #15  0x000000010abd94cb -[XCTestSuite performTest:] + 396
        #16  0x000000010abd94cb -[XCTestSuite performTest:] + 396
        #17  0x000000010abc62e4 __25-[XCTestDriver _runSuite]_block_invoke + 51
        #18  0x000000010abe71f4 -[XCTestObservationCenter _observeTestExecutionForBlock:] + 640
        #19  0x000000010abc6229 -[XCTestDriver _runSuite] + 453
        #20  0x000000010abc6fa5 -[XCTestDriver _checkForTestManager] + 259
        #21  0x000000010ac10fb2 _XCTestMain + 628
        #22  0x000000010ab4d20f ___lldb_unnamed_function3$$xctest + 362
        #23  0x000000010dfaf92d start + 1
        #24  0x000000010dfaf92d start + 1
    (lldb) 
    

    Interestingly, my struct's extension of init(decoder cereal: CerealDecoder) is not called as part of decoding.

    Any pointers you can give to help me debug what's going wrong here?

    opened by cbowns 4
  • Built-in NSData support + thread safety for decoder

    Built-in NSData support + thread safety for decoder

    Added support for NSData built-in serialization. Think this is very helpful, when you cannot express your data in one of other standard types.

    Plus, same as in #20, missed the fact that a decoder contained a thread unsafe code

    opened by Sega-Zero 3
  • Automatic support for RawRepresentable types

    Automatic support for RawRepresentable types

    This pull request allows to shorten existing code for enums or OptionSetType, or any type that conforms to RawRepresentable protocol. With this we no longer need to write code like this:

    enum Gender: Int {
        case Female
        case Male
    }
    
    extension Gender: CerealType {
        private struct Keys {
            static let root = "gender"
        }
    
        init(decoder: CerealDecoder) throws {
            self.init(rawValue: try decoder.decode(Keys.root) ?? Gender.Female.rawValue)!
        }
    
        func encodeWithCereal(inout cereal: CerealEncoder) throws {
            try cereal.encode(rawValue, forKey: Keys.root)
        }
    }
    

    Instead we could write it like this:

    enum Gender: Int {
        case Female
        case Male
    }
    
    extension Gender: CerealRepresentable {}
    

    No force unwrapping, it just works. Now all you need is encode or decode enum variable just like a String or Int type:

    gender = try cereal.decode(Keys.gender) ?? .Female
    try cereal.encode(gender, forKey: Keys.gender)
    
    opened by Sega-Zero 3
  • Dictionary with CER_casted() has ambiguous overloads.

    Dictionary with CER_casted() has ambiguous overloads.

    Hey,

    i'm trying to encode a dictionary with variable value types. According to the Github Documentation one should cast the dictionary with .CER_casted() (since the value type is a protocol) before decoding/encoding. The decoding part works fine but for some reason i can't get the encoding to work. The Dictionary is of the type: [String: CerealType]

    Its decoded like this: guard let data:[String:CerealType] = try decoder.decode(dataKey)?.CER_casted() else { throw CodingError.MissingData } self.data = data

    and encoded like this:

    try encoder.encode(self.data.CER_casted(), forKey: dataKey);

    which fails with:

    Ambiguous Refrence to member 'encode(_:forKey:)

    Any Ideas?

    -- malte

    opened by sl33k 2
  • question: Encoding an enum with associated types

    question: Encoding an enum with associated types

    Hi! I've got a fun, maybe simple question for you about using Cereal with an enum:

    I've got a small class and an enum, which look like:

    enum UploadEnum {
        case Sending
        case Sent
        case Failed(error: NSError)
    }
    
    class DataClass {
        let name: String
        let upload: UploadEnum
    }
    

    I want to encode DataClass with Cereal, but… I don't know how, because of UploadEnum.

    With other enums without associated types, I've encoded the enum's rawValue, and then decoded it with the enum's .init(rawValue: <>) initializer.

    Can you think of any simple ways to encode UploadEnum? I can think of some invasive approaches which require changing the enum, but I'm seeking alternatives.

    opened by cbowns 2
  • More support for RawRepresentable (dictionaries, arrays)

    More support for RawRepresentable (dictionaries, arrays)

    In #13 there was only a support for a single RawRepresentable values and I noticed that I cannot use same automatic support for dictionary keys. So, this pull requests covers an arrays of RawRepresentable types and dictionaries. The whole point of both pull requests is to be able to add an extension to any RawRepresentable type with CerealRepresentable conformance in any swift module, not just a module where it is declared. This swift compiler bug made logic separation nearly impossible.

    I have a two questions:

    1. Made quite a lot of overloaded methods for RawRepresentables, should I separate them into an extension?
    2. Should I patch Readme.md to add a few lines about working with RawRepresentable?
    opened by Sega-Zero 2
  • NSDate losing accuracy

    NSDate losing accuracy

    Unclear whether this is due to a bug in Cereal of not, but encoding an NSDate and then decoding it seems to result in a slightly less accurate date, which then in turn causes isEqualToDate() to return false for the two dates. Here's a minimal test case to highlight the problem:

    let date = NSDate()
    let data = try! CerealEncoder.dataWithRootItem(date)
    let comparison: NSDate = try! CerealDecoder.rootItemWithData(data)
    XCTAssertTrue(date.isEqualToDate(comparison))
    

    The final line is not true, while logically it should be. When logging out info about the dates, they seem correct, but there is actually a problem with the sub-second part of the dates, Cereal seems to be dropping some accuracy:

    Original: 471798680.624343 After encode/decode: 471798680.62434
    
    opened by henrinormak 2
  • Refactor and fix compilation error under Xcode 7.3b3

    Refactor and fix compilation error under Xcode 7.3b3

    This didn't build for me in Xcode 7.3 beta 3: (it lacks a closing array bracket)

    throw CerealError.InvalidEncoding("Failed to instantiate CerealTypeIdentifier with \(encodedString[index ..< index.advancedBy(1))")
    

    I was, frankly, shocked that it built in 7.2.

    opened by cbowns 1
  • Do we need a migration tool to Codable?

    Do we need a migration tool to Codable?

    Swift 4 introduces new Codable protocol that generally do the same as this library. Should we make a migration tool for those who want to use a new protocol and be able to keep backward compatibility with Cereal?

    opened by Sega-Zero 1
  • Allow custom encoding functions for RawRepresentable enums

    Allow custom encoding functions for RawRepresentable enums

    Suppose the following:

    enum Gender: Int {
        case female
        case male
    }
    
    extension Gender: CerealType {
        // custom encode and decode
    }
    

    And encode was called on a variable of type Gender, then the RawRepresentable override version will be called, ignoring the custom serializers.

    I know it's not a huge issue, but it could be fixed by providing an encodeCereal function for example, to explicitly invoke the custom serializers.

    opened by tengyifei 0
  • Cereal failing to decode data that it encoded successfully

    Cereal failing to decode data that it encoded successfully

    Hi, I'm currently using Cereal 1.4.0 in a Swift 3 project on OS X ( I created an OS X target, which has been working fine for me so far, with the exception of this issue).

    I'm not sure if my recent upgrade to Swift 3 and Cereal 1.4.0 introduced this issue, or not, because I'm sure it worked previously but not sure exactly when it stopped working.

    Anyway, I'm serializing an array of Swift objects, in this case the array includes only one object.

    The error I'm getting is ".Array / .Cereal / .Dictionary not expected", so of course at first I thought the problem was that I was deserializing an array (even though that should be supported). However on closer inspection, the issue is with the decoding of a property in the object which is a dictionary. parseEncodedDictionaryString is being called, which calls iterateEncodedValues, which eventually calls parseEncodedDictionaryString. This then fails decoding the key, apparently because the key's type is .cereal.

    Here's all the data that's failing in parseEncodedDictionaryString:

    keyType = CerealOSX.CerealTypeIdentifier.cereal keyValue = "k,8:rawValue:s,4:more" type = CerealOSX.CerealTypeIdentifier.string value = "100"

    Any idea why this is failing? Also what does the key type of .cereal mean and why is that unexpected? I'm getting no errors during the encoding/serialization, only in decoding. Is this a bug in Cereal, or something I'm doing wrong?

    Any help is greatly appreciated, thanks!

    opened by einsteinx2 3
Owner
Weebly
Weebly
An enhancement built on top of Foundation Framework and XCTest.

Beton is a Swift library built on top of the Foundation framework, that provides an additional layer of functionality, including easy localization, performance test measurement support, and convenience functionality. For us, Beton is primarily, but not exclusively, useful for server-side Swift engineering.

21Gram Consulting 26 Dec 10, 2022
AnimeListSwiftUI - Anime quote list built with MVVM Swift 5 using Async/Await

How To In SwiftUI Async/Await AnimeListSwiftUI - Anime quote list built with MVVM Swift 5 using Async/Await Clones Clubhouse - App clone built with Sw

Rogério Toledo 3 Nov 2, 2022
🟣 Verge is a very tunable state-management engine on iOS App (UIKit / SwiftUI) and built-in ORM.

Verge is giving the power of state-management in muukii/Brightroom v2 development! Verge.swift ?? An effective state management architecture for iOS -

VergeGroup 478 Dec 29, 2022
Unit-Converter-SwiftUI - A simple Unit Converter iOS app built in the process of learning SwiftUI

SwiftUI-Unit-Converter A simple Unit Converter iOS app built in the process of l

Ishaan Bedi 2 Jul 13, 2022
💻 A fast and flexible O(n) difference algorithm framework for Swift collection.

A fast and flexible O(n) difference algorithm framework for Swift collection. The algorithm is optimized based on the Paul Heckel's algorithm. Made wi

Ryo Aoyama 3.3k Jan 4, 2023
RandomKit is a Swift framework that makes random data generation simple and easy.

RandomKit is a Swift framework that makes random data generation simple and easy. Build Status Installation Compatibility Swift Package Manager CocoaP

Nikolai Vazquez 1.5k Dec 29, 2022
A Swift micro-framework to easily deal with weak references to self inside closures

WeakableSelf Context Closures are one of Swift must-have features, and Swift developers are aware of how tricky they can be when they capture the refe

Vincent Pradeilles 72 Sep 1, 2022
An extensible monitoring framework written in Swift

XestiMonitors Overview Reference Documentation Requirements Installation CocoaPods Carthage Swift Package Manager Usage Core Location Monitors Core Mo

J. G. Pusey 271 Oct 5, 2022
❇️ Feature flagging framework in Swift sauce

RealFlags makes it easy to configure feature flagsin your codebase. It's designed for Swift and provides a simple and elegant abstraction layer over m

Immobiliare Labs 162 Jan 1, 2023
Framework for easily parsing your JSON data directly to Swift object.

Server sends the all JSON data in black and white format i.e. its all strings & we make hard efforts to typecast them into their respective datatypes

Mukesh 11 Oct 17, 2022
Swift 3 framework for accessing data in Event Registry

Swift 3 framework for accessing data in Event Registry

Pavel Pantus 8 Nov 1, 2016
xxHash framework in Swift.

xxHash-Swift xxHash framework in Swift. Original xxHash algorithm created by Yann Collet. Currently only suppport XXH3-64. It currently points to the

Shuaihu 2 Oct 18, 2022
Vaccine is a framework that aims to make your apps immune to recompile-disease.

Vaccine Description Vaccine is a framework that aims to make your apps immune to recompile-disease. Vaccine provides a straightforward way to make you

Christoffer Winterkvist 298 Dec 23, 2022
Merges a given number of PDF files into one file using the PDFKit framework

Titanium iOS PDF Merge Merges a given number of PDF files into one file using the PDFKit framework Requirements iOS 11+ Titanium SDK 9+ API's Methods

Hans Knöchel 6 Jan 26, 2022
A set of utilities (vmcli + vmctl) for macOS Virtualization.framework

VMCLI A set of utilities to help you manage VMs with Virtualization.framework Installation Prerequisites macOS Big Sur (11+) XCode.app installed # mak

Yifan Gu 771 Dec 24, 2022
SwiftyUpdateKit is a framework for iOS and macOS.

SwiftyUpdateKit is a framework for iOS and macOS. This framework supports for a user to update your app when new app version is released on the App Store.

Hituzi Ando 4 Aug 24, 2022
Another Virtualization.framework demo project, with focus to iBoot (WIP)

Virtual iBoot Fun This is just another Virtualization.framework sample project (WIP), but with focus on iBoot (iOS/macOS/tvOS/etc. bootloader) For a m

john 119 Dec 7, 2022
This iOS framework allows settings to be in-app in addition to or instead of being in the Settings app.

InAppSettingsKit InAppSettingsKit (IASK) is an open source framework to easily add in-app settings to your iOS or Catalyst apps. Normally iOS apps use

Ortwin Gentz, FutureTap 3.1k Jan 2, 2023