Using generics to simplify persisting codable collections
By Ryan Romanchuk
On
On
import JapxCodable
import RealmSwift
import Alamofire
protocol Serviceable {
typealias CollectionCallback = (([DecodableType]?, Error?) -> Void)?
typealias ObjectCallback = ((DecodableType?, Error?) -> Void)?
typealias PaginationCallback = ((_ collection: [DecodableType]?, _ moreResults: Bool, _ error: Error?) -> Void)?
typealias ObjectDataResponse = AFDataResponse<DecodableType>
typealias CollectionDataResponse = AFDataResponse<JapxResponse<[DecodableType]>>
associatedtype RealmType: Object
associatedtype DecodableType: JapxDecodable
}
protocol WithSaveHooks {
func afterCreate()
}
extension Serviceable {
/**
This method takes a collection of `[RealmType]` resources in the form of a `[JapxDecodable]`, and then creates
or updates the Realm object, calls the completion block and then returns the newly saved managed realm object.
- warning: This function will operate and return on the same thread it was called on
- parameter [RealmType]: The Codable representation
- returns: Discardable `(realm, [RealmType])` tuple with realm instance and collection as managed object
*/
@discardableResult
static func success<K: Any>(_ unmanaged: [K]) -> (Realm, [RealmType]) {
try! autoreleasepool {
var managedObjects = [RealmType]()
do {
let realm = try Realm()
realm.beginWrite()
unmanaged.forEach { (unmanagedObject) in
let object = realm.create(RealmType.self, value: unmanagedObject, update: .modified)
(object as? WithSaveHooks)?.afterCreate()
managedObjects.append(object)
}
try realm.commitWrite()
return (realm, managedObjects)
} catch let error {
log.error(error)
SentrySDK.capture(error: error)
throw error
}
}
}
}
class CheckIn: Object, JapxDecodable, WithSaveHooks {
enum CodingKeys: String, CodingKey {
case id
}
@Persisted(primaryKey: true) var id: String
}