[swift] 用泛型整合 Codable 和 文件读写!

先上结果

swift 复制代码
/// encode Configuration 类型再存入文件, 解压 data 再 decode
let configuration = DFDataFile(folder: filePath.projects(), name: "configuration.json")
        .codable(Configuration.self)

/// 压缩 data 存入文件, 解压 data 再 decode
let indexDB = DFDataFile(folder: filePath.projects(), name: "index.db")
        .compression(.zlib)
        .codable(IndexDB.self)

代码实现

swift 复制代码
import Foundation
import STFilePath

/// 表示一个可下载文件的协议,支持异步获取和保存数据。
public protocol DownloadableFile: Sendable {
    
    /// 文件对应的数据模型类型
    associatedtype Model: Sendable
    
    /// 获取操作的数据转换方法:从文件模型转换为目标类型
    typealias FetchTransform<To: Sendable> = @Sendable (_ model: Model) async throws -> To
    
    /// 保存操作的数据转换方法:从目标类型转换为文件模型
    typealias SaveTransform<To: Sendable> = @Sendable (_ model: To) async throws -> Model
    
    /// 异步获取文件内容
    func fetch() async throws -> Model
    
    /// 异步保存文件内容
    func save(_ model: Model?) async throws
}

public extension DownloadableFile {
    
    /// 基于当前文件定义一个新的映射关系(转换模型类型)
    func map<To: Sendable>(
        fetch: @escaping FetchTransform<To>,
        save: @escaping SaveTransform<To>
    ) -> DKFileMap<Self, To> {
        DKFileMap(file: self, fetch: fetch, save: save)
    }
    
    /// 为 Data 类型文件添加压缩/解压逻辑
    func compression(_ algorithm: STComparator.Algorithm) -> DKFileMap<Self, Data> where Model == Data {
        self.map { model in
            try STComparator.decompress(model, algorithm: algorithm)
        } save: { model in
            try STComparator.compress(model, algorithm: algorithm)
        }
    }
    
    /// 为 Data 类型文件添加 Codable 支持(自定义编码器与解码器)
    func codable<Kind: Codable>(
        _ kind: Kind.Type,
        encoder: JSONEncoder,
        decoder: JSONDecoder
    ) -> DKFileMap<Self, Kind> where Model == Data {
        self.map { model in
            try decoder.decode(kind.self, from: model)
        } save: { model in
            try encoder.encode(model)
        }
    }
    
    /// 为 Data 类型文件添加默认的 Codable 支持(自动配置编码器/解码器)
    func codable<Kind: Codable>(
        _ kind: Kind.Type,
        encoderOutputFormatting: JSONEncoder.OutputFormatting = [.prettyPrinted, .sortedKeys]
    ) -> DKFileMap<Self, Kind> where Model == Data {
        let encoder = JSONEncoder()
        encoder.dataEncodingStrategy = .base64
        encoder.dateEncodingStrategy = .iso8601
        encoder.outputFormatting = encoderOutputFormatting
        
        let decoder = JSONDecoder()
        decoder.dataDecodingStrategy = .base64
        decoder.dateDecodingStrategy = .iso8601
        
        return self.codable(kind, encoder: encoder, decoder: decoder)
    }
    
}

/// 基于通用文件接口实现 DownloadableFile 的 Data 类型封装
public struct DFDataFile: Sendable, DownloadableFile {
    
    public var file: any AnyFile
    
    public init(file: any AnyFile) {
        self.file = file
    }
    
    /// 快捷构造器:通过文件夹与文件名创建 Data 文件
    public init(folder: any AnyFolder, name: String) {
        self.init(file: folder.file(name))
    }
   
    /// 异步获取文件数据
    public func fetch() async throws -> Data {
        try await file.data()
    }
    
    /// 异步保存文件数据
    public func save(_ model: Data?) async throws {
        try await file.overlay(with: model, options: .init())
    }
}

/// 用于将 DownloadableFile 进行类型映射的通用结构体
public struct DKFileMap<DKFile: DownloadableFile, To: Sendable>: Sendable, DownloadableFile {
    
    public typealias Model = To
    
    public let file: DKFile
    public let fetchTransform: DKFile.FetchTransform<To>
    public let saveTransform: DKFile.SaveTransform<To>
    
    public init(
        file: DKFile,
        fetch: @escaping DKFile.FetchTransform<To>,
        save: @escaping DKFile.SaveTransform<To>
    ) {
        self.file = file
        self.fetchTransform = fetch
        self.saveTransform = save
    }
    
    /// 获取转换后的模型数据
    public func fetch() async throws -> To {
        let data = try await file.fetch()
        return try await fetchTransform(data)
    }
    
    /// 保存转换后的模型数据
    public func save(_ model: To?) async throws {
        if let model = model {
            let data = try await saveTransform(model)
            try await file.save(data)
        } else {
            try await file.save(nil)
        }
    }
}
相关推荐
初级代码游戏5 小时前
iOS开发 SwiftUI 14:ScrollView 滚动视图
ios·swiftui·swift
初级代码游戏7 小时前
iOS开发 SwitftUI 13:提示、弹窗、上下文菜单
ios·swiftui·swift·弹窗·消息框
zhyongrui10 小时前
托盘删除手势与引导体验修复:滚动冲突、画布消失动画、气泡边框
ios·性能优化·swiftui·swift
zhangfeng113313 小时前
CSDN星图 支持大模型微调 trl axolotl Unsloth 趋动云 LLaMA-Factory Unsloth ms-swift 模型训练
服务器·人工智能·swift
zhyongrui1 天前
SnipTrip 发热优化实战:从 60Hz 到 30Hz 的性能之旅
ios·swiftui·swift
大熊猫侯佩2 天前
Neo-Cupertino 档案:撕开 Actor 的伪装,回归 Non-Sendable 的暴力美学
swift·observable·actor·concurrency·sendable·nonsendable·data race
2501_915921434 天前
在没有源码的前提下,怎么对 Swift 做混淆,IPA 混淆
android·开发语言·ios·小程序·uni-app·iphone·swift
00后程序员张4 天前
对比 Ipa Guard 与 Swift Shield 在 iOS 应用安全处理中的使用差异
android·开发语言·ios·小程序·uni-app·iphone·swift
大熊猫侯佩4 天前
星际穿越:SwiftUI 如何让 ForEach 遍历异构数据(Heterogeneous)集合
swiftui·swift·遍历·foreach·any·异构集合·heterogeneous
hjs_deeplearning5 天前
认知篇#15:ms-swift微调中gradient_accumulation_steps和warmup_ratio等参数的意义与设置
开发语言·人工智能·机器学习·swift·vlm