将数据从委托 Swift 类传递到 SwiftUI 结构中的 EnvironmentObject
Posted
技术标签:
【中文标题】将数据从委托 Swift 类传递到 SwiftUI 结构中的 EnvironmentObject【英文标题】:Passing Data from Delegate Swift Class to EnvironmentObject in SwiftUI Struct 【发布时间】:2020-06-01 16:58:49 【问题描述】:如何将传入的数据从 Swift 类中的委托触发的方法传递到 EnvironmentObject?
我知道,要使其正常工作,我的 Swift 类需要从 SwiftUI 结构(作为父 SwiftUI 结构的子级)调用/初始化。但是,我在 Apple Watch 应用程序的 ExtensionDelegate 中初始化了我的 Swift 类。我希望在名称更新时看到 UI 文本元素发生变化。
以下代码在 Apple Watch 上运行:
class User: ObservableObject
@Published var id: UUID?
@Published var name: String?
//SwiftUI struct
struct UI: View
@EnvironmentObject var userEnv: User
var body: some View
Text(userEnv.name)
// Swift class
class WatchConnectivityProvider: NSObject, WCSessionDelegate
static let shared = WatchConnectivityProvider()
private let session: WCSession
init(session: WCSession = .default)
self.session = session
super.init()
func activateSession()
if WCSession.isSupported()
session.delegate = self
session.activate()
//This func gets triggered when data is sent from the iPhone
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void)
let list = message["list"]
let jsonDecoder = JSONDecoder()
if let data = try? jsonDecoder.decode(User.self, from: list as! Data)
// !!! This is where I would like to update the EnvironmentObject userEnv !!!
// What is the best way to do this? Remember, this class has already been initialised within the ExtensionDelegate.
//ExtensionDelegate of Apple Watch app, initialising the WatchConnectivityProvider
class ExtensionDelegate: NSObject, WKExtensionDelegate
func applicationDidFinishLaunching()
// Perform any final initialization of your application.
WatchConnectivityProvider.shared.activateSession()
【问题讨论】:
【参考方案1】:依赖注入
其中一个解决方案可能是全局存储对您的@EnvironmentObject
的引用,例如。在一些依赖容器中。
enum Dependencies
struct Name: Equatable
let rawValue: String
static let `default` = Name(rawValue: "__default__")
static func == (lhs: Name, rhs: Name) -> Bool lhs.rawValue == rhs.rawValue
final class Container
private var dependencies: [(key: Dependencies.Name, value: Any)] = []
static let `default` = Container()
func register(_ dependency: Any, for key: Dependencies.Name = .default)
dependencies.append((key: key, value: dependency))
func resolve<T>(_ key: Dependencies.Name = .default) -> T
return (dependencies
.filter (dependencyTuple) -> Bool in
return dependencyTuple.key == key
&& dependencyTuple.value is T
.first)?.value as! T
然后你像这样创建你的对象:
Dependencies.Container.default.register(User())
您可以从代码中的任何位置访问它:
let user: User = Dependencies.Container.default.resolve()
user.modify()
Dependency Injection
用于 Swift 的更详细解释可以找到here。
单例
或者,您可以使用标准的Singleton
模式使您的用户数据在全球范围内可用。更详细的解释可以找到here。
最后的想法
Clean Architecture for SwiftUI 是一个很好的例子(至少在我看来)如何以 clean 的方式编写 ios 应用程序。这有点复杂,但你可以拿起一些部分。
【讨论】:
这看起来非常复杂。特别是当自定义对象很复杂时。我对 EnvironmentObject 的理解是允许在应用程序范围内共享数据及其更新。这不应该需要依赖关系。虽然我确信这是一种方法,但感觉不像 SwiftUI。 确实,这很复杂。EnvironmentObject
的问题在于它是一种单例,但仅适用于 SwiftUI Views
。如果您想要一个简单的解决方案,请尝试使用普通的单例。类似的问题是here。
所以在 SwiftUI 中使用 Swift 类的唯一方法是从 SwiftUI 中调用类和方法,例如在 .onAppear self.userEnv.name = WatchConnectivityProvider.shared.getData() ?如果我想获得定期更新,我必须在 Timer 间隔上调用该方法。这感觉像是一个不成熟的设计。我不希望我的应用程序逻辑驻留在 Swift 类中吗?
如果你想遵循MVVM
模式,你可以有一个ViewModel
类,你的业务逻辑驻留在那里,并在你的View
中作为ObservedObject
。您可以像.onAppear self.viewModel.load()
一样使用它。对于访问ViewModel
中的env 对象的问题,我推荐使用Dependency Injection
。否则我所知道的只剩下一个Singleton
。请记住,这仍然是 SwiftUI 的早期阶段。未来可能会有更好的解决方案。
Here 是一篇关于如何以 clean 方式编写 iOS 应用的好文章。【参考方案2】:
这里是错误的巨型代码:
class ExtensionDelegate: ObservableObject, NSObject, WCSessionDelegate, WKExtensionDelegate
var session: WCSession?
@Published var model = Model
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void)
print(#function)
var replyValues = Dictionary<String, Any>()
replyValues["status"] = "failed"
// 2442 Bytes
if let data = message["data"] as? Data
// push that work back to the main thread
DispatchQueue.main.async
self.model = try? JSONDecoder().decode(Model.self, from: data)
if let vm = vm
replyValues["status"] = "ok"
replyValues["id"] = vm.id.uuidString
replyHandler(replyValues)
...
【讨论】:
以上是关于将数据从委托 Swift 类传递到 SwiftUI 结构中的 EnvironmentObject的主要内容,如果未能解决你的问题,请参考以下文章
如何在swift 3中从一个视图控制器传递到上一个视图控制器时设置委托?
为啥我在实现委托以将值从子 swift 类传递给父目标 C 类时出错?
SwiftUI如何从视图调用或通知scenedelegate