AsyncStream
我想了解iOS 17 引入宏的实用性@Observable
,我们可以在其中直接观察模型中任何变量值的变化(并且观察跟踪甚至可以在 SwiftUI 视图之外进行)。因此,如果我AsyncStream
在 SwiftUI 视图中使用连续的值流(例如文件的下载进度),则可以在同一个 SwiftUI 视图中使用onChange(of:initial)
下载进度(存储为模型对象中的属性)观察到相同的值流。我正在寻找这两种方法的优点、缺点和局限性。
具体来说,我的问题与 Apple 的AVCam 示例代码有关,他们观察到以下几个状态。这是在CameraModel
附加到 SwiftUI 视图的类中完成的。
// MARK: - Internal state observations
// Set up camera's state observations.
private func observeState() {
Task {
// Await new thumbnails that the media library generates when saving a file.
for await thumbnail in mediaLibrary.thumbnails.compactMap({ $0 }) {
self.thumbnail = thumbnail
}
}
Task {
// Await new capture activity values from the capture service.
for await activity in await captureService.$captureActivity.values {
if activity.willCapture {
// Flash the screen to indicate capture is starting.
flashScreen()
} else {
// Forward the activity to the UI.
captureActivity = activity
}
}
}
Task {
// Await updates to the capabilities that the capture service advertises.
for await capabilities in await captureService.$captureCapabilities.values {
isHDRVideoSupported = capabilities.isHDRSupported
cameraState.isVideoHDRSupported = capabilities.isHDRSupported
}
}
Task {
// Await updates to a person's interaction with the Camera Control HUD.
for await isShowingFullscreenControls in await captureService.$isShowingFullscreenControls.values {
withAnimation {
// Prefer showing a minimized UI when capture controls enter a fullscreen appearance.
prefersMinimizedUI = isShowingFullscreenControls
}
}
}
}
如果我们看到结构,它是一个具有两个 Bool 成员的小结构。这些变化可以通过 SwiftUI 视图直接观察到。我想知道在这里使用并在 for 循环中不断迭代变化CaptureCapabilities
是否有特定的优势或理由。AsyncStream
/// A structure that represents the capture capabilities of `CaptureService` in
/// its current configuration.
struct CaptureCapabilities {
let isLivePhotoCaptureSupported: Bool
let isHDRSupported: Bool
init(isLivePhotoCaptureSupported: Bool = false,
isHDRSupported: Bool = false) {
self.isLivePhotoCaptureSupported = isLivePhotoCaptureSupported
self.isHDRSupported = isHDRSupported
}
static let unknown = CaptureCapabilities()
}
这里有几个问题:
抽象地回答这个问题
AsyncSequence
(这AsyncStream
只是一个具体的例子)是一种更通用的模式。正确实施后,支持取消、随时间异步产生值以及在需要时产生值,并且当序列完成时具有“完成”的概念。ObservableObject
/@Observable
模式是一种更狭窄的模式(值得注意的是,@Observable
非常适合 SwiftUI,在 UIKit/AppKit 中有点麻烦),用于发布对象某些状态的变化。对于您的特定示例(用于通知 SwiftUI 状态随时间的变化),观察和异步序列都可以完成这项工作;我不会因为其中一个而失眠。当 UI 需要根据对象的状态进行更新时,观察相关模式非常自然。当您希望某个对象(例如此“相机”服务)根据其他事件更新其状态时,情况就不那么自然了(尽管您可以这样做)。我认为这里采用的异步序列模式没有内在缺陷。但您可以在此处采用任何一种模式。事实上,它们只是使用 从发布者创建异步序列
values
,因此两者有点混合。如果您要使用异步序列,我们应该注意,其中所有的非结构化并发
observeState
都是一个值得怀疑的实现。它启动了四个非结构化任务,但无法取消它们。当然,他们是从 启动的
AVCamApp
,所以在这种情况下,他们并没有考虑停止CameraModel
,但我真的不鼓励这种模式。不应该加入不可取消的行为,尤其是在开发人员可能倾向于复制和/或纳入其项目的演示项目中。通常会保存对这些任务的引用,然后提供
stop
/cancel
函数来取消它们。或者,在这种情况下,更好的方法是放弃非结构化并发,将这四个任务包装在一个任务组中:然后,我们将改变
start
(我将其重命名run
)以最后执行此操作:然后
View
可以做类似的事情:现在,再次重申,原始代码示例是在 中执行此操作的
App
,因此此问题并未在此处出现。但上述内容将是一个更通用的解决方案,既可以在 中工作App
,也可以在 中工作View
。原始代码使用所有这些非结构化并发,并且没有考虑取消,这是一种反模式。我们希望
CameraModel
能够从App
或 中使用View
。(我将忽略 是否CameraModel
是此对象的好名称;在我看来,它感觉像是一种“服务”,而不是一个简单的“模型”。)