从服务器存储解码 JSON 数组的位置以及如何在 viewControllers 中全局访问它?
Where to store Decoded JSON array from server and how to access it globally in viewControllers?
目前我正在创建从我的服务器解析 JSON 的应用程序。我可以从服务器接收带有 JSON 模型的数组。
此数组中的数据必须填充到 table 视图中。
我的问题很简单:如果我想从我的应用程序中的多个视图控制器访问它,从服务器存储解码数组的位置?
这是我的 JSON 模型,来自服务器。
import Foundation
struct MyModel: Codable {
var settings: Test?
var provider: [Provider]
}
extension MyModel {
struct setting: Codable {
var name: String
var time: Int
}
}
这是我解码的方式
import Foundation
enum GetResourcesRequest<ResourceType> {
case success([ResourceType])
case failure
}
struct ResourceRequest<ResourceType> where ResourceType: Codable {
var startURL = "https://myurl/api/"
var resourceURL: URL
init(resourcePath: String) {
guard let resourceURL = URL(string: startURL) else {
fatalError()
}
self.resourceURL = resourceURL.appendingPathComponent(resourcePath)
}
func fetchData(completion: @escaping
(GetResourcesRequest<ResourceType>) -> Void ) {
URLSession.shared.dataTask(with: resourceURL) { data, _ , _ in
guard let data = data else { completion(.failure)
return }
let decoder = JSONDecoder()
if let jsonData = try? decoder.decode([ResourceType].self, from: data) {
completion(.success(jsonData))
} else {
completion(.failure)
}
}.resume()
}
}
如果你真的想避免重复加载 data() 调用,最简单的选择是在第一次解析数据后将其缓存在磁盘上(CoreData、Realm、File 等)。
然后每ViewController需要数据的,就可以查询你的存储系统。
当然,这种方法的缺点是您必须编写额外的代码来管理数据的一致性,以确保它在您的应用程序中得到妥善管理。
这是一个 CategoriesProvider 的例子。它仅存储类别 in-memory,您可以在整个应用程序中使用它们。这不是最好的方法,也不是最好的架构,但上手很简单。
class CategoriesProvider {
static let shared = CategoriesProvider()
private(set) var categories: [Category]?
private let categoryRequest = ResourceRequest<Category>(resourcePath: "categories")
private let dataTask: URLSessionDataTask?
private init() {}
func fetchData(completion: @escaping (([Category]?) -> Void)) {
guard categories == nil else {
completion(categories)
return
}
dataTask?.cancel()
dataTask = categoryRequest.fetchData { [weak self] categoryResult in
var fetchedCategories: [Category]?
switch categoryResult {
case .failure:
print("error")
case .success(let categories):
fetchedCategories = categories
}
DispatchQueue.main.async {
self?.categories = fetchedCategories
completion(fetchedCategories)
}
}
}
}
我建议使用 URLSessionDataTask 来取消之前的任务。当您接连多次调用 fetchData
时,可能会发生这种情况。您必须修改您的 ResourceRequest 和 URLSession.shared.dataTask(...)
的 return 值
这里有更多关于数据任务的详细信息https://www.raywenderlich.com/3244963-urlsession-tutorial-getting-started#toc-anchor-004(DataTask 和 DownloadTask)
现在您可以通过这种方式获取 CategoriesViewController 中的类别:
private func loadTableViewData() {
CategoriesProvider.shared.fetchData { [weak self] categories in
guard let self = self, let categories = categories else { return }
self.categories = categories
self.tableView.reloadData()
}
}
在其他视图控制器中,您可以执行相同的操作,但可以在获取之前检查 'categories'。
if let categories = CategoriesProvider.shared.categories {
// do something
} else {
CategoriesProvider.shared.fetchData { [weak self] categories in
// do something
}
}
在任何 class 之外创建一个全局字典数组,以便在每个 viewcontroller.
上访问它
目前我正在创建从我的服务器解析 JSON 的应用程序。我可以从服务器接收带有 JSON 模型的数组。 此数组中的数据必须填充到 table 视图中。 我的问题很简单:如果我想从我的应用程序中的多个视图控制器访问它,从服务器存储解码数组的位置?
这是我的 JSON 模型,来自服务器。
import Foundation
struct MyModel: Codable {
var settings: Test?
var provider: [Provider]
}
extension MyModel {
struct setting: Codable {
var name: String
var time: Int
}
}
这是我解码的方式
import Foundation
enum GetResourcesRequest<ResourceType> {
case success([ResourceType])
case failure
}
struct ResourceRequest<ResourceType> where ResourceType: Codable {
var startURL = "https://myurl/api/"
var resourceURL: URL
init(resourcePath: String) {
guard let resourceURL = URL(string: startURL) else {
fatalError()
}
self.resourceURL = resourceURL.appendingPathComponent(resourcePath)
}
func fetchData(completion: @escaping
(GetResourcesRequest<ResourceType>) -> Void ) {
URLSession.shared.dataTask(with: resourceURL) { data, _ , _ in
guard let data = data else { completion(.failure)
return }
let decoder = JSONDecoder()
if let jsonData = try? decoder.decode([ResourceType].self, from: data) {
completion(.success(jsonData))
} else {
completion(.failure)
}
}.resume()
}
}
如果你真的想避免重复加载 data() 调用,最简单的选择是在第一次解析数据后将其缓存在磁盘上(CoreData、Realm、File 等)。
然后每ViewController需要数据的,就可以查询你的存储系统。
当然,这种方法的缺点是您必须编写额外的代码来管理数据的一致性,以确保它在您的应用程序中得到妥善管理。
这是一个 CategoriesProvider 的例子。它仅存储类别 in-memory,您可以在整个应用程序中使用它们。这不是最好的方法,也不是最好的架构,但上手很简单。
class CategoriesProvider {
static let shared = CategoriesProvider()
private(set) var categories: [Category]?
private let categoryRequest = ResourceRequest<Category>(resourcePath: "categories")
private let dataTask: URLSessionDataTask?
private init() {}
func fetchData(completion: @escaping (([Category]?) -> Void)) {
guard categories == nil else {
completion(categories)
return
}
dataTask?.cancel()
dataTask = categoryRequest.fetchData { [weak self] categoryResult in
var fetchedCategories: [Category]?
switch categoryResult {
case .failure:
print("error")
case .success(let categories):
fetchedCategories = categories
}
DispatchQueue.main.async {
self?.categories = fetchedCategories
completion(fetchedCategories)
}
}
}
}
我建议使用 URLSessionDataTask 来取消之前的任务。当您接连多次调用 fetchData
时,可能会发生这种情况。您必须修改您的 ResourceRequest 和 URLSession.shared.dataTask(...)
这里有更多关于数据任务的详细信息https://www.raywenderlich.com/3244963-urlsession-tutorial-getting-started#toc-anchor-004(DataTask 和 DownloadTask)
现在您可以通过这种方式获取 CategoriesViewController 中的类别:
private func loadTableViewData() {
CategoriesProvider.shared.fetchData { [weak self] categories in
guard let self = self, let categories = categories else { return }
self.categories = categories
self.tableView.reloadData()
}
}
在其他视图控制器中,您可以执行相同的操作,但可以在获取之前检查 'categories'。
if let categories = CategoriesProvider.shared.categories {
// do something
} else {
CategoriesProvider.shared.fetchData { [weak self] categories in
// do something
}
}
在任何 class 之外创建一个全局字典数组,以便在每个 viewcontroller.
上访问它