Revert ObjectMonitor to previous implementation

This commit is contained in:
John Estropia
2019-10-16 19:20:11 +09:00
parent 64a0264354
commit 2818a778a4
9 changed files with 95 additions and 114 deletions

View File

@@ -16,13 +16,13 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
func setPalette<O: ObjectRepresentation>(_ newValue: O?) where O.ObjectType == Palette {
guard self.monitor?.objectID() != newValue?.objectID() else {
guard self.monitor?.object?.objectID() != newValue?.objectID() else {
return
}
if let newValue = newValue {
self.monitor = newValue.asObjectMonitor(in: ColorsDemo.stack)
self.monitor = newValue.asReadOnly(in: ColorsDemo.stack).map(ColorsDemo.stack.monitorObject(_:))
}
else {

View File

@@ -73,9 +73,30 @@ extension Palette {
return "H: \(self.hue.value)˚, S: \(round(self.saturation.value * 100.0))%, B: \(round(self.brightness.value * 100.0))%"
}
}
extension LiveObject where ObjectType == Palette {
var color: UIColor {
return UIColor(
hue: CGFloat(self.hue) / 360.0,
saturation: CGFloat(self.saturation),
brightness: CGFloat(self.brightness),
alpha: 1.0
)
}
var colorText: String {
return "H: \(self.hue)˚, S: \(round(self.saturation * 100.0))%, B: \(round(self.brightness * 100.0))%"
}
}
extension Palette {
func setInitialValues(in transaction: BaseDataTransaction) {
self.hue .= Palette.randomHue()
self.saturation .= Float(1.0)
self.brightness .= Float(arc4random_uniform(70) + 30) / 100.0

View File

@@ -20,22 +20,6 @@ struct SwiftUIView: View {
@ObservedObject
var palettes: LiveList<Palette>
@available(iOS 13.0.0, *)
struct ColorCell: View {
@ObservedObject
var palette: LiveObject<Palette>
var body: some View {
HStack {
Color(palette.color)
.cornerRadius(5)
.frame(width: 30, height: 30, alignment: .leading)
Text(palette.colorText)
}
}
}
var body: some View {
NavigationView {
List {
@@ -124,6 +108,22 @@ struct SwiftUIView: View {
private var needsShowAlert = false
}
@available(iOS 13.0.0, *)
struct ColorCell: View {
@ObservedObject
var palette: LiveObject<Palette>
var body: some View {
HStack {
Color(palette.color)
.cornerRadius(5)
.frame(width: 30, height: 30, alignment: .leading)
Text(palette.colorText)
}
}
}
@available(iOS 13.0.0, *)
struct DetailView: View {

View File

@@ -42,7 +42,7 @@ extension DiffableDataSource {
public typealias ObjectType = O
@nonobjc
public init(collectionView: UICollectionView, dataStack: DataStack, cellProvider: @escaping (UICollectionView, IndexPath, ObjectType) -> UICollectionViewCell?, supplementaryViewProvider: @escaping (UICollectionView, String, IndexPath) -> UICollectionReusableView? = { _, _, _ in nil }) {
public init(collectionView: UICollectionView, dataStack: DataStack, cellProvider: @escaping (UICollectionView, IndexPath, O) -> UICollectionViewCell?, supplementaryViewProvider: @escaping (UICollectionView, String, IndexPath) -> UICollectionReusableView? = { _, _, _ in nil }) {
self.collectionView = collectionView
self.cellProvider = cellProvider
@@ -77,7 +77,7 @@ extension DiffableDataSource {
collectionView.dataSource = self
}
public func apply(_ snapshot: ListSnapshot<ObjectType>, animatingDifferences: Bool = true) {
public func apply(_ snapshot: ListSnapshot<O>, animatingDifferences: Bool = true) {
let diffableSnapshot = snapshot.diffableSnapshot
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
@@ -199,7 +199,7 @@ extension DiffableDataSource {
private weak var collectionView: UICollectionView?
private let dataStack: DataStack
private let cellProvider: (UICollectionView, IndexPath, ObjectType) -> UICollectionViewCell?
private let cellProvider: (UICollectionView, IndexPath, O) -> UICollectionViewCell?
private let supplementaryViewProvider: (UICollectionView, String, IndexPath) -> UICollectionReusableView?
private var rawDataSource: Any!

View File

@@ -48,7 +48,7 @@ extension DiffableDataSource {
public typealias ObjectType = O
@nonobjc
public init(tableView: UITableView, dataStack: DataStack, cellProvider: @escaping (UITableView, IndexPath, ObjectType) -> UITableViewCell?) {
public init(tableView: UITableView, dataStack: DataStack, cellProvider: @escaping (UITableView, IndexPath, O) -> UITableViewCell?) {
self.tableView = tableView
self.cellProvider = cellProvider
@@ -82,7 +82,7 @@ extension DiffableDataSource {
tableView.dataSource = self
}
public func apply(_ snapshot: ListSnapshot<ObjectType>, animatingDifferences: Bool = true) {
public func apply(_ snapshot: ListSnapshot<O>, animatingDifferences: Bool = true) {
let diffableSnapshot = snapshot.diffableSnapshot
// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
@@ -221,7 +221,7 @@ extension DiffableDataSource {
private weak var tableView: UITableView?
private let dataStack: DataStack
private let cellProvider: (UITableView, IndexPath, ObjectType) -> UITableViewCell?
private let cellProvider: (UITableView, IndexPath, O) -> UITableViewCell?
private var rawDataSource: Any!
// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)

View File

@@ -88,10 +88,15 @@ public final class LiveObject<O: DynamicObject>: ObjectRepresentation, Hashable
}
return Self.init(objectID: self.id, context: context)
}
public func asReadOnly(in dataStack: DataStack) -> O? {
return dataStack.unsafeContext().fetchExisting(self.id)
}
public func asEditable(in transaction: BaseDataTransaction) -> O? {
return self.context.fetchExisting(self.id)
return transaction.unsafeContext().fetchExisting(self.id)
}
public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<O>? {
@@ -113,11 +118,6 @@ public final class LiveObject<O: DynamicObject>: ObjectRepresentation, Hashable
}
return .init(objectID: self.id, context: context)
}
public func asObjectMonitor(in dataStack: DataStack) -> ObjectMonitor<O>? {
return .init(objectID: self.id, context: dataStack.unsafeContext())
}
// MARK: Equatable
@@ -354,12 +354,4 @@ extension LiveObject where O: CoreStoreObject {
return self.snapshot[dynamicMember: member]
}
/**
Returns the value for the property identified by a given key.
*/
public subscript<T>(dynamicMember member: KeyPath<O, T>) -> T {
return self.object[keyPath: member]
}
}

View File

@@ -40,17 +40,22 @@ import CoreData
Observers registered via `addObserver(_:)` are not retained. `ObjectMonitor` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles.
*/
@available(macOS 10.12, *)
public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equatable {
public final class ObjectMonitor<O: DynamicObject>: Equatable {
/**
The object type represented by this `ObjectMonitor`
*/
public typealias ObjectType = O
/**
Returns the `DynamicObject` instance being observed, or `nil` if the object was already deleted.
*/
public var object: ObjectType? {
public var object: O? {
return self.fetchedResultsController
.fetchedObjects?
.first
.flatMap({ ObjectType.cs_fromRaw(object: $0) })
.flatMap({ O.cs_fromRaw(object: $0) })
}
/**
@@ -72,7 +77,7 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
- parameter observer: an `ObjectObserver` to send change notifications to
*/
public func addObserver<U: ObjectObserver>(_ observer: U) where U.ObjectEntityType == ObjectType {
public func addObserver<U: ObjectObserver>(_ observer: U) where U.ObjectEntityType == O {
self.unregisterObserver(observer)
self.registerObserver(
@@ -99,7 +104,7 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
- parameter observer: an `ObjectObserver` to unregister notifications to
*/
public func removeObserver<U: ObjectObserver>(_ observer: U) where U.ObjectEntityType == ObjectType {
public func removeObserver<U: ObjectObserver>(_ observer: U) where U.ObjectEntityType == O {
self.unregisterObserver(observer)
}
@@ -120,50 +125,9 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
public let userInfo = UserInfo()
// MARK: ObjectRepresentation
public func objectID() -> O.ObjectID {
return self.id
}
public func asLiveObject(in dataStack: DataStack) -> LiveObject<O>? {
let context = dataStack.unsafeContext()
return .init(objectID: self.id, context: context)
}
public func asEditable(in transaction: BaseDataTransaction) -> O? {
return self.context.fetchExisting(self.id)
}
public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<O>? {
let context = dataStack.unsafeContext()
return .init(objectID: self.id, context: context)
}
public func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot<O>? {
let context = transaction.unsafeContext()
return .init(objectID: self.id, context: context)
}
public func asObjectMonitor(in dataStack: DataStack) -> ObjectMonitor<O>? {
let context = dataStack.unsafeContext()
if self.context == context {
return self
}
return .init(objectID: self.id, context: dataStack.unsafeContext())
}
// MARK: Equatable
public static func == (lhs: ObjectMonitor<ObjectType>, rhs: ObjectMonitor<ObjectType>) -> Bool {
public static func == (lhs: ObjectMonitor<O>, rhs: ObjectMonitor<O>) -> Bool {
return lhs === rhs
}
@@ -173,7 +137,7 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
return lhs.fetchedResultsController === rhs.fetchedResultsController
}
public static func ~= (lhs: ObjectMonitor<ObjectType>, rhs: ObjectMonitor<ObjectType>) -> Bool {
public static func ~= (lhs: ObjectMonitor<O>, rhs: ObjectMonitor<O>) -> Bool {
return lhs === rhs
}
@@ -194,7 +158,7 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
// MARK: Internal
internal init(objectID: ObjectType.ObjectID, context: NSManagedObjectContext) {
internal init(objectID: O.ObjectID, context: NSManagedObjectContext) {
let fetchRequest = Internals.CoreStoreFetchRequest<NSManagedObject>()
fetchRequest.entity = objectID.entity
@@ -207,8 +171,8 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
let fetchedResultsController = Internals.CoreStoreFetchedResultsController(
context: context,
fetchRequest: fetchRequest,
from: From<ObjectType>([objectID.persistentStore?.configurationName]),
applyFetchClauses: Where<ObjectType>("SELF", isEqualTo: objectID).applyToFetchRequest
from: From<O>([objectID.persistentStore?.configurationName]),
applyFetchClauses: Where<O>("SELF", isEqualTo: objectID).applyToFetchRequest
)
let fetchedResultsControllerDelegate = Internals.FetchedResultsControllerDelegate()
@@ -224,7 +188,7 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
self.lastCommittedAttributes = (self.object?.cs_toRaw().committedValues(forKeys: nil) as? [String: NSObject]) ?? [:]
}
internal func registerObserver<U: AnyObject>(_ observer: U, willChangeObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<ObjectType>, _ object: ObjectType) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<ObjectType>, _ object: ObjectType) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<ObjectType>, _ object: ObjectType, _ changedPersistentKeys: Set<String>) -> Void) {
internal func registerObserver<U: AnyObject>(_ observer: U, willChangeObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<O>, _ object: O) -> Void, didDeleteObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<O>, _ object: O) -> Void, didUpdateObject: @escaping (_ observer: U, _ monitor: ObjectMonitor<O>, _ object: O, _ changedPersistentKeys: Set<String>) -> Void) {
Internals.assert(
Thread.isMainThread,
@@ -320,7 +284,7 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
return self.fetchedResultsController.managedObjectContext
}
private func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<ObjectType>) -> Void) {
private func registerChangeNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<O>) -> Void) {
Internals.setAssociatedRetainedObject(
Internals.NotificationObserver(
@@ -340,7 +304,7 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
)
}
private func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<ObjectType>, _ object: ObjectType) -> Void) {
private func registerObjectNotification(_ notificationKey: UnsafeRawPointer, name: Notification.Name, toObserver observer: AnyObject, callback: @escaping (_ monitor: ObjectMonitor<O>, _ object: O) -> Void) {
Internals.setAssociatedRetainedObject(
Internals.NotificationObserver(
@@ -354,7 +318,7 @@ public final class ObjectMonitor<O: DynamicObject>: ObjectRepresentation, Equata
return
}
callback(self, ObjectType.cs_fromRaw(object: object))
callback(self, O.cs_fromRaw(object: object))
}
),
forKey: notificationKey,

View File

@@ -47,9 +47,14 @@ public protocol ObjectRepresentation {
An instance that may be observed for object changes.
*/
func asLiveObject(in dataStack: DataStack) -> LiveObject<ObjectType>?
/**
A read-only instance in the `DataStack`.
*/
func asReadOnly(in dataStack: DataStack) -> ObjectType?
/**
An instance that may be mutated within a transaction.
An instance that may be mutated within a `BaseDataTransaction`.
*/
func asEditable(in transaction: BaseDataTransaction) -> ObjectType?
@@ -62,11 +67,6 @@ public protocol ObjectRepresentation {
A thread-safe `struct` that is a full-copy of the object's properties
*/
func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot<ObjectType>?
/**
An instance that may be observed for property-specific changes.
*/
func asObjectMonitor(in dataStack: DataStack) -> ObjectMonitor<ObjectType>?
}
extension NSManagedObject: ObjectRepresentation {}
@@ -88,6 +88,16 @@ extension DynamicObject where Self: ObjectRepresentation {
return .init(objectID: self.cs_id(), context: context)
}
public func asReadOnly(in dataStack: DataStack) -> Self? {
let context = dataStack.unsafeContext()
if self.cs_toRaw().managedObjectContext == context {
return self
}
return context.fetchExisting(self.cs_id())
}
public func asEditable(in transaction: BaseDataTransaction) -> Self? {
let context = transaction.unsafeContext()
@@ -109,10 +119,4 @@ extension DynamicObject where Self: ObjectRepresentation {
let context = transaction.unsafeContext()
return .init(objectID: self.cs_id(), context: context)
}
public func asObjectMonitor(in dataStack: DataStack) -> ObjectMonitor<Self>? {
let context = dataStack.unsafeContext()
return .init(objectID: self.cs_id(), context: context)
}
}

View File

@@ -56,10 +56,15 @@ public struct ObjectSnapshot<O: DynamicObject>: SnapshotResult, ObjectRepresenta
let context = dataStack.unsafeContext()
return .init(objectID: self.id, context: context)
}
public func asReadOnly(in dataStack: DataStack) -> O? {
return dataStack.unsafeContext().fetchExisting(self.id)
}
public func asEditable(in transaction: BaseDataTransaction) -> O? {
return self.context.fetchExisting(self.id)
return transaction.unsafeContext().fetchExisting(self.id)
}
public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot<O>? {
@@ -81,11 +86,6 @@ public struct ObjectSnapshot<O: DynamicObject>: SnapshotResult, ObjectRepresenta
}
return .init(objectID: self.id, context: context)
}
public func asObjectMonitor(in dataStack: DataStack) -> ObjectMonitor<O>? {
return .init(objectID: self.id, context: dataStack.unsafeContext())
}
// MARK: Equatable
@@ -112,7 +112,7 @@ public struct ObjectSnapshot<O: DynamicObject>: SnapshotResult, ObjectRepresenta
self.id = objectID
self.context = context
self.values = O.cs_snapshotDictionary(id: id, context: context) as NSDictionary
self.values = O.cs_snapshotDictionary(id: objectID, context: context) as NSDictionary
}