标签: combine

相当于在 Swift Combine 中使用 @Published 的计算属性?

在命令式 Swift 中,通常使用计算属性来提供对数据的便捷访问,而无需复制状态。

假设我有这个类用于命令式 MVC 使用:

class ImperativeUserManager {
    private(set) var currentUser: User? {
        didSet {
            if oldValue != currentUser {
                NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
                // Observers that receive this notification might then check either currentUser or userIsLoggedIn for the latest state
            }
        }
    }

    var userIsLoggedIn: Bool {
        currentUser != nil
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

如果我想使用Combine 创建一个反应式等效项,例如用于SwiftUI,我可以轻松添加@Published到存储的属性以生成Publishers,但不能用于计算属性。

    @Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
        currentUser != …
Run Code Online (Sandbox Code Playgroud)

ios swift reactive swiftui combine

55
推荐指数
3
解决办法
2万
查看次数

SwiftUI:随机“调用中的额外参数”错误

所以我正在尝试学习 SwiftUI 和 Combine。我通常通过制作一个简单的小费计算器来开始新技术。

我似乎收到了一个随机的“通话中的额外参数”。编码时出错这是我的 SwiftUI 文件

import SwiftUI

internal enum ReceiptRowType {
    case subtotal
    case tax
    case total
    case tip
    case grandTotal
}

struct TipView: View {
    @ObservedObject internal var adBannerView: BannerAdView = BannerAdView()
    @ObservedObject internal var receiptViewModel: ReceiptViewModel

    private let percentageFormatter: NumberFormatter = {
        let f = NumberFormatter()
        f.numberStyle = .percent
        return f
    }()

    var body: some View {
        ZStack {
            Color.white
                .scaledToFit()

            VStack {
                if adBannerView.adHasLoaded {
                    adBannerView
                        .frame(maxHeight: adBannerView.adHeight)
                        .animation(.easeInOut(duration: 2.0))
                }

                BorderView()

                Text(ARCHLocalizedStrings.receipt)
                    .foregroundColor(Color.gray)

                BorderView() …
Run Code Online (Sandbox Code Playgroud)

swiftui combine

45
推荐指数
2
解决办法
1万
查看次数

当 SwiftUI 中的相关实体发生更改时,如何更新 @FetchRequest?

在 SwiftUI 中,View我有一个List基于@FetchRequest显示Primary实体和通过关系连接Secondary实体的数据。当我添加一个带有新的相关次要实体的新实体时,View和它List的更新正确Primary

问题是,当我Secondary在详细视图中更新连接的项目时,数据库会更新,但更改未反映在Primary列表中。显然,@FetchRequest不会被另一个视图中的更改触发。

当我此后在主视图中添加新项目时,先前更改的项目最终会得到更新。

作为一种解决方法,我还更新Primary了详细信息视图中实体的属性,并将更改正确传播到Primary视图。

我的问题是:如何强制更新@FetchRequestsSwiftUI Core Data 中的所有相关内容?特别是,当我无法直接访问相关实体时/ @Fetchrequests

数据结构

import SwiftUI

extension Primary: Identifiable {}

// Primary View

struct PrimaryListView: View {
    @Environment(\.managedObjectContext) var context

    @FetchRequest(
        entity: Primary.entity(),
        sortDescriptors: [NSSortDescriptor(key: "primaryName", ascending: true)]
    )
    var fetchedResults: FetchedResults<Primary>

    var body: some View {
        List {
            ForEach(fetchedResults) { primary in
                NavigationLink(destination: …
Run Code Online (Sandbox Code Playgroud)

core-data swiftui combine

36
推荐指数
3
解决办法
1万
查看次数

来自 ObservableObject 的绑定值

目的:

我有一个模型,它是一个ObservableObject. 它有一个Bool属性,我想用这个Bool属性来初始化一个@Binding变量。

问题:

  1. 如何将@ObservableObjecta转换为 a @Binding
  2. 创建 a@State是初始化 a 的唯一方法@Binding吗?

笔记:

  • 我知道我可以使用@ObservedObject/ @EnvironmentObject,而且我看到它很有用,但我不确定一个简单的按钮是否需要访问整个模型。
  • 还是我的理解不正确?

代码:

import SwiftUI
import Combine
import SwiftUI
import PlaygroundSupport

class Car : ObservableObject {

    @Published var isReadyForSale = true
}

struct SaleButton : View {

    @Binding var isOn : Bool

    var body: some View {

        Button(action: {

            self.isOn.toggle()
        }) {
            Text(isOn ? "On" : "Off")
        }
    } …
Run Code Online (Sandbox Code Playgroud)

swift swiftui combine

36
推荐指数
2
解决办法
2万
查看次数

SwiftUI - 确保在模型更新时从主线程发布值(通过像 receive(on:) 这样的运算符)

我的应用程序包含一个资源密集型操作,该操作根据从 XML 提要中提取的数据填充数组。我不希望此操作锁定主线程(以及为数组提供新数据时的 UI),因此它是在后台完成的。

let dispatchQueue = DispatchQueue(label: "concurrent.queue", qos: .utility, attributes: .concurrent)

class XMLHandler: ObservableObject {
    let context: NSManagedObjectContext

    @Published var myArray: [CustomObject] = []

    init(context: NSManagedObjectContext) {
        self.context = context
    }
    
    ...some code...
    
    func populateArray {
      dispatchQueue.async {
       ...xml parsing happens...
       (xmlOutputObject) in 
          for x in xmlOutputObject {
              self.myArray.append(x)
          }
      }
    }
}
Run Code Online (Sandbox Code Playgroud)

在其他地方,我的 SwiftUI 视图使用 myArray 来填充它的列表:

struct MyView: View {

 @EnvironmentObject var handler: XMLHandler
    var body: some View {
            List{
                ForEach(handler.myArray) { CustomObject in
              ... generate …
Run Code Online (Sandbox Code Playgroud)

swiftui combine

31
推荐指数
4
解决办法
4万
查看次数

将 Just 与 flatMap 一起使用会产生失败不匹配。结合

我有这样的代码

func request(request: URLRequest) -> AnyPublisher<Data, Error> {
    return Just(request)
        .flatMap { request in
            RequestManager.request(request) // returns AnyPublisher<Data, Error>
    }
    .eraseToAnyPublisher()
}
Run Code Online (Sandbox Code Playgroud)

我收到编译错误:

实例方法 flatMap(maxPublishers:_:) 要求类型 Just.Failure(又名 Never)和 Error 是等价的

很明显,因为Justhas NeverasFailure.flatMaprequires Erroras Failure,所以Never!=Error

我看到两种方法:

  • 使用正确的Publisher 而不是Just,但我没有找到合适的人选。

  • 使用诸如.mapError, 之类的运算符.mapError { $0 as Error },但我不确定这是个好主意。

任何想法如何处理它?

更新:

使用更有意义

.setFailureType(to: Error.self)
Run Code Online (Sandbox Code Playgroud)

或者

.mapError { $0 as Error }
Run Code Online (Sandbox Code Playgroud)

swift combine

28
推荐指数
1
解决办法
7163
查看次数

结合框架序列化异步操作

如何让构成Combine框架的异步管道同步(串行)排队?

假设我有 50 个 URL,我想从中下载相应的资源,假设我想一次下载一个。我知道如何使用 Operation/OperationQueue 来做到这一点,例如使用一个 Operation 子类,该子类在下载完成之前不会声明自己已完成。我将如何使用Combine 做同样的事情?

目前我想到的只是保留一个剩余 URL 的全局列表并弹出一个,为一次下载设置一个管道,进行下载,然后在sink管道中重复。这似乎不太像结合。

我确实尝试制作一组​​ URL 并将其映射到一组发布者。我知道我可以“生产”一个发布者,并使用flatMap. 但后来我仍然在同时进行所有下载。没有任何组合方式以受控方式遍历阵列——或者有吗?

(我也想象过用 Future 做点什么,但我变得绝望了。我不习惯这种思维方式。)

ios swift combine

27
推荐指数
4
解决办法
6262
查看次数

iOS Swift 组合:取消 Set&lt;AnyCancellable&gt;

如果我已将可取消集存储到 ViewController 中:

private var bag = Set<AnyCancellable>()
Run Code Online (Sandbox Code Playgroud)

其中包含多个订阅。

1 - 我应该在 deinit 中取消订阅吗?或者它会自动完成工作?

2 - 如果是这样,我如何取消所有存储的订阅?

bag.removeAll() is enough?
Run Code Online (Sandbox Code Playgroud)

还是我应该遍历集合并一一取消所有订阅?

for sub in bag {
   sub.cancel()
}
Run Code Online (Sandbox Code Playgroud)

Apple 表示订阅一直有效,直到存储的 AnyCancellable 在内存中。所以我想解除分配可取消的bag.removeAll()应该足够了,不是吗?

ios swift ios13 swiftui combine

25
推荐指数
4
解决办法
2万
查看次数

使用 Apple 的新组合框架时如何防止强引用循环(.assign 导致问题)

我不太明白如何在类中正确存储订阅者,以便它们持续存在但不会阻止对象被取消初始化。这是对象不会取消初始化的示例:

import UIKit
import Combine

class Test {
    public var name: String = ""

    private var disposeBag: Set<AnyCancellable> = Set()

    deinit {
        print("deinit")
    }

    init(publisher: CurrentValueSubject<String, Never>) {
        publisher.assign(to: \.name, on: self).store(in: &disposeBag)
    }
}

let publisher = CurrentValueSubject<String, Never>("Test")

var test: Test? = Test(publisher: publisher)
test = nil

Run Code Online (Sandbox Code Playgroud)

当我assign用 a替换sink(在其中我正确声明[weak self])时,它实际上确实正确地进行了 deinit(可能是因为assign访问self的方式会导致问题)。

例如,如何在使用时防止强引用循环.assign

谢谢

swift combine

23
推荐指数
2
解决办法
5309
查看次数

什么是 PassthroughSubject 和 CurrentValueSubject

我碰巧研究了 Apple 的新组合框架,在那里我看到了两件事

PassthroughSubject<String, Failure>

CurrentValueSubject<String, Failure>

有人可以向我解释它们的含义和用途吗?

reactive-programming declarative-programming ios swiftui combine

22
推荐指数
5
解决办法
2万
查看次数