不符合协议BindableObject-Xcode 11 Beta 4

时间:2019-07-18 05:28:27

标签: swiftui xcode11

尝试一些示例。找到了一个项目,该项目的类是可绑定的对象,没有给出任何错误。现在Xcode 11 beta 4已经发布,我得到了错误:

  

类型'UserSettings'不符合协议'BindableObject'

它在错误上有一个修复按钮,当您单击它时,它会添加

typealias PublisherType = <#type#>

它希望您填写类型。

类型是什么?

class UserSettings: BindableObject {

    let didChange = PassthroughSubject<Void, Never>()

    var score: Int = 0 {
        didSet {
            didChange.send()
        }
    }
}

4 个答案:

答案 0 :(得分:29)

Beta 4 Release notes说:

  

BindableObject协议的要求现在是willChange而不是   didChange,现在应该在对象更改之前发送   比它改变之后。此更改可以改善   更改通知。 (51580731)

您需要将代码更改为:

class UserSettings: BindableObject {

    let willChange = PassthroughSubject<Void, Never>()

    var score: Int = 0 {
        willSet {
            willChange.send()
        }
    }
}

答案 1 :(得分:3)

SwiftUI和Combine是在WWDC 2019上宣布的两个新框架。这两个框架在WWDC 2019上引起了很多关注,这些技术在其中的使用次数都得到了证明。

SwiftUI引入为

一种革命性的新方法,可以更快地构建更好的应用程序。

组合被描述为

用于处理随时间变化的值的统一声明框架

在初始版本和现在(2020年5月,Swift 5.2)之间,发生了一些更改。可能已经看过WWDC视频的SwiftUI和Combine的新手可能会对这两个框架如何协同工作提出一些疑问。

Combine定义了两个接口:Publisher和Subscriber。发布者将事件发送给订阅者。请参见下面的序列图。

sequence diagram for combine

如果您在SwiftUI中启动一个应用程序,然后添加Combine,将不会提及发布者或订阅者,这是使用Combine的两个主要参与者。在下面考虑这个非常简单的示例应用程序。

import SwiftUI
import Combine
import SwiftUI

final class ActorViewModel: ObservableObject {
    var name : String
    private var imageUrl : URL?
    //@Published
    private (set) var image : Image = Image(systemName: "photo") {
        willSet {
            DispatchQueue.main.async {
                self.objectWillChange.send()
            }
        }
    }

    init(name: String, imageUrl: URL?) {
        self.name = name
        self.imageUrl = imageUrl
        self.fetchImage()
    }

    private func fetchImage() {
        guard nil != self.imageUrl,
            String() != self.imageUrl!.absoluteString else { return }
        let task = URLSession.shared.dataTask(with: self.imageUrl!) { (data, response, error) in

            guard nil == error , nil != response, nil != data,
                let uiImage = UIImage(data: data!) else { return }
                self.image = Image(uiImage: uiImage)

        }
        task.resume()
    }
}

struct ContentView: View {
    @ObservedObject var actor : ActorViewModel

    var body: some View {
        HStack {
            actor.image
                .resizable()
                .aspectRatio(contentMode: ContentMode.fit)
                .frame(width: 60, height: 60)
            Text(actor.name)
        }

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let actor = ActorViewModel(name: "Mark Hammill",
                                   imageUrl: URL(string: "https://m.media-amazon.com/images/M/MV5BOGY2MjI5MDQtOThmMC00ZGIwLWFmYjgtYWU4MzcxOGEwMGVkXkEyXkFqcGdeQXVyMzM4MjM0Nzg@._V1_.jpg"))
        return ContentView(actor: actor)
    }
}

通过画布进行的应用预览如下所示:

app preview

该应用程序使用列表视图显示演员的姓名和图像。只有两类要考虑:

  1. ContentView-SwiftUI View子类
  2. ActorViewModel-ContentView的数据源(由于它在MVVM中扮演VM的角色,因此称为ViewModel)

根据下面的类图,视图引用了actor对象。

application class diagram

尽管此示例使用的是Combine,但这并不是立即可见的。没有提及发布者或订阅者。发生了什么事?

答案:查看类层次结构可填补缺失的空白。下面的类图说明了完整图片(单击图片可查看更多详细信息)。

combine UML

咨询Apple的文档提供了以下类型的定义:

  • ObservedObject :一种属性包装器类型,可订阅可观察对象,并在可观察对象发生更改时使视图无效。
  • ObservableObject :一种具有发布者的对象类型,该对象在更改对象之前发出。默认情况下,ObservableObject会合成一个objectWillChange发布者,该发布者会在其任何@Published属性更改之前发出更改的值。
  • objectWillChange :在对象更改之前发出的发布者。
  • PassthroughSubject :向下游订户广播元素的主题。作为Subject的具体实现,PassthroughSubject提供了一种方便的方法,可以将现有的命令式代码调整为Combine模型。

首先,考虑@ObservedObject的含义。这是一个属性包装器。属性包装器可减少代码重复,并在声明隐藏属性存储和定义方式的属性时使用简洁的语法。在这种情况下,“观察到的对象”是观察另一个对象的属性。

换句话说,该属性是 Subscriber (来自Combine框架)。参与者(通过使用属性包装器)是订阅服务器,它订阅了 Publisher ,但是在这种情况下,发布服务器是什么?

“可观察对象”本身不是发布者,而是拥有发布者。 ActorViewModel符合ObservableObject协议。通过这样做,它通过扩展名(框架在ObservableObject协议上提供)为发布者属性提供了名为objectWillChange的发布者属性。此objectWillChange属性的类型为PassthroughSubject,它是Publisher协议的一种具体类型。传递主题具有称为send的属性,该属性是用于将数据发送到任何订阅者的发布者方法。因此,名为“ objectWillChange”的属性是 Publisher

总而言之,订阅服务器是ContentView类中名为actor的属性,而发布服务器是ActorViewModel类中名为objectWillChange的属性。订阅者订阅发布者需要什么? “ @ObservedObject”属性包装器本身就是一个订阅服务器,因此它必须订阅发布服务器。但是,视图如何找出发送到订阅服务器的更改?这由SwiftUI框架处理,我们从未见过。

实用知识:我们不必担心将视图订阅给发布者。另一方面,我们确实需要担心要确保发布者告诉订阅者什么时候要进行更改。从远程服务器上获取图像并将数据转换为图像对象后,我们调用objectWillChange.send()通知View。订阅者从发布者那里收到即将发生更改的通知后,视图便失效(导致视图重绘)。

摘要 SwiftUI使用ObservedObject PropertyWrapper的方式从表面上看并没有放弃在公式中甚至存在Combine的事实。但是通过检查Observ ed Object和Observ able Object,可以揭示底层的Combine框架以及设计模式:

订户->订阅发布者->然后发布发布->订户收到的更改

参考:

答案 2 :(得分:0)

在Xcode 11.2.1中

BindableObject更改为ObservableObject。

ObjectBinding现在是ObservedObject。

didChange应该更改为objectWillChange。

列表(dataSource.pictures,id:.self){} 您现在也可以摆脱did / willChange发布者和.send代码,而只需将图片制作为@Published

其余的将自动为您生成。

例如:

import SwiftUI
import Combine
import Foundation

class RoomStore: ObservableObject {
    @Published var rooms: [Room]
    init(rooms: [Room]) {
        self.rooms = rooms
    }
}

struct ContentView: View {
    @ObservedObject var store = RoomStore(rooms: [])
}

ref:https://www.reddit.com/r/swift/comments/cu8cqk/getting_the_errors_pictured_below_when_try_to/

答案 3 :(得分:-2)

为什么发送通知之前可以更改值?我很困惑。 有了didChange,一切对我而言都是合乎逻辑且清晰的