Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
//
// RealmSwift+Codable.swift
//
// Created by Michael Gray on 8/16/17.
//
import Foundation
import RealmSwift
// swiftlint:disable line_length identifier_name
// stolen functions from the Swift stdlib
// https://github.com/apple/swift/blob/2e5817ebe15b8c2fc2459e08c1d462053cbb9a99/stdlib/public/core/Codable.swift
//
func assertTypeIsEncodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
guard T.self is Encodable.Type else {
if T.self == Encodable.self || T.self == Codable.self {
preconditionFailure("\(wrappingType) does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.")
} else {
preconditionFailure("\(wrappingType) does not conform to Encodable because \(T.self) does not conform to Encodable.")
}
}
}
func assertTypeIsDecodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
guard T.self is Decodable.Type else {
if T.self == Decodable.self || T.self == Codable.self {
preconditionFailure("\(wrappingType) does not conform to Decodable because Decodable does not conform to itself. You must use a concrete type to encode or decode.")
} else {
preconditionFailure("\(wrappingType) does not conform to Decodable because \(T.self) does not conform to Decodable.")
}
}
}
extension Encodable {
func __encode(to container: inout SingleValueEncodingContainer) throws { try container.encode(self) }
func __encode(to container: inout UnkeyedEncodingContainer) throws { try container.encode(self) }
func __encode<Key>(to container: inout KeyedEncodingContainer<Key>, forKey key: Key) throws { try container.encode(self, forKey: key) }
}
extension Decodable {
// Since we cannot call these __init, we'll give the parameter a '__'.
fileprivate init(__from container: SingleValueDecodingContainer) throws { self = try container.decode(Self.self) }
fileprivate init(__from container: inout UnkeyedDecodingContainer) throws { self = try container.decode(Self.self) }
fileprivate init<Key>(__from container: KeyedDecodingContainer<Key>, forKey key: Key) throws { self = try container.decode(Self.self, forKey: key) }
}
extension RealmOptional : Encodable /* where Wrapped : Encodable */ {
public func encode(to encoder: Encoder) throws {
assertTypeIsEncodable(T.self, in: type(of: self))
var container = encoder.singleValueContainer()
if let v = self.value {
try (v as! Encodable).encode(to: encoder) // swiftlint:disable:this force_cast
} else {
try container.encodeNil()
}
}
}
extension RealmOptional : Decodable /* where Wrapped : Decodable */ {
public convenience init(from decoder: Decoder) throws {
// Initialize self here so we can get type(of: self).
self.init()
assertTypeIsDecodable(T.self, in: type(of: self))
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
let metaType = (T.self as! Decodable.Type) // swiftlint:disable:this force_cast
let element = try metaType.init(from: decoder)
self.value = (element as! T) // swiftlint:disable:this force_cast
}
}
}
extension List : Decodable /* where Element : Decodable */ {
public convenience init(from decoder: Decoder) throws {
// Initialize self here so we can get type(of: self).
self.init()
assertTypeIsDecodable(T.self, in: type(of: self))
let metaType = (T.self as! Decodable.Type) // swiftlint:disable:this force_cast
var container = try decoder.unkeyedContainer()
while !container.isAtEnd {
let element = try metaType.init(__from: &container)
self.append(element as! Element) // swiftlint:disable:this force_cast
}
}
}
extension List : Encodable /* where Element : Decodable */ {
public func encode(to encoder: Encoder) throws {
assertTypeIsEncodable(T.self, in: type(of: self))
var container = encoder.unkeyedContainer()
for element in self {
// superEncoder appends an empty element and wraps an Encoder around it.
// This is normally appropriate for encoding super, but this is really what we want to do.
let subencoder = container.superEncoder()
try (element as! Encodable).encode(to: subencoder) // swiftlint:disable:this force_cast
}
}
}
@RVijay007
Copy link

RVijay007 commented Feb 25, 2018

I'm getting errors with this code saying that each of the extensions doesn't know what 'T' is. Any ideas on how to fix?

@h1m5
Copy link

h1m5 commented Apr 5, 2018

I fix the 'T' errors this way:



extension RealmOptional : Encodable where Value : Encodable {
    public func encode(to encoder: Encoder) throws {
        assertTypeIsEncodable(Value.self, in: type(of: self))

        var container = encoder.singleValueContainer()
        if let v = self.value {
            try (v as Encodable).encode(to: encoder)  // swiftlint:disable:this force_cast
        } else {
            try container.encodeNil()
        }
    }
}

extension RealmOptional : Decodable where Value : Decodable {
    public convenience init(from decoder: Decoder) throws {
        // Initialize self here so we can get type(of: self).
        self.init()
        assertTypeIsDecodable(Value.self, in: type(of: self))
        
        let container = try decoder.singleValueContainer()
        if !container.decodeNil() {
            let metaType = (Value.self as Decodable.Type) // swiftlint:disable:this force_cast
            let element = try metaType.init(from: decoder)
            self.value = (element as! Value)  // swiftlint:disable:this force_cast
        }
    }
}

extension List : Decodable where Element : Decodable {
    public convenience init(from decoder: Decoder) throws {
        // Initialize self here so we can get type(of: self).
        self.init()
        assertTypeIsDecodable(Element.self, in: type(of: self))

        let metaType = (Element.self as Decodable.Type) // swiftlint:disable:this force_cast
        var container = try decoder.unkeyedContainer()
        while !container.isAtEnd {
            let element = try metaType.init(__from: &container)
            self.append(element as! Element) // swiftlint:disable:this force_cast
        }
    }
}

extension List : Encodable where Element : Decodable {
    public func encode(to encoder: Encoder) throws {
        assertTypeIsEncodable(Element.self, in: type(of: self))

        var container = encoder.unkeyedContainer()
        for element in self {
            // superEncoder appends an empty element and wraps an Encoder around it.
            // This is normally appropriate for encoding super, but this is really what we want to do.
            let subencoder = container.superEncoder()
            try (element as! Encodable).encode(to: subencoder) // swiftlint:disable:this force_cast
        }
    }
}

and it works flawlessly. Repeat this for the other functions

@tiborr
Copy link

tiborr commented Jul 20, 2018

For me, the public convenience init(from decoder: Decoder) throws function never runs, which makes the stored value nil every time I decode the data and no errors are thrown. Do you know why this might be? Thanks!

@thekan23
Copy link

thekan23 commented Jul 22, 2018

I hava a same problem, public convenience init(from decoder: Decoder) throws never called.. how to fix this problem? thanks.

@Evyasafmordechai
Copy link

Evyasafmordechai commented Jul 30, 2018

Maybe it's because the property is let, it should be var

@sofbix
Copy link

sofbix commented Jul 30, 2018

For me, the public convenience init(from decoder: Decoder) throws function never runs, which makes the stored value nil every time I decode the data and no errors are thrown. Do you know why this might be? Thanks!

Probably you try to use let:
let v = RealmOptional<Bool>()
You can try use var:
private(set) var v = RealmOptional<Bool>()
then if this field have not went you will come exception. This field should get with value "null" at least

@vilhelmst
Copy link

vilhelmst commented Aug 2, 2018

I'm struggling with the same issue. The init method is never called for me either.

@srv7
Copy link

srv7 commented Aug 8, 2018

import Foundation
import RealmSwift

// swiftlint:disable line_length identifier_name
// stolen functions from the Swift stdlib
// https://github.com/apple/swift/blob/2e5817ebe15b8c2fc2459e08c1d462053cbb9a99/stdlib/public/core/Codable.swift
//
func assertTypeIsEncodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
    guard T.self is Encodable.Type else {
        if T.self == Encodable.self || T.self == Codable.self {
            preconditionFailure("\(wrappingType) does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.")
        } else {
            preconditionFailure("\(wrappingType) does not conform to Encodable because \(T.self) does not conform to Encodable.")
        }
    }
}

func assertTypeIsDecodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
    guard T.self is Decodable.Type else {
        if T.self == Decodable.self || T.self == Codable.self {
            preconditionFailure("\(wrappingType) does not conform to Decodable because Decodable does not conform to itself. You must use a concrete type to encode or decode.")
        } else {
            preconditionFailure("\(wrappingType) does not conform to Decodable because \(T.self) does not conform to Decodable.")
        }
    }
}

extension Encodable {
    func __encode(to container: inout SingleValueEncodingContainer) throws { try container.encode(self) }
    func __encode(to container: inout UnkeyedEncodingContainer)     throws { try container.encode(self) }
    func __encode<Key>(to container: inout KeyedEncodingContainer<Key>, forKey key: Key) throws { try container.encode(self, forKey: key) }
}

extension Decodable {
    // Since we cannot call these __init, we'll give the parameter a '__'.
    fileprivate init(__from container: SingleValueDecodingContainer)   throws { self = try container.decode(Self.self) }
    fileprivate init(__from container: inout UnkeyedDecodingContainer) throws { self = try container.decode(Self.self) }
    fileprivate init<Key>(__from container: KeyedDecodingContainer<Key>, forKey key: Key) throws { self = try container.decode(Self.self, forKey: key) }
}


extension RealmOptional : Encodable where Value : Encodable {
    public func encode(to encoder: Encoder) throws {
        assertTypeIsEncodable(Value.self, in: type(of: self))
        
        var container = encoder.singleValueContainer()
        if let v = self.value {
            try (v as! Encodable).encode(to: encoder)  // swiftlint:disable:this force_cast
        } else {
            try container.encodeNil()
        }
    }
}

extension RealmOptional : Decodable where Value : Decodable {
    public convenience init(from decoder: Decoder) throws {
        // Initialize self here so we can get type(of: self).
        self.init()
        assertTypeIsDecodable(Value.self, in: type(of: self))
        
        let container = try decoder.singleValueContainer()
        if !container.decodeNil() {
            let metaType = (Value.self as! Decodable.Type) // swiftlint:disable:this force_cast
            let element = try metaType.init(from: decoder)
            self.value = (element as! Value)  // swiftlint:disable:this force_cast
        }
    }
}
extension List : Decodable where Element : Decodable {
    public convenience init(from decoder: Decoder) throws {
        // Initialize self here so we can get type(of: self).
        self.init()
        assertTypeIsDecodable(Element.self, in: type(of: self))
        
        let metaType = (Element.self as! Decodable.Type) // swiftlint:disable:this force_cast
        let container = try? decoder.unkeyedContainer()
        if var container = container {
            while !container.isAtEnd {
                let element = try metaType.init(__from: &container)
                self.append(element as! Element) // swiftlint:disable:this force_cast
            }
        } else {
            
        }
    }
}

extension List : Encodable where Element : Decodable { 
    public func encode(to encoder: Encoder) throws {
        assertTypeIsEncodable(Element.self, in: type(of: self))
        
        var container = encoder.unkeyedContainer()
        for element in self {
            // superEncoder appends an empty element and wraps an Encoder around it.
            // This is normally appropriate for encoding super, but this is really what we want to do.
            let subencoder = container.superEncoder()
            try (element as! Encodable).encode(to: subencoder) // swiftlint:disable:this force_cast
        }
    }
}
import Foundation
import RealmSwift

@objcMembers
class Book: Object, Decodable {
    dynamic var name: String = ""
    private(set) var optionalBool = RealmOptional<Bool>()
}

@objcMembers
class File: Object, Decodable {
    dynamic var string: String = ""
    dynamic var optionalString: String?
    dynamic var int: Int = 0
    private(set) var optionalInt = RealmOptional<Int>()
    dynamic var bool: Bool = false
    dynamic private(set) var optionalBool = RealmOptional<Bool>()
    dynamic var optionalData: Data?
    dynamic var optionalDate: Date?
    dynamic var float: Float = 0.0
    private(set) var optionalFloat = RealmOptional<Float>()
    dynamic var book: Book?
    dynamic var book2: Book?
    private(set) var list = List<Book>()
    private(set) var nullList = List<Int>()
}
let jsonData = """
{
    "string": "struggle",
    "optionalString": null,
    "int": 1,
    "optionalInt": null,
    "bool": true,
    "optionalBool": false,
    "optionalData": null,
    "optionalDate": null,
    "float": 2.1,
    "optionalFloat":null,
    "book": {
        "name": "sixsixsix",
        "optionalBool": false
    },
    "book2": null,
    "list": [
        {
            "name": "srv7",
            "optionalBool": false
        }
    ],
    "nullList": null
}
""".data(using: .utf8)


do {
    let x = try JSONDecoder().decode(File.self, from: jsonData!)
    print(x)
} catch let e {
    print(e)
}

this should works fine.

And one question:
with the entension to List:

extension List : Encodable where Element : Decodable {
 ...
}

Why is Decodable instead of Encodable?

@tiborr
Copy link

tiborr commented Aug 23, 2018

@sofbix 's solution worked for me

@X901
Copy link

X901 commented May 12, 2019

@objcMembers is very important

I spend hours searching for solution
because ,it always saved as Null in Realm
but when I printed it, I get value !

this only happens with String and any non-Optional value
after added @objcMembers it finally worked !

and if you use Realm Optional you must use @sofbix solution
change let to private(set) var

Thank you @srv7 and @sofbix and also @mishagray for this amazing Code !

@andreaagudo3
Copy link

andreaagudo3 commented Feb 6, 2020

Conformance of 'RealmOptional' to protocol 'Encodable' conflicts with that stated in the type's module 'RealmSwift' and will be ignored; there cannot be more than one conformance, even with different conditional bounds

Is there any way i can fix this? I am getting this warning

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment