Skip to content

Commit 50d7d45

Browse files
authored
Improve observable object and erased existential examples with AnyUserService (#185)
1 parent 9de4922 commit 50d7d45

File tree

17 files changed

+108
-137
lines changed

17 files changed

+108
-137
lines changed

Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/AnyObservableObject.swift

Lines changed: 0 additions & 32 deletions
This file was deleted.

Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Models/UserService.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,34 @@ public protocol UserService: ObservableObject {
2626
var observableObjectPublisher: ObservableObjectPublisher { get }
2727
}
2828

29+
/// A type-erased wrapper around a `UserService` that is itself a `UserService` and `ObservableObject`.
30+
public final class AnyUserService: UserService, ObservableObject {
31+
public init(_ userService: some UserService) {
32+
self.userService = userService
33+
objectWillChange = userService
34+
.objectWillChange
35+
.map { _ in () }
36+
.eraseToAnyPublisher()
37+
}
38+
39+
public var userName: String? {
40+
get {
41+
userService.userName
42+
}
43+
set {
44+
userService.userName = newValue
45+
}
46+
}
47+
48+
public var observableObjectPublisher: ObservableObjectPublisher {
49+
userService.observableObjectPublisher
50+
}
51+
52+
public let objectWillChange: AnyPublisher<Void, Never>
53+
54+
private let userService: any UserService
55+
}
56+
2957
@Instantiable(fulfillingAdditionalTypes: [UserService.self])
3058
public final class DefaultUserService: Instantiable, UserService {
3159
public init(stringStorage: StringStorage) {

Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/ExampleApp.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public struct NotesApp: Instantiable, App {
4242
// Memberwise initializer to satisfy SafeDI.
4343
// `public init()` will be generated for this type because this type is a root.
4444
public init(
45-
userService: any UserService,
45+
userService: AnyUserService,
4646
stringStorage: StringStorage,
4747
nameEntryViewBuilder: Instantiator<NameEntryView>,
4848
noteViewBuilder: Instantiator<NoteView>
@@ -51,17 +51,14 @@ public struct NotesApp: Instantiable, App {
5151
self.stringStorage = stringStorage
5252
self.nameEntryViewBuilder = nameEntryViewBuilder
5353
self.noteViewBuilder = noteViewBuilder
54-
observedUserService = AnyObservableObject(userService)
5554
}
5655

5756
/// A private property that is instantiated when the app is instantiated and manages the User state.
58-
@Instantiated private let userService: any UserService
57+
@ObservedObject @Instantiated(fulfilledByType: "DefaultUserService", erasedToConcreteExistential: true) private var userService: AnyUserService
5958
/// A private property that is instantiated when the app is instantiated and manages the persistence of strings.
6059
@Instantiated private let stringStorage: StringStorage
6160
/// A private property that is instantiated when the app is instantiated and can create a NameEntryView on demand.
6261
@Instantiated private let nameEntryViewBuilder: Instantiator<NameEntryView>
6362
/// A private property that is instantiated when the app is instantiated and can create a NoteView on demand.
6463
@Instantiated private let noteViewBuilder: Instantiator<NoteView>
65-
/// A mechanism for observing updates to the user service.
66-
@ObservedObject private var observedUserService: AnyObservableObject
6764
}

Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/NameEntryView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import SwiftUI
2424
@MainActor
2525
@Instantiable
2626
public struct NameEntryView: Instantiable, View {
27-
public init(userService: any UserService) {
27+
public init(userService: AnyUserService) {
2828
self.userService = userService
2929
}
3030

@@ -46,9 +46,9 @@ public struct NameEntryView: Instantiable, View {
4646

4747
@State private var name: String = ""
4848

49-
@Received private let userService: any UserService
49+
@Received private let userService: AnyUserService
5050
}
5151

5252
#Preview {
53-
NameEntryView(userService: DefaultUserService(stringStorage: UserDefaults.standard))
53+
NameEntryView(userService: .init(DefaultUserService(stringStorage: UserDefaults.standard)))
5454
}

Examples/ExampleCocoaPodsIntegration/ExampleCocoaPodsIntegration/Views/NoteView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import SwiftUI
2424
@MainActor
2525
@Instantiable
2626
public struct NoteView: Instantiable, View {
27-
public init(userName: String, userService: any UserService, stringStorage: StringStorage) {
27+
public init(userName: String, userService: AnyUserService, stringStorage: StringStorage) {
2828
self.userName = userName
2929
self.userService = userService
3030
self.stringStorage = stringStorage
@@ -48,7 +48,7 @@ public struct NoteView: Instantiable, View {
4848
}
4949

5050
@Forwarded private let userName: String
51-
@Received private let userService: any UserService
51+
@Received private let userService: AnyUserService
5252
@Received private let stringStorage: StringStorage
5353

5454
@State private var note: String = ""
@@ -57,7 +57,7 @@ public struct NoteView: Instantiable, View {
5757
#Preview {
5858
NoteView(
5959
userName: "dfed",
60-
userService: DefaultUserService(stringStorage: UserDefaults.standard),
60+
userService: .init(DefaultUserService(stringStorage: UserDefaults.standard)),
6161
stringStorage: UserDefaults.standard
6262
)
6363
}

Examples/ExampleMultiProjectIntegration/ExampleMultiProjectIntegration.xcodeproj/project.pbxproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
3289B4072BF955720053F2E4 /* Subproject.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3289B4012BF955710053F2E4 /* Subproject.framework */; };
1717
3289B4082BF955720053F2E4 /* Subproject.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3289B4012BF955710053F2E4 /* Subproject.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1818
3289B40D2BF955A10053F2E4 /* StringStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324F1ECC2B314DB20001AC0C /* StringStorage.swift */; };
19-
3289B40E2BF955A10053F2E4 /* AnyObservableObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324F1ED62B3156810001AC0C /* AnyObservableObject.swift */; };
2019
3289B40F2BF955A10053F2E4 /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324F1ECA2B314D8D0001AC0C /* UserService.swift */; };
2120
32B72E192D39763900F5EB6F /* SafeDI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B72E182D39763900F5EB6F /* SafeDI */; };
2221
32B72E1B2D39764200F5EB6F /* SafeDI in Frameworks */ = {isa = PBXBuildFile; productRef = 32B72E1A2D39764200F5EB6F /* SafeDI */; };
@@ -51,7 +50,6 @@
5150
324F1ECC2B314DB20001AC0C /* StringStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringStorage.swift; sourceTree = "<group>"; };
5251
324F1ECE2B314E030001AC0C /* NameEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NameEntryView.swift; sourceTree = "<group>"; };
5352
324F1ED12B3150480001AC0C /* NoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteView.swift; sourceTree = "<group>"; };
54-
324F1ED62B3156810001AC0C /* AnyObservableObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyObservableObject.swift; sourceTree = "<group>"; };
5553
32756FE22B24C042006BDD24 /* ExampleMultiProjectIntegration.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleMultiProjectIntegration.app; sourceTree = BUILT_PRODUCTS_DIR; };
5654
32756FE52B24C042006BDD24 /* ExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleApp.swift; sourceTree = "<group>"; };
5755
32756FE92B24C044006BDD24 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -135,7 +133,6 @@
135133
children = (
136134
324F1ECA2B314D8D0001AC0C /* UserService.swift */,
137135
324F1ECC2B314DB20001AC0C /* StringStorage.swift */,
138-
324F1ED62B3156810001AC0C /* AnyObservableObject.swift */,
139136
3289B4032BF955720053F2E4 /* Subproject.h */,
140137
);
141138
path = Subproject;
@@ -280,7 +277,6 @@
280277
isa = PBXSourcesBuildPhase;
281278
buildActionMask = 2147483647;
282279
files = (
283-
3289B40E2BF955A10053F2E4 /* AnyObservableObject.swift in Sources */,
284280
3289B40D2BF955A10053F2E4 /* StringStorage.swift in Sources */,
285281
3289B40F2BF955A10053F2E4 /* UserService.swift in Sources */,
286282
);

Examples/ExampleMultiProjectIntegration/ExampleMultiProjectIntegration/Views/ExampleApp.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public struct NotesApp: Instantiable, App {
4343
// Memberwise initializer to satisfy SafeDI.
4444
// `public init()` will be generated for this type because this type is a root.
4545
public init(
46-
userService: any UserService,
46+
userService: AnyUserService,
4747
stringStorage: StringStorage,
4848
nameEntryViewBuilder: Instantiator<NameEntryView>,
4949
noteViewBuilder: Instantiator<NoteView>
@@ -52,17 +52,14 @@ public struct NotesApp: Instantiable, App {
5252
self.stringStorage = stringStorage
5353
self.nameEntryViewBuilder = nameEntryViewBuilder
5454
self.noteViewBuilder = noteViewBuilder
55-
observedUserService = AnyObservableObject(userService)
5655
}
5756

5857
/// A private property that is instantiated when the app is instantiated and manages the User state.
59-
@Instantiated private let userService: any UserService
58+
@ObservedObject @Instantiated(fulfilledByType: "DefaultUserService", erasedToConcreteExistential: true) private var userService: AnyUserService
6059
/// A private property that is instantiated when the app is instantiated and manages the persistence of strings.
6160
@Instantiated private let stringStorage: StringStorage
6261
/// A private property that is instantiated when the app is instantiated and can create a NameEntryView on demand.
6362
@Instantiated private let nameEntryViewBuilder: Instantiator<NameEntryView>
6463
/// A private property that is instantiated when the app is instantiated and can create a NoteView on demand.
6564
@Instantiated private let noteViewBuilder: Instantiator<NoteView>
66-
/// A mechanism for observing updates to the user service.
67-
@ObservedObject private var observedUserService: AnyObservableObject
6865
}

Examples/ExampleMultiProjectIntegration/ExampleMultiProjectIntegration/Views/NameEntryView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import SwiftUI
2525
@MainActor
2626
@Instantiable
2727
public struct NameEntryView: Instantiable, View {
28-
public init(userService: any UserService) {
28+
public init(userService: AnyUserService) {
2929
self.userService = userService
3030
}
3131

@@ -47,9 +47,9 @@ public struct NameEntryView: Instantiable, View {
4747

4848
@State private var name: String = ""
4949

50-
@Received private let userService: any UserService
50+
@Received private let userService: AnyUserService
5151
}
5252

5353
#Preview {
54-
NameEntryView(userService: DefaultUserService(stringStorage: UserDefaults.standard))
54+
NameEntryView(userService: .init(DefaultUserService(stringStorage: UserDefaults.standard)))
5555
}

Examples/ExampleMultiProjectIntegration/ExampleMultiProjectIntegration/Views/NoteView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import SwiftUI
2525
@MainActor
2626
@Instantiable
2727
public struct NoteView: Instantiable, View {
28-
public init(userName: String, userService: any UserService, stringStorage: StringStorage) {
28+
public init(userName: String, userService: AnyUserService, stringStorage: StringStorage) {
2929
self.userName = userName
3030
self.userService = userService
3131
self.stringStorage = stringStorage
@@ -49,7 +49,7 @@ public struct NoteView: Instantiable, View {
4949
}
5050

5151
@Forwarded private let userName: String
52-
@Received private let userService: any UserService
52+
@Received private let userService: AnyUserService
5353
@Received private let stringStorage: StringStorage
5454

5555
@State private var note: String = ""
@@ -58,7 +58,7 @@ public struct NoteView: Instantiable, View {
5858
#Preview {
5959
NoteView(
6060
userName: "dfed",
61-
userService: DefaultUserService(stringStorage: UserDefaults.standard),
61+
userService: .init(DefaultUserService(stringStorage: UserDefaults.standard)),
6262
stringStorage: UserDefaults.standard
6363
)
6464
}

Examples/ExampleMultiProjectIntegration/Subproject/AnyObservableObject.swift

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)