我想在我的SwiftUI应用程序中设置一个在两个或多个View模型之间共享的数据控制器.我了解了如何将数据控制器设置为单例,以便在两者之间共享该实例,但我也了解了单例是一种反模式.我有什么 Select ?

这是我当前的try ,但没有成功,因为我相信每个VM最终都会实例化自己的用户控制器实例.在一个视图中更新用户名不会在另一个视图中更新.

UserController

@Observable final class UserController {
    
    private(set) var userProfile: UserProfile?
    
    func getUserProfile() async -> () {
        do {
           ...
        } catch {
           ...
        }
    }
    
    func updateUsername(newUsername: String) -> () {
        userProfile?.updateUsername(newUsername: newUsername)
    }
    
}

首页视图Model

@Observable final class 首页视图Model {
    
    var controller: UserController
    
    init(controller: UserController) {
        self.controller = controller
    }
    
    func getData() async {
        await controller.getUserProfile()
    }
    
    func updateUsername(newUsername: String) -> () {
        controller.updateUsername(newUsername: newUsername)
    }
}

设置视图模型

@Observable final class 设置视图模型 {
    
    var controller: UserController
    
    init(controller: UserController) {
        self.controller = controller
    }
    
    func getData() async {
        await controller.getUserProfile()
    }
    
    func updateUsername(newUsername: String) -> () {
        controller.updateUsername(newUsername: newUsername)
    }
}

在这些视图中,我通过try 更新用户名以使其反映在两个视图中来测试这一点.

首页视图

struct 首页视图: View {
    
    @State private var vm = 首页视图Model(controller: UserController())
    
    var body: some View {
            VStack {
                if let userProfile = vm.controller.userProfile {
                    Text("\(userProfile.username)")
                }
                
                Button("Get data") {
                    Task {
                        await vm.getData()
                    }
                }
                
                Button("Update username") {
                    vm.updateUsername(newUsername: "首页视图Username")
                }
            }
        }
}

设置查看

struct 设置查看: View {
    
    @State private var vm = 设置视图模型(controller: UserController())
    
    var body: some View {
        VStack {
            if let userProfile = vm.controller.userProfile {
                Text("\(userProfile.username)")
            }
            
            Button("Update username") {
                vm.updateUsername(newUsername: "Settings username")
            }
        }
    }
}

我使用此设置的目标是拥有一个数据控制器/文件,其中包含所需的所有API请求及其响应.当我为一个屏幕设置一个视图模型时,我可以用一种方式来设置它,它使用来自控制器的数据,该控制器在更新时同步整个应用程序.

推荐答案

您的观点应该是:

  • 收到UserController个实例
  • 使用它实例化ViewModel

下面是一个例子

struct HomeView: View {
    
    @State private var vm: HomeViewModel
    
    init(userController: UserController) {
        _vm = .init(wrappedValue: .init(controller: userController))
    }

    var body: some View {
        ...
    }
}

当然,这里也是如此

struct SettingsView: View {
    
    @State private var vm: SettingsViewModel
    
    init(userController: UserController) {
        _vm = .init(wrappedValue: .init(controller: userController))
    }

    var body: some View {
        ...
    }
}

这样,相同的UserController个实例将在几个视图模型之间共享.

Update

这就是如何创建和注入UserController的.

@main
struct ExampleApp: App {
    private let userController = UserController()
    
    var body: some Scene {
        WindowGroup {
            HomeView(userController: userController)
        }
    }
}

Swift相关问答推荐

{Singleton类}类型的值没有成员$showegView'

NSWindow中以编程方式创建的TextField快捷方式与SwiftUI

如何在SWIFT任务中判断当前任务是否已取消(异步/等待)

在Xcode SWIFT中运行用Ionic编写的函数

如何将函数参数(字符串文字)标记为可本地化?

可选,类似于自定义类型的初始化

如何在 Vapor 中制作可选的查询过滤器

如何绑定环境变量ios17

TimeZone 背后的目的

SwiftUI:决定键盘重叠的内容

在 Swift 中,如何查看 Process() 传递给 shell 的字符串?

Apple 的自然语言 API 返回意外结果

将项目引用添加到 Swift iOS XCode 项目并调试

如何在 Xcode 中的 UITests 下以通用方式访问后退栏按钮项?

在 Xcode 中自动实现 Swift 协议方法

如何从 Swift 中的字符串生成条形码?

用 UIBezierPath 画一条线

在 unwind segue 之后执行 push segue

Facebook SDK 4.0 IOS Swift 以编程方式注销用户

ISO8601DateFormatter 不解析 ISO 日期字符串