mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-04-22 16:58:30 +02:00
Removed ObservableObject implementations of ListPublisher/ObjectPublisher in favor of LiveList/LiveObject and ".reactive" publishers
This commit is contained in:
@@ -25,7 +25,7 @@ extension Modern.PlacemarksDemo {
|
|||||||
Modern.PlacemarksDemo.dataStack.perform(
|
Modern.PlacemarksDemo.dataStack.perform(
|
||||||
asynchronous: { (transaction) in
|
asynchronous: { (transaction) in
|
||||||
|
|
||||||
let place = self.place.asEditable(in: transaction)
|
let place = self.$place?.asEditable(in: transaction)
|
||||||
place?.annotation = .init(coordinate: coordinate)
|
place?.annotation = .init(coordinate: coordinate)
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
@@ -42,7 +42,7 @@ extension Modern.PlacemarksDemo {
|
|||||||
_ = try? Modern.PlacemarksDemo.dataStack.perform(
|
_ = try? Modern.PlacemarksDemo.dataStack.perform(
|
||||||
synchronous: { (transaction) in
|
synchronous: { (transaction) in
|
||||||
|
|
||||||
let place = self.place.asEditable(in: transaction)
|
let place = self.$place?.asEditable(in: transaction)
|
||||||
place?.setRandomLocation()
|
place?.setRandomLocation()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -71,22 +71,25 @@ extension Modern.PlacemarksDemo {
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
@ObservedObject
|
@LiveObject(Modern.PlacemarksDemo.placePublisher)
|
||||||
var place: ObjectPublisher<Modern.PlacemarksDemo.Place>
|
var place: ObjectSnapshot<Modern.PlacemarksDemo.Place>?
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
||||||
self.place = Modern.PlacemarksDemo.placePublisher
|
self.sinkCancellable = self.$place?.reactive.snapshot().sink(
|
||||||
self.sinkCancellable = self.place.sink(
|
|
||||||
receiveCompletion: { _ in
|
receiveCompletion: { _ in
|
||||||
|
|
||||||
// Deleted, do nothing
|
// Deleted, do nothing
|
||||||
},
|
},
|
||||||
receiveValue: { [self] (snapshot) in
|
receiveValue: { [self] (snapshot) in
|
||||||
|
|
||||||
|
guard let snapshot = snapshot else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
self.geocoder.geocode(place: snapshot) { (title, subtitle) in
|
self.geocoder.geocode(place: snapshot) { (title, subtitle) in
|
||||||
|
|
||||||
guard self.place.snapshot == snapshot else {
|
guard self.place == snapshot else {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -104,22 +107,29 @@ extension Modern.PlacemarksDemo {
|
|||||||
// MARK: View
|
// MARK: View
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Modern.PlacemarksDemo.MapView(
|
|
||||||
place: self.place.snapshot,
|
Group {
|
||||||
onTap: { coordinate in
|
|
||||||
|
if let place = self.place {
|
||||||
|
|
||||||
self.demoAsynchronousTransaction(coordinate: coordinate)
|
Modern.PlacemarksDemo.MapView(
|
||||||
|
place: place,
|
||||||
|
onTap: { coordinate in
|
||||||
|
|
||||||
|
self.demoAsynchronousTransaction(coordinate: coordinate)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.overlay(
|
||||||
|
InstructionsView(
|
||||||
|
("Random", "Sets random coordinate"),
|
||||||
|
("Tap", "Sets to tapped coordinate")
|
||||||
|
)
|
||||||
|
.padding(.leading, 10)
|
||||||
|
.padding(.bottom, 40),
|
||||||
|
alignment: .bottomLeading
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
.overlay(
|
|
||||||
InstructionsView(
|
|
||||||
("Random", "Sets random coordinate"),
|
|
||||||
("Tap", "Sets to tapped coordinate")
|
|
||||||
)
|
|
||||||
.padding(.leading, 10)
|
|
||||||
.padding(.bottom, 40),
|
|
||||||
alignment: .bottomLeading
|
|
||||||
)
|
|
||||||
.navigationBarTitle("Placemarks")
|
.navigationBarTitle("Placemarks")
|
||||||
.navigationBarItems(
|
.navigationBarItems(
|
||||||
trailing: Button("Random") {
|
trailing: Button("Random") {
|
||||||
@@ -132,7 +142,7 @@ extension Modern.PlacemarksDemo {
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private var sinkCancellable: AnyCancellable? = nil
|
private var sinkCancellable: AnyCancellable?
|
||||||
private let geocoder = Modern.PlacemarksDemo.Geocoder()
|
private let geocoder = Modern.PlacemarksDemo.Geocoder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ CoreStore was (and is) heavily shaped by real-world needs of developing data-dep
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- **SwiftUI and Combine API utilities.**
|
- **🆕SwiftUI and Combine API utilities.** `ListPublisher`s and `ObjectPublisher`s now have their `@LiveList` and `@LiveObject` SwiftUI property wrappers. Combine `Publisher` s are also available through the `ListPublisher.reactive`, `ObjectPublisher.reactive`, and `DataStack.reactive` namespaces.
|
||||||
- **Backwards-portable DiffableDataSources implementation!** `UITableViews` and `UICollectionViews` now have a new ally: `ListPublisher`s provide diffable snapshots that make reloading animations very easy and very safe. Say goodbye to `UITableViews` and `UICollectionViews` reload errors!
|
- **Backwards-portable DiffableDataSources implementation!** `UITableViews` and `UICollectionViews` now have a new ally: `ListPublisher`s provide diffable snapshots that make reloading animations very easy and very safe. Say goodbye to `UITableViews` and `UICollectionViews` reload errors!
|
||||||
- **💎Tight design around Swift’s code elegance and type safety.** CoreStore fully utilizes Swift's community-driven language features.
|
- **💎Tight design around Swift’s code elegance and type safety.** CoreStore fully utilizes Swift's community-driven language features.
|
||||||
- **🚦Safer concurrency architecture.** CoreStore makes it hard to fall into common concurrency mistakes. The main `NSManagedObjectContext` is strictly read-only, while all updates are done through serial *transactions*. *(See [Saving and processing transactions](#saving-and-processing-transactions))*
|
- **🚦Safer concurrency architecture.** CoreStore makes it hard to fall into common concurrency mistakes. The main `NSManagedObjectContext` is strictly read-only, while all updates are done through serial *transactions*. *(See [Saving and processing transactions](#saving-and-processing-transactions))*
|
||||||
|
|||||||
@@ -25,16 +25,6 @@
|
|||||||
|
|
||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
#if canImport(Combine)
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if canImport(SwiftUI)
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ListPublisher
|
// MARK: - ListPublisher
|
||||||
|
|
||||||
@@ -89,13 +79,8 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
*/
|
*/
|
||||||
public fileprivate(set) var snapshot: ListSnapshot<O> = .init() {
|
public fileprivate(set) var snapshot: ListSnapshot<O> = .init() {
|
||||||
|
|
||||||
willSet {
|
|
||||||
|
|
||||||
self.willChange()
|
|
||||||
}
|
|
||||||
didSet {
|
didSet {
|
||||||
|
|
||||||
self.didChange()
|
|
||||||
self.notifyObservers()
|
self.notifyObservers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,11 +300,6 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
self.fetchedResultsControllerDelegate.fetchedResultsController = nil
|
self.fetchedResultsControllerDelegate.fetchedResultsController = nil
|
||||||
self.observers.removeAllObjects()
|
self.observers.removeAllObjects()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: FilePrivate
|
|
||||||
|
|
||||||
fileprivate let rawObjectWillChange: Any?
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
@@ -375,21 +355,6 @@ public final class ListPublisher<O: DynamicObject>: Hashable {
|
|||||||
applyFetchClauses: applyFetchClauses
|
applyFetchClauses: applyFetchClauses
|
||||||
)
|
)
|
||||||
|
|
||||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
|
||||||
|
|
||||||
#if canImport(Combine)
|
|
||||||
self.rawObjectWillChange = ObservableObjectPublisher()
|
|
||||||
|
|
||||||
#else
|
|
||||||
self.rawObjectWillChange = nil
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
self.rawObjectWillChange = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fetchedResultsControllerDelegate.handler = self
|
self.fetchedResultsControllerDelegate.handler = self
|
||||||
|
|
||||||
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
try! self.fetchedResultsController.performFetchFromSpecifiedStores()
|
||||||
@@ -428,143 +393,3 @@ extension ListPublisher: FetchedDiffableDataSourceSnapshotHandler {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if canImport(Combine)
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
|
||||||
extension ListPublisher: ObservableObject {}
|
|
||||||
|
|
||||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
|
||||||
extension ListPublisher: Publisher {
|
|
||||||
|
|
||||||
// MARK: Publisher
|
|
||||||
|
|
||||||
public typealias Output = ListSnapshot<O>
|
|
||||||
public typealias Failure = Never
|
|
||||||
|
|
||||||
public func receive<S: Subscriber>(subscriber: S) where S.Input == Output, S.Failure == Failure {
|
|
||||||
|
|
||||||
subscriber.receive(
|
|
||||||
subscription: ListSnapshotSubscription(
|
|
||||||
publisher: self,
|
|
||||||
subscriber: subscriber
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ListSnapshotSubscriber
|
|
||||||
|
|
||||||
fileprivate final class ListSnapshotSubscriber: Subscriber {
|
|
||||||
|
|
||||||
// MARK: Subscriber
|
|
||||||
|
|
||||||
typealias Failure = Never
|
|
||||||
|
|
||||||
func receive(subscription: Subscription) {
|
|
||||||
|
|
||||||
subscription.request(.unlimited)
|
|
||||||
}
|
|
||||||
|
|
||||||
func receive(_ input: Output) -> Subscribers.Demand {
|
|
||||||
|
|
||||||
return .unlimited
|
|
||||||
}
|
|
||||||
|
|
||||||
func receive(completion: Subscribers.Completion<Failure>) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ListSnapshotSubscription
|
|
||||||
|
|
||||||
fileprivate final class ListSnapshotSubscription<S: Subscriber>: Subscription where S.Input == Output, S.Failure == Never {
|
|
||||||
|
|
||||||
// MARK: FilePrivate
|
|
||||||
|
|
||||||
init(publisher: ListPublisher<O>, subscriber: S) {
|
|
||||||
|
|
||||||
self.publisher = publisher
|
|
||||||
self.subscriber = subscriber
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Subscription
|
|
||||||
|
|
||||||
func request(_ demand: Subscribers.Demand) {
|
|
||||||
|
|
||||||
guard demand > 0 else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.publisher.addObserver(self) { [weak self] (publisher) in
|
|
||||||
|
|
||||||
guard let self = self, let subscriber = self.subscriber else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_ = subscriber.receive(publisher.snapshot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Cancellable
|
|
||||||
|
|
||||||
func cancel() {
|
|
||||||
self.publisher.removeObserver(self)
|
|
||||||
self.subscriber = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
private let publisher: ListPublisher<O>
|
|
||||||
private var subscriber: S?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - ListPublisher
|
|
||||||
|
|
||||||
extension ListPublisher {
|
|
||||||
|
|
||||||
// MARK: ObservableObject
|
|
||||||
|
|
||||||
#if canImport(Combine)
|
|
||||||
|
|
||||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
|
||||||
public var objectWillChange: ObservableObjectPublisher {
|
|
||||||
|
|
||||||
return self.rawObjectWillChange! as! ObservableObjectPublisher
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fileprivate func willChange() {
|
|
||||||
|
|
||||||
guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
#if canImport(Combine)
|
|
||||||
|
|
||||||
#if canImport(SwiftUI)
|
|
||||||
withAnimation {
|
|
||||||
|
|
||||||
self.objectWillChange.send()
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
self.objectWillChange.send()
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func didChange() {
|
|
||||||
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ public struct LiveObject<O: DynamicObject>: DynamicProperty {
|
|||||||
return self.observer.item
|
return self.observer.item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var projectedValue: ObjectPublisher<O>? {
|
||||||
|
|
||||||
|
return self.observer.objectPublisher
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: DynamicProperty
|
// MARK: DynamicProperty
|
||||||
|
|
||||||
|
|||||||
@@ -25,16 +25,6 @@
|
|||||||
|
|
||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
#if canImport(Combine)
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if canImport(SwiftUI)
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ObjectPublisher
|
// MARK: - ObjectPublisher
|
||||||
|
|
||||||
@@ -226,26 +216,10 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
|||||||
|
|
||||||
// MARK: FilePrivate
|
// MARK: FilePrivate
|
||||||
|
|
||||||
fileprivate let rawObjectWillChange: Any?
|
|
||||||
|
|
||||||
fileprivate init(objectID: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>?) {
|
fileprivate init(objectID: O.ObjectID, context: NSManagedObjectContext, initializer: @escaping (NSManagedObjectID, NSManagedObjectContext) -> ObjectSnapshot<O>?) {
|
||||||
|
|
||||||
self.id = objectID
|
self.id = objectID
|
||||||
self.context = context
|
self.context = context
|
||||||
if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) {
|
|
||||||
|
|
||||||
#if canImport(Combine)
|
|
||||||
self.rawObjectWillChange = ObservableObjectPublisher()
|
|
||||||
|
|
||||||
#else
|
|
||||||
self.rawObjectWillChange = nil
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
self.rawObjectWillChange = nil
|
|
||||||
}
|
|
||||||
self.$lazySnapshot.initialize { [weak self] in
|
self.$lazySnapshot.initialize { [weak self] in
|
||||||
|
|
||||||
guard let self = self else {
|
guard let self = self else {
|
||||||
@@ -262,16 +236,12 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
|||||||
|
|
||||||
self.object = nil
|
self.object = nil
|
||||||
|
|
||||||
self.willChange()
|
|
||||||
self.$lazySnapshot.reset({ nil })
|
self.$lazySnapshot.reset({ nil })
|
||||||
self.didChange()
|
|
||||||
self.notifyObservers()
|
self.notifyObservers()
|
||||||
}
|
}
|
||||||
else if updatedIDs.contains(objectID) {
|
else if updatedIDs.contains(objectID) {
|
||||||
|
|
||||||
self.willChange()
|
|
||||||
self.$lazySnapshot.reset({ initializer(objectID, context) })
|
self.$lazySnapshot.reset({ initializer(objectID, context) })
|
||||||
self.didChange()
|
|
||||||
self.notifyObservers()
|
self.notifyObservers()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,154 +275,6 @@ public final class ObjectPublisher<O: DynamicObject>: ObjectRepresentation, Hash
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if canImport(Combine)
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
|
||||||
extension ObjectPublisher: ObservableObject {}
|
|
||||||
|
|
||||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
|
||||||
extension ObjectPublisher: Publisher {
|
|
||||||
|
|
||||||
// MARK: Publisher
|
|
||||||
|
|
||||||
public typealias Output = ObjectSnapshot<O>
|
|
||||||
public typealias Failure = Never
|
|
||||||
|
|
||||||
public func receive<S: Subscriber>(subscriber: S) where S.Input == Output, S.Failure == Failure {
|
|
||||||
|
|
||||||
subscriber.receive(
|
|
||||||
subscription: ObjectSnapshotSubscription(
|
|
||||||
publisher: self,
|
|
||||||
subscriber: subscriber
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ObjectSnapshotSubscriber
|
|
||||||
|
|
||||||
fileprivate final class ObjectSnapshotSubscriber: Subscriber {
|
|
||||||
|
|
||||||
// MARK: Subscriber
|
|
||||||
|
|
||||||
typealias Failure = Never
|
|
||||||
|
|
||||||
func receive(subscription: Subscription) {
|
|
||||||
|
|
||||||
subscription.request(.unlimited)
|
|
||||||
}
|
|
||||||
|
|
||||||
func receive(_ input: Output) -> Subscribers.Demand {
|
|
||||||
|
|
||||||
return .unlimited
|
|
||||||
}
|
|
||||||
|
|
||||||
func receive(completion: Subscribers.Completion<Failure>) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ObjectSnapshotSubscription
|
|
||||||
|
|
||||||
fileprivate final class ObjectSnapshotSubscription<S: Subscriber>: Subscription where S.Input == Output, S.Failure == Never {
|
|
||||||
|
|
||||||
// MARK: FilePrivate
|
|
||||||
|
|
||||||
init(publisher: ObjectPublisher<O>, subscriber: S) {
|
|
||||||
|
|
||||||
self.publisher = publisher
|
|
||||||
self.subscriber = subscriber
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Subscription
|
|
||||||
|
|
||||||
func request(_ demand: Subscribers.Demand) {
|
|
||||||
|
|
||||||
guard demand > 0 else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.publisher.addObserver(self) { [weak self] (publisher) in
|
|
||||||
|
|
||||||
guard let self = self, let subscriber = self.subscriber else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if let snapshot = publisher.snapshot {
|
|
||||||
|
|
||||||
_ = subscriber.receive(snapshot)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
subscriber.receive(completion: .finished)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Cancellable
|
|
||||||
|
|
||||||
func cancel() {
|
|
||||||
self.publisher.removeObserver(self)
|
|
||||||
self.subscriber = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
private let publisher: ObjectPublisher<O>
|
|
||||||
private var subscriber: S?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - ObjectPublisher
|
|
||||||
|
|
||||||
extension ObjectPublisher {
|
|
||||||
|
|
||||||
// MARK: ObservableObject
|
|
||||||
|
|
||||||
#if canImport(Combine)
|
|
||||||
|
|
||||||
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
|
|
||||||
public var objectWillChange: ObservableObjectPublisher {
|
|
||||||
|
|
||||||
return self.rawObjectWillChange! as! ObservableObjectPublisher
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fileprivate func willChange() {
|
|
||||||
|
|
||||||
guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) else {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
#if canImport(Combine)
|
|
||||||
|
|
||||||
#if canImport(SwiftUI)
|
|
||||||
|
|
||||||
withAnimation {
|
|
||||||
|
|
||||||
self.objectWillChange.send()
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
self.objectWillChange.send()
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func didChange() {
|
|
||||||
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - ObjectPublisher where O: NSManagedObject
|
// MARK: - ObjectPublisher where O: NSManagedObject
|
||||||
|
|
||||||
extension ObjectPublisher where O: NSManagedObject {
|
extension ObjectPublisher where O: NSManagedObject {
|
||||||
|
|||||||
Reference in New Issue
Block a user