美文网首页
iOS Swift 字典转模型 模型存沙盒

iOS Swift 字典转模型 模型存沙盒

作者: Yuency | 来源:发表于2019-08-13 17:27 被阅读0次

前言:

本篇接着上一篇
iOS Swift 原生 字典数组转模型 JSONDecoder 对象存储 NSKeyedArchiver
继续写的。

因为在代码里看到了一个骚操作。

这里记录一下模型(Model)简化的写法,上次我写模型用了 NSCoding 协议,这次不用写这玩意了。公司大佬还是6啊。代码之路太TM漫长了。

字典转模型这一步没有变化。

然后是模型在沙盒里的存取,有一点小小的改动。

Swift
代码地址:https://github.com/gityuency/Autolayout
示例代码类名 【SwiftCoding_2_ViewController】

运行截图

再存沙盒.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改起来费工夫。那时候也正好是夏天,天还比较热,在公司食堂吃饭完,就跑到大楼大厅下面的沙发上葛优躺睡觉。也许是睡相太难看,被保安叫起来两三次了。靠在沙发上的时候,对面是巨大的玻璃墙,玻璃墙的外面,是猛烈的日光和寻常的马路,还有小树和灌木。看着这广阔的前景,总觉得......。好像我去那睡觉的次数也不多啊,一个夏天就这么悄悄地过去了。也不记得忘记了什么。

相关文章

网友评论

      本文标题:iOS Swift 字典转模型 模型存沙盒

      本文链接:https://www.haomeiwen.com/subject/bkvbjctx.html