前言:
本篇接着上一篇
iOS Swift 原生 字典数组转模型 JSONDecoder 对象存储 NSKeyedArchiver
继续写的。
因为在代码里看到了一个骚操作。
这里记录一下模型(Model)简化的写法,上次我写模型用了 NSCoding 协议,这次不用写这玩意了。公司大佬还是6啊。代码之路太TM漫长了。
字典转模型这一步没有变化。
然后是模型在沙盒里的存取,有一点小小的改动。
Swift
代码地址:https://github.com/gityuency/Autolayout
示例代码类名 【SwiftCoding_2_ViewController】
运行截图
![](https://img.haomeiwen.com/i1235875/5ea0bcb97070e4fd.gif)
第一阶段 回望JSON
{
"name": "农夫果园",
"location": "上海市 浦东新区 申迪北路 753号 上海迪士尼度假区",
"number": 10001,
"money": 998.12,
"open": true,
"fruits": [
{
"name": "火龙果",
"count": 2000,
"price": 56.23,
"onsale": true
},
{
"name": "山竹",
"count": 555,
"price": 17.22,
"onsale": false
}
],
"owner": {
"name": "姬友大人",
"age": 30
}
}
第二阶段 再写Model
这次的Model不用NSCoding写了,但是需要新增一个协议, 代码如下:
示例的 Model:
import Foundation
/// 一个用于 model -> data 的协议
@objc protocol DataConvertible : class {
func convertToData() -> Data?
}
class FarmerModel: Codable, DataConvertible {
/// 重写协议里的方法
func convertToData() -> Data? {
return try? JSONEncoder().encode(self)
}
/// 自己写的方法
static func convert(from data: Data) -> FarmerModel? {
return try? JSONDecoder().decode(FarmerModel.self, from: data)
}
var name: String?
var number: Int?
var money: Float?
var open: Bool?
var address: String?
var fruits: [FarmerFruits]?
var owner: FarmerOwner?
}
/// 二级模型 水果摊
class FarmerFruits: Codable {
var name: String?
var count: Int?
var price: Float?
var onsale: Bool?
}
/// 二级模型 商店老板
class FarmerOwner: Codable {
var name: String?
var age: Int?
}
extension FarmerModel: CustomStringConvertible {
var description: String {
return """
\(String(describing: name))
\(String(describing: number))
\(String(describing: money))
\(String(describing: open))
\(String(describing: address))
\(String(describing: fruits))
\(String(describing: owner))
"""
}
}
extension FarmerFruits: CustomStringConvertible {
var description: String {
return """
\(String(describing: name))
\(String(describing: count))
\(String(describing: price))
\(String(describing: onsale))
"""
}
}
extension FarmerOwner: CustomStringConvertible {
var description: String {
return """
\(String(describing: name))
\(String(describing: age))
"""
}
}
第三阶段 JSON 转 Model
这里是上一次的代码,没有变化
代码如下:
import Foundation
/// 字典转模型工具类, 重复代码不抽取
struct YXTransferToModel {
/// 字典转模型
public static func toModelObject<T>(_ dictionary: Any?, to type: T.Type) -> T? where T: Decodable {
guard let dictionary = dictionary else {
print("❌ 传入的数据解包失败!")
return nil
}
if !JSONSerialization.isValidJSONObject(dictionary) {
print("❌ 不是合法的json对象!")
return nil
}
guard let data = try? JSONSerialization.data(withJSONObject: dictionary, options: []) else {
print("❌ JSONSerialization序列化失败!")
return nil
}
guard let model = try? JSONDecoder().decode(type, from: data) else {
print("❌ JSONDecoder字典转模型失败!")
return nil
}
return model
}
/// 数组转模型
public static func toModelArray<T>(_ array: Any?, to type: T.Type) -> [T]? where T: Decodable {
guard let array = array else {
print("❌ 传入的数据解包失败!")
return nil
}
if !JSONSerialization.isValidJSONObject(array) {
print("❌ 不是合法的json对象!")
return nil
}
guard let data = try? JSONSerialization.data(withJSONObject: array, options: []) else {
print("❌ JSONSerialization序列化失败!")
return nil
}
guard let arrayModel = try? JSONDecoder().decode([T].self, from: data) else {
print("❌ JSONDecoder数组转模型失败!")
return nil
}
return arrayModel
}
}
第四阶段 把模型存到沙盒里
这次写了个新的类,主要是不想和以前的代码混到一起。
代码如下:
import Foundation
/// 把 模型对象 或者 模型数组 存到 沙盒 里面, 重复代码不抽取
struct FarmerSaver {
static let key = "被教育了"
private static let YXFramerCache = "YXFramerCache"
/// 把 模型对象 存到 沙盒 里面
static func save(data: DataConvertible, key: String) {
guard let docPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else {
print("❌ 获取沙盒目录失败!")
return
}
let userDirPath = URL(fileURLWithPath: docPath).appendingPathComponent(YXFramerCache)
guard (try? FileManager.default.createDirectory(at: userDirPath, withIntermediateDirectories: true, attributes: [:])) != nil else {
print("❌ 创建沙盒文件目录失败!")
return
}
let dataFullPath = "\(docPath)/\(YXFramerCache)/\(key)"
// 这一步直接把 model 转换成 data
if let converted = data.convertToData() {
NSKeyedArchiver.archiveRootObject(converted, toFile: dataFullPath)
}
print("存储路径\n\(dataFullPath)")
}
/// 把 模型对象 从 沙盒 里取出来
static func loadDataWithCodable(key: String) -> Data? {
guard let docPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else {
print("❌ 获取沙盒目录失败!")
return nil
}
let dataFullPath = "\(docPath)/\(YXFramerCache)/\(key)"
if let data = NSKeyedUnarchiver.unarchiveObject(withFile: "\(dataFullPath)") as? Data {
return data
}
return nil
}
/// 删掉沙盒里的文件
static func removeAll() {
guard let docPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else {
print("❌ 获取沙盒目录失败!")
return
}
let dirPath = "\(docPath)/\(YXFramerCache)"
guard ((try? FileManager.default.removeItem(atPath: dirPath)) != nil) else {
print("❌ 删除文件夹失败! / 没有这个文件夹!")
return
}
print("✅ 清理成功!")
}
}
第五阶段 测试代码
让我们再次测试一下代码好不好用
ViewController 代码如下:
import UIKit
import Alamofire
class SwiftCoding_2_ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func actionGo(_ sender: UIButton) {
let urlString = "http://www.yuency.com/yuencyDictionary.json"
Alamofire.request(urlString).responseJSON { (json) in
switch json.result {
case .success:
let farmer = YXTransferToModel.toModelObject(json.result.value, to: FarmerModel.self)
if let farmer = farmer {
FarmerSaver.save(data: farmer, key: FarmerSaver.key)
}
case .failure(let error):
print("网络请求失败 \(error)")
}
}
}
@IBAction func actionFetch(_ sender: UIButton) {
if let data = FarmerSaver.loadDataWithCodable(key: FarmerSaver.key), let farmer = FarmerModel.convert(from: data) {
print(farmer)
} else {
print("没有金坷垃")
}
}
@IBAction func actionClear(_ sender: UIButton) {
FarmerSaver.removeAll()
}
}
结语:
我说过我不想再写这个玩意了。代码弄人啊。去年的这个时候,我在XEF写代码。虽然活有点紧,但是心情还是轻松的,项目也比较简单,可以随便写的那种,就是业务逻辑烦躁了点,有些bug改起来费工夫。那时候也正好是夏天,天还比较热,在公司食堂吃饭完,就跑到大楼大厅下面的沙发上葛优躺睡觉。也许是睡相太难看,被保安叫起来两三次了。靠在沙发上的时候,对面是巨大的玻璃墙,玻璃墙的外面,是猛烈的日光和寻常的马路,还有小树和灌木。看着这广阔的前景,总觉得......。好像我去那睡觉的次数也不多啊,一个夏天就这么悄悄地过去了。也不记得忘记了什么。
网友评论