ios - 使用 SwiftUI 和 Combine 自动构建和更新列表
问题描述
我开始学习 SwiftUI 开发,我正在制作我的第一个基本的基于 SwiftUI 的新闻应用程序,我计划开源,但我目前陷入困境。我一直在阅读 Apple 的文档并查看有关如何使用 combine 等自动处理 SwiftUI 中的数据更改的示例。我找到了一篇文章,假设会自动更新列表。我无法看到任何即时的数据更改或正在记录的任何内容。
我使用与 NewsAPI 相同的结构,但作为示例,我已将其上传到 GitHub repo。我做了一个小项目并尝试更新我的存储库中的数据并尝试查看我的数据中所做的任何更改。老实说,我正在尽我最大的努力,并且可以真正使用一些指针或更正我的错误可能是什么。我认为我的困惑在于@ObservedObject
以及@Published
如何处理我的内容视图中的任何更改。这篇文章没有显示他们为处理数据更改所做的任何事情,所以也许我遗漏了什么?
import Foundation
import Combine
struct News : Codable {
var articles : [Article]
}
struct Article : Codable,Hashable {
let description : String?
let title : String?
let author: String?
let source: Source
let content: String?
let publishedAt: String?
}
struct Source: Codable,Hashable {
let name: String?
}
class NewsData: ObservableObject {
@Published var news: News = News(articles: [])
init() {
guard let url = URL(string: "https://raw.githubusercontent.com/ca13ra1/data/main/data.json") else { return }
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let response = try? JSONDecoder().decode(News.self, from: data) {
DispatchQueue.main.async() {
self.news = response
print("data called")
}
}
}
}
.resume()
}
}
我的观点
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var data: NewsData
var body: some View {
List(data.news.articles , id: \.self) { article in
Text(article.title ?? "")
}
}
}
解决方案
SwiftUI 中的数据绑定不会扩展到与服务器同步状态。如果这就是你想要的,那么你需要使用一些其他机制来告诉客户端服务器中有新数据(即 GraphQL、Firebase、发送推送、使用 Web 套接字或轮询服务器)。
一个简单的轮询解决方案看起来像这样,并注意您不应该在 init 中执行网络请求,只有当您从视图中出现 on 时,因为即使您看不到它们,SwiftUI 也会急切地对其视图进行水合。同样,您需要在屏幕外取消轮询:
struct Article: Codable, Identifiable {
var id: String
}
final class ViewModel: ObservableObject {
@Published private(set) var articles: [Article] = []
private let refreshSubject = PassthroughSubject<Void, Never>()
private var timerSubscription: AnyCancellable?
init() {
refreshSubject
.map {
URLSession
.shared
.dataTaskPublisher(for: URL(string: "someURL")!)
.map(\.data)
.decode(type: [Article].self, decoder: JSONDecoder())
.replaceError(with: [])
}
.switchToLatest()
.receive(on: DispatchQueue.main)
.assign(to: &$articles)
}
func refresh() {
refreshSubject.send()
guard timerSubscription == nil else { return }
timerSubscription = Timer
.publish(every: 5, on: .main, in: .common)
.autoconnect()
.sink(receiveValue: { [refreshSubject] _ in
refreshSubject.send()
})
}
func onDisappear() {
timerSubscription = nil
}
}
struct MyView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
List(viewModel.articles) { article in
Text(article.id)
}
.onAppear { viewModel.refresh() }
.onDisappear { viewModel.onDisappear() }
}
}
推荐阅读
- javascript - 在节点 js 中使用参数调用 URL
- c# - 用于嵌套项目集合的 AutoMapper
- python - 如何将 django 对象发送到 celery 任务?
- java - Java - 如何过滤值(列表)
- mysql - 同一张表上的两个 select 语句并获取 Count(*)
- xamarin.forms - 无法在 Xamarin.Forms 中通过 Internet 发送请求
- javascript - 将模板引擎加载到 node.js / express
- python - Scipy处理大数据
- javascript - 我需要在 NodeJS 中的网站和 API 身份验证中使用什么
- mysql - 如何从另一个数据库更新mysql中的表