Ensure LazilyDecoding runs on the managed object context's thread
Maybe fix the crash in KeyPath machinery?
This commit is contained in:
parent
bc7500bde9
commit
b40d815274
|
@ -7,12 +7,13 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
private let decoder = PropertyListDecoder()
|
private let decoder = PropertyListDecoder()
|
||||||
private let encoder = PropertyListEncoder()
|
private let encoder = PropertyListEncoder()
|
||||||
|
|
||||||
@propertyWrapper
|
@propertyWrapper
|
||||||
public struct LazilyDecoding<Enclosing: NSObject, Value: Codable> {
|
public struct LazilyDecoding<Enclosing: NSManagedObject, Value: Codable> {
|
||||||
|
|
||||||
private let keyPath: ReferenceWritableKeyPath<Enclosing, Data?>
|
private let keyPath: ReferenceWritableKeyPath<Enclosing, Data?>
|
||||||
private let fallback: Value
|
private let fallback: Value
|
||||||
|
@ -32,37 +33,41 @@ public struct LazilyDecoding<Enclosing: NSObject, Value: Codable> {
|
||||||
|
|
||||||
public static subscript(_enclosingInstance instance: Enclosing, wrapped wrappedKeyPath: ReferenceWritableKeyPath<Enclosing, Value>, storage storageKeyPath: ReferenceWritableKeyPath<Enclosing, Self>) -> Value {
|
public static subscript(_enclosingInstance instance: Enclosing, wrapped wrappedKeyPath: ReferenceWritableKeyPath<Enclosing, Value>, storage storageKeyPath: ReferenceWritableKeyPath<Enclosing, Self>) -> Value {
|
||||||
get {
|
get {
|
||||||
var wrapper = instance[keyPath: storageKeyPath]
|
instance.performOnContext {
|
||||||
if let value = wrapper.value {
|
var wrapper = instance[keyPath: storageKeyPath]
|
||||||
return value
|
if let value = wrapper.value {
|
||||||
} else {
|
return value
|
||||||
guard let data = instance[keyPath: wrapper.keyPath] else { return wrapper.fallback }
|
} else {
|
||||||
do {
|
guard let data = instance[keyPath: wrapper.keyPath] else { return wrapper.fallback }
|
||||||
let value = try decoder.decode(Box.self, from: data)
|
do {
|
||||||
wrapper.value = value.value
|
let value = try decoder.decode(Box.self, from: data)
|
||||||
wrapper.observation = instance.observe(wrapper.keyPath, changeHandler: { instance, _ in
|
wrapper.value = value.value
|
||||||
var wrapper = instance[keyPath: storageKeyPath]
|
wrapper.observation = instance.observe(wrapper.keyPath, changeHandler: { instance, _ in
|
||||||
if wrapper.skipClearingOnNextUpdate {
|
var wrapper = instance[keyPath: storageKeyPath]
|
||||||
wrapper.skipClearingOnNextUpdate = false
|
if wrapper.skipClearingOnNextUpdate {
|
||||||
} else {
|
wrapper.skipClearingOnNextUpdate = false
|
||||||
wrapper.removeCachedValue()
|
} else {
|
||||||
}
|
wrapper.removeCachedValue()
|
||||||
|
}
|
||||||
|
instance[keyPath: storageKeyPath] = wrapper
|
||||||
|
})
|
||||||
instance[keyPath: storageKeyPath] = wrapper
|
instance[keyPath: storageKeyPath] = wrapper
|
||||||
})
|
return value.value
|
||||||
instance[keyPath: storageKeyPath] = wrapper
|
} catch {
|
||||||
return value.value
|
return wrapper.fallback
|
||||||
} catch {
|
}
|
||||||
return wrapper.fallback
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
var wrapper = instance[keyPath: storageKeyPath]
|
instance.performOnContext {
|
||||||
wrapper.value = newValue
|
var wrapper = instance[keyPath: storageKeyPath]
|
||||||
wrapper.skipClearingOnNextUpdate = true
|
wrapper.value = newValue
|
||||||
instance[keyPath: storageKeyPath] = wrapper
|
wrapper.skipClearingOnNextUpdate = true
|
||||||
let newData = try! encoder.encode(Box(value: newValue))
|
instance[keyPath: storageKeyPath] = wrapper
|
||||||
instance[keyPath: wrapper.keyPath] = newData
|
let newData = try! encoder.encode(Box(value: newValue))
|
||||||
|
instance[keyPath: wrapper.keyPath] = newData
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +78,16 @@ public struct LazilyDecoding<Enclosing: NSObject, Value: Codable> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension NSManagedObject {
|
||||||
|
fileprivate func performOnContext<V>(_ f: () -> V) -> V {
|
||||||
|
if let managedObjectContext {
|
||||||
|
managedObjectContext.performAndWait(f)
|
||||||
|
} else {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension LazilyDecoding {
|
extension LazilyDecoding {
|
||||||
init<T>(arrayFrom keyPath: ReferenceWritableKeyPath<Enclosing, Data?>) where Value == [T] {
|
init<T>(arrayFrom keyPath: ReferenceWritableKeyPath<Enclosing, Data?>) where Value == [T] {
|
||||||
self.init(from: keyPath, fallback: [])
|
self.init(from: keyPath, fallback: [])
|
||||||
|
|
Loading…
Reference in New Issue