Swift Codable 自定义默认值解码

2023-11-19

前言

最近我们公司服务端修改了某个接口返回数据结构,减少了一些字段,导致iOS这边codeable解码失败,获取不到正确的数据信息,相关业务无法完成。不想使用可选值类型,可以使用属性包装器实现对基础类型的包装,decode解析时给定默认值,解决了这个困扰

Decodable 解码时,如果model中的字段为非可选值而 json数据中没有该字段的key,就会解析失败。常用的方法是将数据模型中的key给成可选值类型,但是可选值类型在后续使用中比较麻烦需要解包。创建一种包装类型使其Decodable解码时如果解码失败那么就添加默认值,对于json中有可能不存在key的属性使用这种包装类型。这样就不需要我们编写完全自定义的 Codable 实现,也省去了后续使用可选值的麻烦

具体代码实现

public protocol DecodableDefaultSource {
    associatedtype Value: Decodable
    static var defaultValue: Value { get }
}

public enum DecodableDefault {}

public extension DecodableDefault {
    @propertyWrapper
    struct Wrapper<Source: DecodableDefaultSource> {
        public typealias Value = Source.Value
        public var wrappedValue = Source.defaultValue
        public init(wrappedValue: Value = Source.defaultValue) {
            self.wrappedValue = wrappedValue
        }
    }

    typealias Source = DecodableDefaultSource
    typealias List = Decodable & ExpressibleByArrayLiteral
    typealias Map = Decodable & ExpressibleByDictionaryLiteral

    //定义一些常用的数据类型默认值解码类型
    enum Sources {
        public enum True: Source {
            public static var defaultValue: Bool { true }
        }

        public enum False: Source {
            public static var defaultValue: Bool { false }
        }
        
        public enum IntZero: Source {
            public static var defaultValue: Int { 0 }
        }
        
        public enum Int64Zero: Source {
            public static var defaultValue: Int64 { 0 }
        }
        
        public enum DoubleZero: Source {
            public static var defaultValue: Double { 0 }
        }
        
        public enum EmptyString: Source {
            public static var defaultValue: String { "" }
        }

        public enum EmptyList<T: List>: Source {
            public static var defaultValue: T { [] }
        }

        public enum EmptyMap<T: Map>: Source {
            public static var defaultValue: T { [:] }
        }
    }

    typealias IntZero = Wrapper<Sources.IntZero>
    typealias Int64Zero = Wrapper<Sources.Int64Zero>
    typealias DoubleZero = Wrapper<Sources.DoubleZero>
    typealias True = Wrapper<Sources.True>
    typealias False = Wrapper<Sources.False>
    typealias EmptyString = Wrapper<Sources.EmptyString>
    typealias EmptyList<T: List> = Wrapper<Sources.EmptyList<T>>
    typealias EmptyMap<T: Map> = Wrapper<Sources.EmptyMap<T>>
}

extension DecodableDefault.Wrapper: Equatable where Value: Equatable {}
extension DecodableDefault.Wrapper: Hashable where Value: Hashable {}

extension DecodableDefault.Wrapper: Encodable where Value: Encodable {
    public func encode(to encoder: Encoder) throws {
        
        var container = encoder.singleValueContainer()
        try container.encode(wrappedValue)
    }
}

extension DecodableDefault.Wrapper: Decodable {
    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = try container.decode(Value.self)
    }
}

extension KeyedDecodingContainer {
    func decode<T>(_ type: DecodableDefault.Wrapper<T>.Type,
                   forKey key: Key) throws -> DecodableDefault.Wrapper<T> {
        try decodeIfPresent(type, forKey: key) ?? .init()
    }
}

另一种实现方式

定义常用类型的包装类型,自定义解码,此方式可以添加一些容错处理,如果json中key对应的类型不是model的类型但是可以转成model中的类型,也可以解码成功

//MARK: 另一种方式
@propertyWrapper
public struct DecodableString:Codable {
    public var wrappedValue: String
    
    public init(wrappedValue: String) {
        self.wrappedValue = wrappedValue
    }
    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        var string: String = ""
        do {
            string = try container.decode(String.self)
        } catch  {
            //此处可添加一些其它类型,例如Int
            do {
                string = String(try container.decode(Bool.self))
            } catch {
                do {
                    string = String(try container.decode(Double.self))
                } catch {
                    
                }
            }
        }
        self.wrappedValue = string
    }
}

@propertyWrapper
public struct DecodableInt:Codable {
    public var wrappedValue: Int
    
    public init(wrappedValue: Int) {
        self.wrappedValue = wrappedValue
    }
    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        var num: Int = 0
        do {
            num = try container.decode(Int.self)
        } catch  {
            //此处可添加一些其它类型
            do {
                num = (try container.decode(Bool.self)) ? 1 : 0
            } catch {
                do {
                    num = Int(try container.decode(Double.self))
                } catch {
                    do {
                        num = Int(try container.decode(String.self)) ?? 0
                    } catch  {
                        
                    }
                    
                }
            }
        }
        self.wrappedValue = num
    }
}
@propertyWrapper
public struct DecodableDouble:Codable {
    public var wrappedValue: Double
    
    public init(wrappedValue: Double) {
        self.wrappedValue = wrappedValue
    }
    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        var num: Double = 0
        do {
            num = try container.decode(Double.self)
        } catch  {
            //此处可添加一些其它类型
            do {
                num = Double(try container.decode(String.self)) ?? 0
            } catch {
                
            }
        }
        self.wrappedValue = num
    }
}

具体使用

//使用实例
struct User:Codable {
    var name: String
    @DecodableInt var age: Int
    @DecodableDefault.EmptyString var nickName
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Swift Codable 自定义默认值解码 的相关文章

  • 获取动画 UIImageview 的坐标

    我正在水平位置对 UIImageview 进行动画处理 为此我使用了下面的代码我使用了 NSTimer timer NSTimer scheduledTimerWithTimeInterval 0 2 target self selecto
  • 如何将 UIImage 和 UILabel 合并为一张图像并保存

    我有 2 个 UILabels 和 2 个图像 我需要将它们合并到一个 UIImage 中进行保存 我知道我可以用屏幕截图来做到这一点 但我的主图像是圆形的 所以如果我对其进行校正 它仍然会显示锐利的边缘 我可以这样做来组合图像 CGSiz
  • iOS WebView 空白

    我已使用 YouTube 的标准嵌入网址在我的 iOS 8 3 应用程序中嵌入了 YouTube 视频 sample http www youtube com embed HkAK9QRe4ds 它按预期工作 但看起来很奇怪 那个白色间隙
  • 在 Swift 中使用 UIActivityIndi​​catorView 和 UIWebView

    当 url 加载到 WebView 中时 我试图在我的应用程序中向用户显示活动指示器视图 我尝试过使用 Activity startAnimating activity stopAnimating 并尝试将它们放入函数等中 但没有任何运气
  • NSMenu 未调用 validateMenuItem 或 menuWillOpen

    我的 Mac 应用程序有一个 NSMenu 其委托功能validateMenuItem and menuWillOpen从来没有被调用过 到目前为止 网上的解决方案都没有帮助 看来我做的一切都是对的 菜单项的选择器属于同一类 管理它的类继承
  • 在屏幕外绘制 uiview

    我想创建一个 UIView 它在调用 ViewDidLoad 时位于屏幕外 但一旦调用某个函数 我就会将其动画显示到屏幕上 用于对 UIView 进行动画处理的代码很好 但我似乎无法从屏幕外绘制 UIView 我已将故事板中的 UIView
  • Google 地图 ios:Mapview 路线垂直折线始终指向位置箭头的北部或顶部

    我已经在我的应用程序中实现了 Google Maps IOS 的基本功能 我必须将地图视图的方位设置为始终指向顶部或北部 我需要 如果我点击导航按钮 地图视图应该动画到位置箭头顶部 就像谷歌地图 ios 应用程序一样 箭头应始终固定并指向北
  • 运行使用 XCode 7 部署的应用程序会崩溃

    我在 xcode 6 中开发应用程序 然后设备连接 我通过调试运行应用程序 在我可以断开设备与 Xcode 的连接并再次运行应用程序后 它正在运行 但今天开始出现一些问题 我使用 xcode 7 中的运行按钮运行应用程序 gt 应用程序正常
  • SwiftUI 关闭多个模态表

    我在用 sheet isPresented self showModal 在我的根视图中呈现一个模式 在模态中 我正在使用NavigationView引导用户浏览各个页面 对于用户配置文件构建器 在导航堆栈的最后一页中 我正在使用 Envi
  • Captive Wifi 弹出窗口:单击链接打开 Safari

    我们的 iOS 设备 ipad iphone 等 网络出现问题 连接到 SSID 后 iphone ipad 立即打开强制网络助手 CNA 它就像一个缩小版的浏览器 没有导航按钮等 显示我们的欢迎页面 准备好让用户在网络中验证他的 MAC
  • 文件是为存档而构建的,这不是正在链接的体系结构(i386)

    我必须构建静态库 我想在我的 iPhone 和 iPad 应用程序中使用 当我尝试运行模拟器时 出现链接错误 我是 iOS 开发新手 请帮忙 ld 警告 忽略文件 Users valuelabs Desktop DruvaProject l
  • kCVPixelFormatType_420YpCbCr8BiPlanarFullRange 帧到 UIImage 转换

    我有一个应用程序可以捕获 kCVPixelFormatType 420YpCbCr8BiPlanarFullRange 格式的实时视频来处理 Y 通道 根据苹果的文档 kCVPixelFormatType 420YpCbCr8BiPlana
  • 继承属性,从 readonly 继承的属性中读写时不会合成 setter

    我在使用属性时发现了一个奇怪的行为 该属性被继承为只读 然后在继承的类中重新声明为读写 In A h interface A NSObject property nonatomic strong readonly NSObject some
  • IOS - 如何创建具有模糊背景的 Facebook 反应栏?

    虽然这周可能不会复制 Facebook 的某些设计 但我希望能够设计我自己的下面的反应指示器视图版本 我有三个UIImageViews 排列在与上面相同的位置 问题是 与 Facebook 不同 背景颜色可能会改变 即位于UIBlurEff
  • 高度在 IOS (iphone) 上无法正常工作

    我已经创建了this https codepen io salman15 project live DWbWpo Codepen 上的网站 在尝试使其响应所有平台时 我遇到了问题 看起来单个 div 覆盖了整个页面 仅在 IOS 上 并且并
  • 在从初始化器返回 nil 之前,必须初始化类实例的所有存储属性

    尽管我不断收到上述消息 但我正在尝试在课堂上使用此代码 let filePath NSString let fileHandle NSFileHandle let totalFileLength CUnsignedLongLong init
  • 优化 - 步进可能表现奇怪:iOS/Unity

    我正在尝试将 Unity 集成到 iOS 应用程序中 我已经遵循了这个教程http www agnosticdev com blog entry swift integrating unity and vuforia ios swift p
  • 从 AppDelegate 启动 ViewController

    我有一个自定义 URL 方案 我想打开某个ViewController当我访问此 URL 时 这不是根 我已经能够做到这一点 剩下的就是推动这一点ViewController进入navigationController来自AppDelega
  • 在 iOS 上保存(私人)应用程序设置?

    我知道NSUserDefaults用于保存 恢复user优先 什么是等效类应用 例如 应用程序可能有一个 上次运行 字段 或者它可能有一个用于在应用程序级别使用的设备的唯一标识的字段 我的目的是将应用程序的设置 而不是用户的设置 保留在设置
  • Xcode 调试器显示错误的变量值

    我之前问过类似的问题here https stackoverflow com q 53092448 1187415 这个版本有更简单的例子 更新摘要 Xcode 在调试器变量部分中为每个字符串显示 FAIL Swift print 语句显示

随机推荐

  • 锂电池生产全过程图解

    锂电池大致可分为两类 锂金属电池和锂离子电池 锂离子电池不含有金属态的锂 并且是可以充电的 我们所熟知的特斯拉电动汽车便是用的18650锂离子电池通过串并联组成的电池板 随着新能源汽车日渐火爆 动力电池行业也变得炙手可热 下面我们将通过图解
  • “多点”开花,独立走向新零售

    12月7日 亚洲最大的数字零售服务商多点Dmall正式向港交所递交招股说明书 在零售行业逐渐向线上线下一体化 店仓一体模式迈进之时 多点Dmall成为很多传统零售商转型路上的首选合作伙伴 给予了资本市场一定想象空间 但也有观点认为 在自带流
  • Oracle 事务

    文章目录 一 事务的基本概念 二 事务的特征 1 事务的原子性 Atomicity 2 事务的一致性 Consistency 3 独立性 Isolation 4 持久性 Durability 三 事务锁 1 多个会话同时处理一条数据 2 注
  • 比较文本差异的工具_Linux 开发的五大必备工具

    Linux 已经成为工作 娱乐和个人生活等多个领域的支柱 人们已经越来越离不开它 在 Linux 的帮助下 技术的变革速度超出了人们的想象 Linux 开发的速度也以指数规模增长 因此 越来越多的开发者也不断地加入开源和学习 Linux 开
  • [ArcGIS] 表格输出为shp时日期时间列只保留日期而时间被截掉

    1 首先将存有GPS数据的表格加载到ArcGIS中 2 然后右击表格 gt Display XY Data 生成矢量数据 查看dataall csv Events的属性表 可以看到此时的Time属性的值有日期和时间 3 然后右击dataal
  • PostgreSQL导出表结构

    Windows PgAdmin 环境变量配置 PG HOME D Program Files PostgreSQL 9 5 Path PG HOME bin PG HOME lib PG HOME data 最后追加 查看配置是否成功 出现
  • 编写 EL 自定义函数 的方法

    一 利用EL表达式调用普通Java类中的静态方法 1 编写一个java类 并编写一个静态方法 如下所示 public class ElDemo 静态方法 将小写转换为大写 public static String convert Strin
  • 【PAT乙级】锤子剪刀布

    题目描述 大家应该都会玩 锤子剪刀布 的游戏 两人同时给出手势 胜负规则如图所示 现给出两人的交锋记录 请统计双方的胜 平 负次数 并且给出双方分别出什么手势的胜算最大 输入格式 输入第 1 行给出正整数 N 10 5 即双方交锋的次数 随
  • numpy基本教程

    此处所指的数组就是numpy的ndarray 1 numpy加载npz文件 变量filename存放npz文件的地址 加载文件 data seq np load graph signal matrix filename data np lo
  • CSS选择器汇总

    CSS选择器汇总 选择器选择所有元素 选择器也可以选择另一个元素内的所有元素
  • solidworks启动慢的原因在这里

    你打开SOLIDWOKRS需要多长时间 有的人可能是十秒左右SOLIDWOKRS 有的人可能要等上一两分钟才能看到SOLIDWORKS的界面 那么我们今天抛开硬件的差异 主要针对软件和系统环境的设置帮助大家加快打开SOLIDWORKS的速度
  • HBase的Compact和Split源码分析与应用--基于0.94.5

    HBase的Compact和Split源码分析与应用 基于0 94 5 经过对比 0 94 5以后版本主要过程基本类似 有些新功能和细节增加 一 Compact 2 1 Compact主要来源 来自四个方面 1 Memstoreflush时
  • 数组、字符串、Math常用的API

    数组的API 方法 用法 concat 连接两个或多个数组 并返回已连接数组的副本 原数组不变 join 将数组的所有元素连接成一个字符串 返回字符串 原数组不变 toString 将数组转换为字符串 并返回结果 from 从对象创建数组
  • PID控制算法01

    PID控制算法 PID控制算法公式 原理 参数作用 PID算法及改进 两个基本类型 位置型PID控制 增量型PID控制 积分环节改进的PID控制 积分分离的PID控制 变速积分的PID控制 抗积分饱和的PID控制 微分环节改进的PID控制
  • 数据结构 --- 数组

    1 求数组中第二大的数 1 定义两个变量 2 const int MINNUMBER 32767 3 int find sec max int data int count 4 5 int maxnumber data 0 6 int se
  • 软件测试中动态测试与静态测试的区别

    这里讲一下软件测试中动态测试与静态测试的区别 静态测试主要包括 1 代码检查 代码会审 代码走查 桌面检查 2 静态结构分析 3 代码质量度量 动态测试主要包括 1 黑盒测试 又称功能测试 这种方法把被测软件看成黑盒 在不考虑软件内部结构和
  • 2023年深圳杯数学建模A题影响城市居民身体健康的因素分析

    2023年深圳杯数学建模 A题 影响城市居民身体健康的因素分析 原题再现 以心脑血管疾病 糖尿病 恶性肿瘤以及慢性阻塞性肺病为代表的慢性非传染性疾病 以下简称慢性病 已经成为影响我国居民身体健康的重要问题 随着人们生活方式的改变 慢性病的患
  • Python中的常见问题与解决方案

    机器学习作为当今最热门的领域之一 为数据科学和人工智能带来了巨大的突破和进步 然而 在Python中进行机器学习和深度学习开发时 我们可能会遇到一些常见的问题 本文将分享一些这些常见问题 并给出解决方案 帮助您更好地进行机器学习和深度学习的
  • js 正则exec()函数在循环中使用

    在每次循环中 需要把正则表达式的lastIndex重置为0 如 reg lastIndex 0
  • Swift Codable 自定义默认值解码

    前言 最近我们公司服务端修改了某个接口返回数据结构 减少了一些字段 导致iOS这边codeable解码失败 获取不到正确的数据信息 相关业务无法完成 不想使用可选值类型 可以使用属性包装器实现对基础类型的包装 decode解析时给定默认值