Swift Combine-@Published属性数组

时间:2019-08-04 16:52:23

标签: swift swiftui combine

我目前正在使用SwiftUI和Combine进行项目。我正在使用Xcode11 Beta5。我想获取我的Github存储库,显示它们,然后能够对其中一些添加书签。

我能够获取并显示它们。我正在使用Combine with @Published属性包装器来更新我的视图。到目前为止,一切都按预期进行。

因此,我继续下一步,为存储库添加了书签。我想使用Realm来保留这些存储库。

我的问题是,我有一个Observable类,该类具有一个 @Published 存储库数组。存储库是可解码的类。在我的存储库类中,现在有一个简单的类属性 isFavorite 设置为false。

在我的存储库列表中,当我单击存储库以查看详细信息时,我希望可以将其添加为书签。因此,我从存储库数组中按其ID检索其索引,并将其属性 isFavorite 设置为true。但是我的观点没有更新。我的意思是我有条件渲染,并且被固定为false。

这是我的代码:

import SwiftUI
import Combine

struct RepositoryList : View {
    @ObservedObject var store: Store

    func move(from source: IndexSet, to destination: Int) {
        store.repositories.swapAt(source.first!, destination)
    }

    @State private var isTapped = false
    @State private var bgColor = Color.white

    var body: some View {
        NavigationView {
            VStack {
                List {
                    Section(header: Text("\(String(store.repositories.count)) repositories")) {
                        ForEach(store.repositories) { repository in
                            NavigationLink(destination: RepositoryDetail(store: self.store, repository: repository)) {
                                RepositoryRow(repository: repository)
                            }.padding(.vertical, 8.0)
                        }.onDelete { index in
                            self.store.repositories.remove(at: index.first!)
                        }.onMove(perform: move)
                    }
                }
            }
        }.navigationBarTitle("Github user").navigationBarItems(trailing: EditButton())
    }
}

struct RepositoryDetail : View {
    @ObservedObject var store: Store
    var repository: Repository

    var repoIndex: Int {
        let repoIndex = store.repositories.firstIndex(where: {$0.id == repository.id})!
        print("### IS FAVORITE \(String(store.repositories[repoIndex].isFavorite))")
        return repoIndex
    }

    var body: some View {
        VStack(alignment: .leading) {
            Text(String(store.repositories[repoIndex].isFavorite))
            Button(action: { self.store.repositories[self.repoIndex].isFavorite.toggle() }) {
                if (self.store.repositories[self.repoIndex].isFavorite) {
                    Image(systemName: "star.fill").foregroundColor(Color.yellow)
                } else {
                    Image(systemName: "star").foregroundColor(Color.gray)
                }
            }
        }.navigationBarTitle(Text(repository.name), displayMode: .inline)
    }
}

class Store: ObservableObject {
    private var cancellable: AnyCancellable? = nil
    @Published var repositories: [Repository] = []

    init () {
        var urlComponents = URLComponents(string: "https://api.github.com/users/Hurobaki/repos")!
        var request = URLRequest(url: urlComponents.url!)
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")

        cancellable = URLSession.shared.send(request: request)
            .decode(type: [Repository].self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { completion in
                print("### .sink() received the completion", String(describing: completion))
                switch completion {
                case .finished:
                    break
                case .failure(_):
                   print("### ERROR")
                }
            }, receiveValue: { repositories in
                self.repositories = repositories
            })

    }
}

class Repository: Decodable, Identifiable {
    var id: Int = 0
    var name: String = ""
    var desc: String? = nil

    var isFavorite = false

    private enum CodingKeys: String, CodingKey {
        case id, name
        case desc = "description"
    }
}

我不知道为什么我的Button组件从不显示Image(systemName: "star.fill").foregroundColor(Color.yellow),当我打印 isFavorite 属性时,其值从true变为false,反之亦然,但是我的视图没有更新。 / p>

我从Apple Developer那里获得了教程,他们做的事情完全相同,所以我不知道为什么会出现此错误,我想念的是什么?

某些帮助将不胜感激和/或对我的代码进行评论:)

谢谢

PS:为了不发布更长的代码,我已将其上传到Pastebin https://pastebin.com/zjDwQSGq

1 个答案:

答案 0 :(得分:8)

感谢您发布完整的代码。它使故障排除变得更加容易。

您的问题很简单。您将Repository设为类,而不是结构。这意味着当您更改.isFavorite时,数组中的对象不会更改,因为它是引用类型。如果您更改并且将Repository构造为结构,则代码将起作用,因为更改.isFavorite时,您实际上是在更改值,而Publisher会捕获它。

如果您不知道引用类型和值类型之间的区别,请检查以下页面:https://developer.apple.com/swift/blog/?id=10,其中有详细说明。但是请记住,类是引用类型,而结构是值类型。

如果您仍然想保留Repository一个类,可以这样做,但是您需要在商店的objectWillChange主题上手动调用send:

Button(action: {
                self.store.repositories[self.repoIndex].isFavorite.toggle()
                self.store.objectWillChange.send()
            }) {
                if (self.store.repositories[self.repoIndex].isFavorite) {
                    Image(systemName: "star.fill").foregroundColor(Color.yellow)
                } else {
                    Image(systemName: "star").foregroundColor(Color.gray)
                }
            }