Skip to content

Commit 060ecc9

Browse files
committed
Add UnmanagedReadOnly protocol for read and fetch
add-unmanaged-read-only-model-protocol
1 parent e310b2b commit 060ecc9

6 files changed

Lines changed: 96 additions & 24 deletions

File tree

Sources/CoreDataRepository/Internal/FetchSubscription.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import CoreData
1010
import Foundation
1111

1212
/// Subscription provider that sends updates when a fetch request changes
13-
final class FetchSubscription<Model: UnmanagedModel>: Subscription<
13+
final class FetchSubscription<Model: UnmanagedReadOnlyModel>: Subscription<
1414
[Model],
1515
Model.ManagedModel,
1616
Model.ManagedModel

Sources/CoreDataRepository/Internal/FetchThrowingSubscription.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import CoreData
1010
import Foundation
1111

1212
/// Subscription provider that sends updates when a fetch request changes
13-
final class FetchThrowingSubscription<Model: UnmanagedModel>: ThrowingSubscription<
13+
final class FetchThrowingSubscription<Model: UnmanagedReadOnlyModel>: ThrowingSubscription<
1414
[Model],
1515
Model.ManagedModel,
1616
Model.ManagedModel

Sources/CoreDataRepository/Internal/ReadSubscription.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import CoreData
1111
import Foundation
1212

1313
/// Subscription provider that sends updates when a single ``NSManagedObject`` changes
14-
final class ReadSubscription<Model: UnmanagedModel> {
14+
final class ReadSubscription<Model: UnmanagedReadOnlyModel> {
1515
private let objectId: NSManagedObjectID
1616
private let context: NSManagedObjectContext
1717
private var cancellables: Set<AnyCancellable>

Sources/CoreDataRepository/Internal/ReadThrowingSubscription.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Combine
1010
import CoreData
1111
import Foundation
1212

13-
final class ReadThrowingSubscription<Model: UnmanagedModel> {
13+
final class ReadThrowingSubscription<Model: UnmanagedReadOnlyModel> {
1414
private let objectId: NSManagedObjectID
1515
private let context: NSManagedObjectContext
1616
private var cancellables: Set<AnyCancellable>

Sources/CoreDataRepository/UnmanagedModel.swift

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,8 @@
99
import CoreData
1010
import Foundation
1111

12-
/// A protocol for a value type that corresponds to a ``NSManagedObject`` subclass
13-
public protocol UnmanagedModel: Equatable {
14-
/// The ``NSManagedObject`` subclass `Self` corresponds to
15-
associatedtype ManagedModel: NSManagedObject
16-
12+
/// Protocol for a value type that corresponds to a ``NSManagedObject`` subclass
13+
public protocol UnmanagedModel: UnmanagedReadOnlyModel {
1714
/// URL representation of the ``ManagedModel``'s ``NSManagedObjectID``
1815
///
1916
/// A `nil` value should mean that this instance has not been saved in the repository
@@ -24,19 +21,4 @@ public protocol UnmanagedModel: Equatable {
2421

2522
/// Update the properties of the ``ManagedModel`` instance from `self`
2623
func updating(managed: ManagedModel) throws
27-
28-
/// Initialize of new instance of `Self` from an instance of ``ManagedModel``
29-
init(managed: ManagedModel) throws
30-
31-
/// ``NSFetchRequest`` for ``ManagedModel`` with a strongly typed ``NSFetchRequest.ResultType``
32-
static func managedFetchRequest() -> NSFetchRequest<ManagedModel>
33-
}
34-
35-
extension UnmanagedModel {
36-
public static func managedFetchRequest() -> NSFetchRequest<ManagedModel> {
37-
NSFetchRequest<ManagedModel>(
38-
entityName: ManagedModel.entity().name ?? ManagedModel.entity()
39-
.managedObjectClassName
40-
)
41-
}
4224
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// UnmanagedReadOnlyModel.swift
2+
// CoreDataRepository
3+
//
4+
//
5+
// MIT License
6+
//
7+
// Copyright © 2024 Andrew Roan
8+
9+
import CoreData
10+
import Foundation
11+
12+
/// Protocol for a value type for reading and fetchiing a ``NSManagedObject`` subclass
13+
///
14+
/// There are times where a single ``NSManagedObject``subclass may be accessed via multiple value types.
15+
/// ``UnmanagedReadOnlyModel`` provides a minimal interface for reading and fetching values from CoreData
16+
/// without needing to implement the full ``UnmanagedModel`` protocol.
17+
///
18+
/// For example, for `ManagedMovie`, there are two unmanaged models, `Movie` and `MoviePlaceholder`.
19+
/// `MoviePlaceholder` will never create, update, or delete a corresponding instance of `ManagedMovie`. It is read only.
20+
/// `Movie` supports all operations against a corresponding instacnce of `ManagedMovie`.
21+
///
22+
/// ```swift
23+
/// @objc(ManagedMovie)
24+
/// final class ManagedMovie: NSManagedObject {
25+
/// @NSManaged var id: UUID?
26+
/// @NSManaged var title: String?
27+
/// }
28+
///
29+
/// struct Movie: Hashable, UnmanagedModel {
30+
/// let id: UUID
31+
/// var title: String = ""
32+
/// var url: URL?
33+
///
34+
/// init(managed: ManagedMovie) throws {
35+
/// self.init(
36+
/// id: managed.id!,
37+
/// title: managed.title!,
38+
/// url: managed.objectID.uriRepresentation()
39+
/// )
40+
/// }
41+
///
42+
/// var managedIdUrl: URL? {
43+
/// get {
44+
/// url
45+
/// }
46+
/// set(newValue) {
47+
/// url = newValue
48+
/// }
49+
/// }
50+
///
51+
/// func asManagedModel(in context: NSManagedObjectContext) throws -> ManagedMovie {
52+
/// let object = ManagedMovie(context: context)
53+
/// object.id = id
54+
/// object.title = title
55+
/// return object
56+
/// }
57+
///
58+
/// func updating(managed: ManagedMovie) throws {
59+
/// managed.id = id
60+
/// managed.title = title
61+
/// }
62+
/// }
63+
///
64+
/// struct MoviePlaceholder: UnmanagedReadOnlyModel {
65+
/// let id: UUID
66+
///
67+
/// init(managed: ManagedMovie) throws {
68+
/// self.init(id: managed.id!)
69+
/// }
70+
/// }
71+
/// ```
72+
public protocol UnmanagedReadOnlyModel: Equatable {
73+
/// The ``NSManagedObject`` subclass `Self` corresponds to
74+
associatedtype ManagedModel: NSManagedObject
75+
76+
/// Initialize of new instance of `Self` from an instance of ``ManagedModel``
77+
init(managed: ManagedModel) throws
78+
79+
/// ``NSFetchRequest`` for ``ManagedModel`` with a strongly typed ``NSFetchRequest.ResultType``
80+
static func managedFetchRequest() -> NSFetchRequest<ManagedModel>
81+
}
82+
83+
extension UnmanagedReadOnlyModel {
84+
public static func managedFetchRequest() -> NSFetchRequest<ManagedModel> {
85+
NSFetchRequest<ManagedModel>(
86+
entityName: ManagedModel.entity().name ?? ManagedModel.entity()
87+
.managedObjectClassName
88+
)
89+
}
90+
}

0 commit comments

Comments
 (0)