【问题标题】:"Extra argument" error when using CombineLatest from the Combine framework使用 Combine 框架中的 CombineLatest 时出现“额外参数”错误
【发布时间】:2020-06-03 23:05:28
【问题描述】:

可以在以下操场中看到该问题。有四个已发布的值将异步更新(一个图像和三个字符串)。当所有四个都已初始化或随后更改时,将需要更新 UI。当我尝试使用 CombineLatest4 捕获此数据流时,编译器立即使用消息 Extra argument in call 反对第四个参数。 (注意:以下代码实际上并没有做任何事情,因为它只有一个发布者,但足以在 Playground 中生成错误消息。

import Combine
import UIKit

struct CustomerUpdates
{
    @Published var photo: UIImage!
    @Published var firstName: String!
    @Published var lastName: String!
    @Published var id: String!

    typealias customerTuple =
        (   photo: UIImage,
            firstName: String,
            lastName: String,
            id: String )
    var validatedCustomer: AnyPublisher< customerTuple, Never >
    {
        return Publishers.CombineLatest4( $photo,
                                          $firstName,
                                          $lastName,
                                          $id )
        {
            photo, firstName, lastName, id in
            if      photo == nil
                ||  firstName == nil
                ||  lastName == nil
                ||  id == nil
            {
                return nil
            }
            return ( photo!, firstName!, lastName!, id! )
        }
        .compactMap
        .return( on: RunLoop.main )
    }
}

我的问题是,为什么编译器会标记第四个参数(“id”)? Apple 的 CombineLatest4 通用结构文档说:

接收并结合来自四个方面的最新元素的发布者 出版商。

【问题讨论】:

    标签: ios swift compiler-errors combine


    【解决方案1】:

    CombineLatest 类型(及其更大的变体,包括CombineLatest4)不采用转换闭包。但是PublishercombineLatest operators 这样做。所以你可以这样说:

        return $photo.combineLatest($firstName, $lastName, $id) {
            guard
                let photo = $0,
                let firstName = $1,
                let lastName = $2,
                let id = $3
                else { return nil }
            return ($0, $1, $2, $3)
        }
        .compactMap { $0 }
        .receive(on: RunLoop.main)
    

    【讨论】:

    • 有趣!我错过了 Type 和 operator 是如此不同。也就是说,WWDC 视频“Combine In Practice”中的示例显示了一个示例(跳到 26:06 分钟),他们创建了一个带有尾随闭包的 CombineLatest 类型的实例。因此,请注意,所有联合学生,WWDC 代码不再有效!
    【解决方案2】:

    问题在于CombineLatest 没有关闭,它只是在任何上游发出新值时发出从其所有上游发出的最新值。您的闭包应该提供给compactMap,它是一个运算符,它接受一个返回Optional 的闭包,并且只有在返回值不是nil 时才向下游发出一个值。

    struct CustomerUpdates {
        @Published var photo: UIImage!
        @Published var firstName: String!
        @Published var lastName: String!
        @Published var id: String!
    
        typealias CustomerTuple = (photo: UIImage, firstName: String, lastName: String, id: String)
        var validatedCustomer: AnyPublisher<CustomerTuple, Never> {
    
            return Publishers.CombineLatest4($photo, $firstName, $lastName, $id)
                .compactMap { photo, firstName, lastName, id in
                    guard let photo = photo, let firstName = firstName, let lastName = lastName, let id = id else { return nil }
                    return ( photo, firstName, lastName, id)
                }
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
        }
    }
    

    与您的问题无关的其他几个问题:没有这样的运算符return(on:),您需要receive(on:),并且您希望通过DispatchQueue.main,而不是RunLoop.main,以便能够将更新直接发布到您的UI。

    在 Swift 中类型名称应为 UpperCamelCase,因此请使用 CustomerTuple 而不是 customerTuple

    此外,您的属性应该是 Optional (var photo: UIImage?),而不是隐式展开的可选 (var photo: UIImage!)。

    【讨论】:

    • 谢谢! WWDC 2019 上的“Combine In Practice”视频展示了带有尾随闭包的 CombineLatest 的实例化。我想从那以后事情一定发生了变化。我使用“return(on:)”的错误,但该视频再次显示了“RunLoop.main”的使用。我希望其他正在观看该视频的人看到您的回复。超级有用!
    • RunLoop.mainDispatchQueue.main 通常都可以工作,因为两者都符合 Combine.Scheduler 协议。
    猜你喜欢
    • 2019-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多