我观看了WWDC 2019会议``实践结合''(https://developer.apple.com/videos/play/wwdc2019/721/)。
在视频中,他们使用以下语法创建了发布者:
@Published var someName: String = ""
Run Code Online (Sandbox Code Playgroud)
他们这样做是为了someName成为出版商。但是,Xcode不喜欢这种语法,并给我一个错误:
未知属性“已发布
我不知道为什么。我正在macOS Catalina上使用Xcode 11 beta。
有任何想法吗?
在 Apple 的 2019 年 WWDC 视频中Swift Combine in Practice,他们演示了使用debounce发布者来减慢消息速度。
return $username
.debounce(for: 0.5, scheduler: RunLoop.main)
.removeDuplicates()
.eraseToAnyPublisher()
Run Code Online (Sandbox Code Playgroud)
但是,每当我尝试以类似的方式使用它时,都会出现以下错误:
无法使用类型为“(for:Double,调度程序:RunLoop)”的参数列表调用“debounce”
该debounce()签名是:
public func debounce<S>(for dueTime: S.SchedulerTimeType.Stride,
scheduler: S,
options: S.SchedulerOptions? = nil) ->
Publishers.Debounce<Self, S> where S : Scheduler
Run Code Online (Sandbox Code Playgroud)
SchedulerTimeType.Stride 似乎可以用数字初始化,但它对我不起作用,或者我对 Swift 泛型缺乏经验。
调用它的正确方法是什么?
编辑
重复这个问题...
目前,搜索诸如“组合”之类的通用词相当具有挑战性......
macOS 10.15,Xcode 11
SwiftUI 具有使用 的隐式动画.animate()和使用 的显式动画.withAnimation()。但是,我无法弄清楚如何为图像更改设置动画:
struct ImageViewWidget : View {
@ObjectBinding var imageLoader: ImageLoader
init(imageURL: URL) {
imageLoader = ImageLoader(imageURL: imageURL)
}
var body: some View {
Image(uiImage:
(imageLoader.data.count == 0) ? UIImage(named: "logo-old")! : UIImage(data: imageLoader.data)!)
.resizable()
.cornerRadius(5)
.frame(width: 120, height:120)
}
}
Run Code Online (Sandbox Code Playgroud)
该Image的uiImage参数传递的old-logo(预留),如果有中没有数据imageLoader(一BindableObject),并用正确的一个替换它一旦这样异步加载:
class ImageLoader : BindableObject {
let didChange = PassthroughSubject<Data, Never>()
var data = Data() {
didSet {
didChange.send(data)
}
}
init(imageURL: URL) …Run Code Online (Sandbox Code Playgroud) 我一直在看通过SwiftUI WWDC进行的数据流。他们有一张带有示例代码的幻灯片,其中他们使用Timer发布者,该发布者连接到SwiftUI视图,并随时间更新UI。
我正在编写一些代码,在其中我想做完全相同的事情,但是无法弄清楚它PodcastPlayer.currentTimePublisher是如何实现的,然后将其连接到UI结构。我还看了所有关于Combine的视频。
我该如何实现?
示例代码:
struct PlayerView : View {
let episode: Episode
@State private var isPlaying: Bool = true
@State private var currentTime: TimeInterval = 0.0
var body: some View {
VStack { // ...
Text("\(playhead, formatter: currentTimeFormatter)")
}
.onReceive(PodcastPlayer.currentTimePublisher) { newCurrentTime in
self.currentTime = newCurrentTime
}
}
}
Run Code Online (Sandbox Code Playgroud) 我发现了这个问题SwiftUI: Putting multiple BindableObjects into Envionment
答案说 environmentObject(ObservableObject) 返回修改后的视图,因此我可以为多个 environmentObject 创建调用链。
喜欢
let rootView = ContentView()
.environmentObject(firstBindable)
.environmentObject(secondBindable)
Run Code Online (Sandbox Code Playgroud)
我想知道如果 firstBindable 和 secondBindable 是相同类型的结果是什么。如何.environmentObject()知道什么是程序员打算在firstBindable和之间设置的 exec 值secondBindable。
所以我测试了这个
final class TempStr: ObservableObject {
@Published var tmpStr = "temp"
init(initStr: String) {
tmpStr = initStr
}
}
Run Code Online (Sandbox Code Playgroud)
window.rootViewController
= UIHostingController(rootView:
TestView()
.environmentObject(TempStr(initStr: "1st"))
.environmentObject(TempStr(initStr: "2nd"))
Run Code Online (Sandbox Code Playgroud)
struct TestView: View {
@EnvironmentObject var tmp1: TempStr
@EnvironmentObject var tmp2: TempStr
var body: some …Run Code Online (Sandbox Code Playgroud) 我有一个核心数据模型,其中一个实体生成到 class 中Task。我正在尝试objectWillChange从NSManagedObject发送(自动,无需手动工作)中获取合并发布者,但它不会。任务实体有一个name属性。
let task = Task(context: container.viewContext)
let taskSubscription = task.objectWillChange.sink(receiveValue: { _ in
print("Task changed")
})
task.name = "Foo" // WILL NOT trigger
Run Code Online (Sandbox Code Playgroud)
如果我手动调用发送,订阅将起作用:
task.objectWillChange.send() // Will trigger
Run Code Online (Sandbox Code Playgroud)
如果我用一个 simple 替换ObservableObject它,它将按预期工作:
class DummyTask: ObservableObject {
@Published var name: String?
}
Run Code Online (Sandbox Code Playgroud)
let dummy = DummyTask()
let dummySubscription = dummy.objectWillChange.sink(receiveValue: { _ in
print("Dummy changed")
})
dummy.name = "Foo" // Will trigger
dummy.objectWillChange.send() // Will trigger
Run Code Online (Sandbox Code Playgroud)
NSManagedObject 被窃听了吗?我应该如何观察通用实体对象的变化?我应该如何让 SwiftUI 看到它们? …
我有一些使用 RxSwift 构建的代码,我正在尝试将其转换为使用 Apple 的 Combine 框架。
一种非常常见的模式是使用Observable.create一次性可观察对象(通常是网络请求)。像这样的东西:
func loadWidgets() -> Observable<[Widget]> {
return Observable.create { observer in
// start the request when someone subscribes
let loadTask = WidgetLoader.request("allWidgets", completion: { widgets in
// publish result on success
observer.onNext(widgets)
observer.onComplete()
}, error: { error in
// publish error on failure
observer.onError()
})
// allow cancellation
return Disposable {
loadTask.cancel()
}
}
}
Run Code Online (Sandbox Code Playgroud)
我正在尝试将其映射到结合,但我一直无法弄清楚。我能得到的最接近的是使用 Future 来做这样的事情:
func loadWidgets() -> AnyPublisher<[Widget], Error> {
return Future<[Widget], Error> { resolve in
// …Run Code Online (Sandbox Code Playgroud) 有没有办法指定 count 应该只在主线程上发布?我看过一些讨论使用 设置发布者的文档receive(on:),但在这种情况下,@Publisher包装器隐藏了该逻辑。
import SwiftUI
import Combine
class MyCounter: ObservableObject {
@Published var count = 0
public static let shared = MyCounter()
private init() { }
}
struct ContentView: View {
@ObservedObject var state = MyCounter.shared
var body: some View {
return VStack {
Text("Current count: \(state.count)")
Button(action: increment) {
HStack(alignment: .center) {
Text("Increment")
.foregroundColor(Color.white)
.bold()
}
}
}
}
private func increment() {
NetworkUtils.count()
}
}
public class NetworkUtils {
public static …Run Code Online (Sandbox Code Playgroud) 当使用 Swift/Combine 中的 retry() 发生某些错误时,我想多次发出网络请求。发布者内部的块只被调用一次,这意味着当发生错误时,对真正的应用程序只有一个请求。我的代码是:
import UIKit
import Combine
import PlaygroundSupport
enum TestFailureCondition: Error {
case invalidServerResponse
}
var backgroundQueue: DispatchQueue = DispatchQueue(label: "backgroundQueue")
var failPublisher: AnyPublisher<(Data, URLResponse), Error> {
Future<(Data, URLResponse), Error> { promise in
print("Attempt to call")
backgroundQueue.asyncAfter(deadline: .now() + Double.random(in: 1..<3)) {
promise(.failure(TestFailureCondition.invalidServerResponse))
}
}
.eraseToAnyPublisher()
}
let cancellable = failPublisher
.print("(1)>")
.retry(3)
.print("(2)>")
.sink(receiveCompletion: { fini in
print(" ** .sink() received the completion:", String(describing: fini))
PlaygroundPage.current.finishExecution()
}, receiveValue: { stringValue in
print(" ** .sink() received \(stringValue)") …Run Code Online (Sandbox Code Playgroud) 我正试图将我的头围绕在 Combine 上。
这是我想转换为Combine 的方法,以便它返回AnyPublisher。
func getToken(completion: @escaping (Result<String, Error>) -> Void) {
dispatchQueue.async {
do {
if let localEncryptedToken = try self.readTokenFromKeychain() {
let decryptedToken = try self.tokenCryptoHelper.decrypt(encryptedToken: localEncryptedToken)
DispatchQueue.main.async {
completion(.success(decryptedToken))
}
} else {
self.fetchToken(completion: completion)
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
整个过程在单独的调度队列上执行,因为从 Keychain 读取和解密可能很慢。
我第一次尝试拥抱结合
func getToken() -> AnyPublisher<String, Error> {
do {
if let localEncryptedToken = try readTokenFromKeychain() {
let decryptedToken = try tokenCryptoHelper.decrypt(encryptedToken: localEncryptedToken)
return Result.success(decryptedToken).publisher.eraseToAnyPublisher()
} else { …Run Code Online (Sandbox Code Playgroud)