标签: combine

使用 Xcode 12 访问 SwiftUI 中的 ViewModel 字段:“访问安装在视图之外的状态值”

我认为此错误消息是 Xcode 12 中的 SwiftUI 的新消息,因为它在 Google 中的点击率为 0,而该消息本身相当通用:

在安装在 View 之外访问 State 的值。这将导致初始值的常量绑定并且不会更新。

我有以下代码(删除了一些绒毛):

public struct ContentView: View {
    @ObservedObject var model: RootViewModel

    public var body: some View {
        VStack(alignment: .center, content: {
            Picker(selection: model.$amount, label: Text("Amount")) {
                Text("€1").tag(1)
                Text("€2").tag(2)
                Text("€5").tag(5)
                Text("€10").tag(10)
            }.pickerStyle(SegmentedPickerStyle())
            Text("Donating: €\(model.amount)").font(.largeTitle)
        }).padding(.all, 20.0)
    }
}

public class RootViewModel: ObservableObject {
    @State public var amount: Int = 1
}
Run Code Online (Sandbox Code Playgroud)

我曾经有field权利,ContentView而且工作正常。现在 UI 不再更新,而是我收到了运行时警告。

swift swiftui combine xcode12

10
推荐指数
2
解决办法
5366
查看次数

使用Swift Combine创建Timer Publisher

我一直在看通过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)

publisher swift swiftui combine

9
推荐指数
3
解决办法
1355
查看次数

NSManagedObject 更改不会触发 objectWillChange

我有一个核心数据模型,其中一个实体生成到 class 中Task。我正在尝试objectWillChangeNSManagedObject发送(自动,无需手动工作)中获取合并发布者,但它不会。任务实体有一个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 看到它们? …

core-data swift swiftui combine

9
推荐指数
2
解决办法
2557
查看次数

Swift Combine 替代 Rx Observable.create

我有一些使用 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)

swift combine

9
推荐指数
1
解决办法
2580
查看次数

在主线程上使用@Published 值?

有没有办法指定 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 swiftui combine

9
推荐指数
2
解决办法
2612
查看次数

迅速。结合。有没有办法在重试时多次调用发布者块?

当使用 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)

swift combine

9
推荐指数
2
解决办法
2889
查看次数

使用 SwiftUI 绑定 ViewModel 和 TextFields

我正在寻找在textfields和之间创建绑定的最佳方法ViewModel。目前我正在@State为每个文本字段创建一个,并在需要时手动将值从文本字段发送到 viewModel 属性。我很确定这不是最好的方法......有没有办法将TextFieldViewModel财产绑定?

这是我当前的代码:

struct SigninView: View {
    @State var username:String = ""
    @State var password:String = ""

    var viewModel:SignInViewModel

    var body: some View {
        VStack(alignment: .leading, spacing: 15.0){

            DefaultTextField(placeholder: "username", value: $username)
            DefaultTextField(placeholder: "password", value: $password)

            Spacer()

            FillButton(title:"Sign In"){
                ///// IS THIS CORRECT?
                self.viewModel.email = self.username
                self.viewModel.password = self.password
                //------------------
                self.viewModel.signin()
            }
        }.padding()
    }
}
Run Code Online (Sandbox Code Playgroud)

视图模型类似于:

class SignInViewModel:ObservableObject{

    var username:String? = nil
    var password:String? = nil
Run Code Online (Sandbox Code Playgroud)

ios swiftui combine

9
推荐指数
2
解决办法
6296
查看次数

如何正确测试 XCTestCase 中 viewModel 中通过发布者更改的 var

我正在尝试在合并框架和 SwiftUI 中测试一个简单的发布者。我的测试在我的视图模型中测试了一个名为 isValid 的已发布布尔值。我的视图模型还有一个已发布的用户名字符串,当该字符串发生更改并变为 3 个或更多字符时,isValid 会被分配该值。这是视图模型。我确信我不了解发布商如何在测试环境、时间安排等中工作......提前致谢。

public class UserViewModel: ObservableObject {
  @Published var username = ""
  @Published var isValid = false
  private var disposables = Set<AnyCancellable>()

  init() {
    $username
      .receive(on: RunLoop.main)
      .removeDuplicates()
      .map { input in
        print("~~~> \(input.count >= 3)")
        return input.count >= 3
    }
    .assign(to: \.isValid, on: self)
    .store(in: &disposables)
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我的观点,这里并不重要

struct ContentView: View {
  @ObservedObject private var userViewModel = UserViewModel()
  var body: some View {
    TextField("Username", text: $userViewModel.username)
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我的测试文件和失败的单个测试

class StackoverFlowQuestionTests: XCTestCase …
Run Code Online (Sandbox Code Playgroud)

swift swiftui combine

9
推荐指数
2
解决办法
2187
查看次数

如何查看 URLSession.dataTaskPublisher 的当前进度?

我正在使用 adataTaskPublisher来获取一些数据:

func downloadData(_ req: URLRequest) {
  self.cancelToken = dataTaskPublisher(for: req).sink { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

如果在请求正在进行时调用该函数,我想返回。

目前我要么:
1.cancelToken在接收器中将设置为 nil 或
2. Crate 并管理一个isDownloading变量。

是否有内置的方法来检查是否dataTaskPublisher正在运行(以及可选的进度)?

swift combine

9
推荐指数
2
解决办法
953
查看次数

Swift 合并错误:'Publisher' 上的方法需要 .Failure'(又名 'WeatherError')和 'Never' 是等价的

我现在正在学习 Swift Combine,找到了非常简单的视频教程,但是由于某种原因,当我尝试在 PassthroughSubject<Int, WeatherError>() 中使用我的枚举时出现错误

检查此代码:

import Combine 

enum WeatherError: Error {
   case thingsJustHappen
   
}
let weatherPublisher = PassthroughSubject<Int, WeatherError>()


let subscriber = weatherPublisher
   .filter {$0 > 10}
   .sink { value in
       print("\(value)")
   }

weatherPublisher.send(10)
weatherPublisher.send(30)
Run Code Online (Sandbox Code Playgroud)

“.filter”突出显示,错误是:

Referencing instance method 'sink(receiveValue:)' on 'Publisher' 
requires the types 'Publishers.Filter<PassthroughSubject<Int, WeatherError>>.Failure' 
(aka 'WeatherError') and 'Never' be equivalent
Run Code Online (Sandbox Code Playgroud)

令人惊讶的是,这段代码在视频教程中有效。我怎样才能让我的 WeatherError 和 Never 相等???

xcode ios swift combine

9
推荐指数
1
解决办法
1671
查看次数

标签 统计

combine ×10

swift ×9

swiftui ×6

ios ×2

core-data ×1

publisher ×1

xcode ×1

xcode12 ×1