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 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