Swift 合并 - @Published 属性 数组
Swift Combine - @Published property Array
我目前正在使用 SwiftUI 和 Combine 做一个项目。我正在使用 Xcode11 Beta 5。我想获取我的 Github 存储库,显示它们,然后能够为其中的一些添加书签。
我能够获取并显示它们。我正在使用 Combine with @Published 属性 包装器来更新我的视图。到现在,一切都按预期进行。
所以我继续下一步,为存储库添加书签。我想使用 Realm 来持久化这些存储库。
我的问题是,我有一个 Observable class,它有一个 @Published 存储库数组。存储库是可解码的 class。在我的存储库 class 中,我有一个简单的 class 属性 isFavorite 现在设置为 false。
在我的存储库列表中,当我单击存储库以查看详细信息时,我希望可以将其添加为书签。因此,我通过它的 id 从我的存储库数组中检索它的索引,并将其 属性 isFavorite 设置为 true。但我的观点没有更新。我的意思是我有一个条件渲染并且它被卡住了。
这是我的代码:
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: {[=12=].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,反之亦然但是我的观点没有更新。
我从 Apple Developer 那里学习了他们做完全相同的事情的教程,所以我不知道为什么我会出现这个错误,我错过了什么?
非常感谢和/或评论我的代码:)
谢谢
PS:为了不 post 更长的代码,我已将其上传到 Pastebin
https://pastebin.com/zjDwQSGq
感谢您发布完整代码。它使故障排除变得更加容易。
你的问题很简单。您将 Repository
设为 class,而不是结构。这意味着当您更改 .isFavorite
时,数组中的对象不会更改,因为它是引用类型。如果您更改并将 Repository 改为结构,您的代码将起作用,因为当您更改 .isFavorite
时,您实际上是在改变一个值,而 Publisher 将捕获它。
如果您不知道引用类型和值类型之间的区别,请查看此页面:https://developer.apple.com/swift/blog/?id=10,其中有详细解释。但请记住,classes 是引用类型,而结构是值类型。
如果您仍想保留 Repository
个 class,您可以这样做,但您需要手动调用商店的 objectWillChange 主题上的发送:
Button(action: {
self.store.objectWillChange.send()
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)
}
}
我目前正在使用 SwiftUI 和 Combine 做一个项目。我正在使用 Xcode11 Beta 5。我想获取我的 Github 存储库,显示它们,然后能够为其中的一些添加书签。
我能够获取并显示它们。我正在使用 Combine with @Published 属性 包装器来更新我的视图。到现在,一切都按预期进行。
所以我继续下一步,为存储库添加书签。我想使用 Realm 来持久化这些存储库。
我的问题是,我有一个 Observable class,它有一个 @Published 存储库数组。存储库是可解码的 class。在我的存储库 class 中,我有一个简单的 class 属性 isFavorite 现在设置为 false。
在我的存储库列表中,当我单击存储库以查看详细信息时,我希望可以将其添加为书签。因此,我通过它的 id 从我的存储库数组中检索它的索引,并将其 属性 isFavorite 设置为 true。但我的观点没有更新。我的意思是我有一个条件渲染并且它被卡住了。
这是我的代码:
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: {[=12=].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,反之亦然但是我的观点没有更新。
我从 Apple Developer 那里学习了他们做完全相同的事情的教程,所以我不知道为什么我会出现这个错误,我错过了什么?
非常感谢和/或评论我的代码:)
谢谢
PS:为了不 post 更长的代码,我已将其上传到 Pastebin https://pastebin.com/zjDwQSGq
感谢您发布完整代码。它使故障排除变得更加容易。
你的问题很简单。您将 Repository
设为 class,而不是结构。这意味着当您更改 .isFavorite
时,数组中的对象不会更改,因为它是引用类型。如果您更改并将 Repository 改为结构,您的代码将起作用,因为当您更改 .isFavorite
时,您实际上是在改变一个值,而 Publisher 将捕获它。
如果您不知道引用类型和值类型之间的区别,请查看此页面:https://developer.apple.com/swift/blog/?id=10,其中有详细解释。但请记住,classes 是引用类型,而结构是值类型。
如果您仍想保留 Repository
个 class,您可以这样做,但您需要手动调用商店的 objectWillChange 主题上的发送:
Button(action: {
self.store.objectWillChange.send()
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)
}
}