Skip to content

Commit 4fb748a

Browse files
committed
AnyObservableObject -> AnyUserService
1 parent 9de4922 commit 4fb748a

File tree

16 files changed

+108
-133
lines changed

16 files changed

+108
-133
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/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.

Examples/ExampleMultiProjectIntegration/Subproject/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) {

0 commit comments

Comments
 (0)