6

SwiftUI 学习记录 - 使用 Alamofire 加载 JSON 并初始化列表

 9 months ago
source link: https://www.boris1993.com/swiftui-initialize-list-with-remote-data.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

这两天又捡起来了之前开的一个老坑,继续完成 X 岛揭示板的 iOS 客户端,而且刚刚完成了从 JSON 初始化版面列表的功能。
这部分感觉最难的还是上手 Alamofire,因为它返回结果不像我平时做 Web 开发那样通过方法返回(也有可能是我没学到位),而是要把反序列化得到的对象传给一个回调方法。而这个思路的差异也导致我刚开始学的时候非常的痛苦,因为怎么也找不到我想要的那种返回方式。
我相信应该不止我一个人会遇到这种情况,所以打算在这里把完整的实现过程记录在这里,并希望后面有类似情况的同志能因为这篇文章而少掉几根头发。

定义 JSON 对应的结构体

X 岛揭示板的版面列表 API 会返回一个类似这样的 JSON:

[
{
"id": "4",
"sort": "1",
"name": "综合",
"status": "n",
"forums": [
{
"id": "-1",
"name": "时间线",
"msg": "这里是匿名版最新的串"
},
{
"id": "23",
"fgroup": "3",
"sort": "12",
"name": "暴雪游戏",
"showName": "暴雪游戏",
"msg": "•本版发文间隔为15秒。",
"interval": "15",
"safe_mode": "0",
"auto_delete": "0",
"thread_count": "72",
"permission_level": "0",
"forum_fuse_id": "0",
"createdAt": "2012-05-25 21:21:21",
"updateAt": "2015-04-21 12:30:39",
"status": "n"
}
]
}
]

所以,我们可以创建一个这样的结构体来用来反序列化它:

struct ForumGroup: Codable, Identifiable {
var id: String
var sort: String
var name: String
var status: String
var forums: [Forum]

private enum CodingKeys: String, CodingKey {
case id
case sort
case name
case status
case forums
}
}

struct Forum: Codable, Identifiable {
var id: String = ""
var fGroup: String? = ""
var sort: String? = ""
var name: String = ""
var showName: String? = ""
var msg: String = ""
var interval: String? = ""
var threadCount: String? = ""
var permissionLevel: String? = ""
var forumFuseId: String? = ""
var createdAt: String? = ""
var updateAt: String? = ""
var status: String? = ""

private enum CodingKeys: String, CodingKey {
case id
case fGroup
case sort
case name
case showName
case msg
case interval
// 因为X岛揭示板的API存在CamelCase和snake_case混用的情况
// 所以需要CodingKeys来配置正确的映射
case threadCount = "thread_count"
case permissionLevel = "permission_level"
case forumFuseId = "forum_fuse_id"
case createdAt
case updateAt
case status
}
}

编写网络请求

创建一个新的 Swift 文件 AnoBbsApiClient,编写如下代码:

final class AnoBbsApiClient {
private static let logger = LoggerHelper.getLoggerForNetworkRequest(name: "AnoBbsApiClient")

public static func loadForumGroups(
completion:@escaping ([ForumGroup]) -> Void,
failure:@escaping (String) -> Void
) {
let url = URL(string: XdnmbAPI.GET_FORUM_LIST)!
AF.request(url, method: .get, interceptor: .retryPolicy) { $0.timeoutInterval = 10 }
.cacheResponse(using: .cache)
.validate()
.responseDecodable(of: [ForumGroup].self) { response in
switch response.result {
case .success(let data):
completion(data)
case .failure(let error):
failure(error.localizedDescription)
}
}
}
}

是的,就这几行代码,花了我大概一整天时间来学明白,定稿之前不知道来来回回试了多少遍。前面都很好懂,重点就是 responseDecodable 这个方法调用,of 参数指明我希望把返回的 JSON 反序列化成一个 ForumGroup 列表,后面的方法块中根据成功反序列化和发生任何错误的情况,分别调用 completionfailure 这两个回调方法。

现在回到展示版面的 ForumsView,在 body 里面做如下实现:

var body: some View {
NavigationStack {
List {
ForEach($forumGroups) { $forumGroup in
Section {
ForEach(forumGroup.forums) { forum in
NavigationLink(destination: CookieListView(globalState: globalState)) {
if (forum.showName == nil || forum.showName!.isEmpty) {
Text(forum.name)
} else {
Text(forum.showName!)
}
}
}
} header: {
Text(forumGroup.name)
}
}
}
}
.onAppear {
if (!isContentLoaded) {
globalState.loadingStatus = String(localized: "msgLoadingForumList");
shouldDisplayProgressView = true;

AnoBbsApiClient.loadForumGroups { forumGroups in
self.forumGroups = forumGroups
isContentLoaded = true
shouldDisplayProgressView = false;
} failure: { error in
showErrorToast(message: error)
shouldDisplayProgressView = false;
}
}
}
.toast(isPresenting: $isErrorToastShowing) {
AlertToast(type: .regular, title: errorMessage)
}
}

在这个 View 展示时,如果版面列表没有被加载,那么就调用刚刚写的 loadForumGroups 方法获取版面列表,后面的第一个代码块就是 completion 这个回调的实现,负责把 loadForumGroups 方法得到的结果传给一个 @State 变量 forumGroups,以及标记内容已经成功载入,并隐藏载入提示的风火轮;第二个代码块是 failure 这个回调的实现,负责显示一个带有错误信息的 Toast 并隐藏风火轮。

在这个 ViewNavigationStack 里面,就可以监听 forumGroups 这个 @State 变量,并用变量里面的内容来渲染整个列表了。最后,我们就可以得到这样一个结果:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK