-
-
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 | |
} | |
} | |
} |
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
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!
I hava a same problem, public convenience init(from decoder: Decoder) throws
never called.. how to fix this problem? thanks.
Maybe it's because the property is let
, it should be var
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
I'm struggling with the same issue. The init method is never called for me either.
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?
@sofbix 's solution worked for me
@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 !
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
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?