Skip to content

Instantly share code, notes, and snippets.

@mishagray
Created August 16, 2017 16:16
Show Gist options
  • Star 33 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mishagray/3ee82a3a82f357bfbf8ff3b3d9eca5cd to your computer and use it in GitHub Desktop.
Save mishagray/3ee82a3a82f357bfbf8ff3b3d9eca5cd to your computer and use it in GitHub Desktop.
//
// 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
}
}
}
@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

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