Swift:🔧类

2022-01-10  本文已影响0人  时光啊混蛋_97boy

原创:问题解决型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录


1、APP的信息

public class AppInfo: NSObject
public static var shared = AppInfo()
UUID:从钥匙串中获取到uuid
import KeychainAccess

public var sysd: String = {
    let keychain = Keychain(service: kAppKeyChainServiceName)
    var uuid = keychain[kAppKeyChainUUID] ?? ""
    if uuid == "" {
        let strUUID = UIDevice.stringWithUUID
        keychain[kAppKeyChainUUID] = strUUID
        uuid = strUUID
    }
    return uuid
}()
getSession (key: String, aesKey: String):获取Session
import CryptoSwift

public func getSession (key: String, aesKey: String) -> String? {
    do {
        let aes = try AES(key: aesKey, iv: aesKey, padding: .zeroPadding)
        let decrypted = try aes.decrypt(Array(key.utf8))
        let da = Data(bytes: decrypted)
        let string = String(data: da, encoding: String.Encoding.utf8)
        return string
    } catch _ {
        return ""
    }
}

2、屏幕适配

Fit.fitFloat(20)

首先我们实现了最基本的屏幕适配方法fitHelper,根据屏幕宽度来进行计算,像上面的屏幕其宽度为375,我们根据这个数值按照比例来进行计算。

public enum FitHelperType: Int {
    case none
    case flex
}

public class FitHelper: NSObject {
    public static var fitType:FitHelperType = .flex
    fileprivate static func fitHelper( _ value: CGFloat)  -> CGFloat {
        switch fitType {
        case .none:
            return value
        case .flex:
            return value * CGFloat(UIScreen.main.bounds.width /  CGFloat(375.0))
        }
    }
}

接着我们通过重载运算符,来实现了获取当入参是Int、Float、CGPoint、CGSize、CGRect、UIEdgeInsets这几种情况下其适配值。

public extension FitHelper {
    static func heavyLoadFit(_ value: CGFloat) -> CGFloat {
        return FitHelper.fitHelper(value)
    }

    static func heavyLoadFit(_ value: Int) -> Int {
        return Int(FitHelper.fitHelper(CGFloat(value)))
    }

    static func heavyLoadFit(_ value: Float) -> CGFloat {
        return CGFloat(FitHelper.fitHelper(CGFloat(value)))
    }

    static func heavyLoadFit(_ value: CGPoint) -> CGPoint {
        return CGPoint(
            x: FitHelper.fitHelper(value.x),
            y: FitHelper.fitHelper(value.y)
        )
    }
    static func heavyLoadFit(_ value: CGSize) -> CGSize {
        return CGSize(
            width: FitHelper.fitHelper(value.width),
            height: FitHelper.fitHelper(value.height)
        )
    }
    static func heavyLoadFit(_ value: CGRect) -> CGRect {
        return CGRect(
            x: FitHelper.fitHelper(value.origin.x),
            y: FitHelper.fitHelper(value.origin.y),
            width: FitHelper.fitHelper(value.size.width),
            height: FitHelper.fitHelper(value.size.height)
        )
    }
    static func heavyLoadFit(_ value: UIEdgeInsets) -> UIEdgeInsets {
        return UIEdgeInsets(
            top: FitHelper.fitHelper(value.top),
            left: FitHelper.fitHelper(value.left),
            bottom: FitHelper.fitHelper(value.bottom),
            right: FitHelper.fitHelper(value.right)
        )
    }
}

为了方便提供给外界使用,我们又通过重新命名方法来使用内部的这些重载方法。

static func fitInt(_ value: Int) -> Int { return FitHelper.heavyLoadFit(value) }
static func fitFloat(_ value: CGFloat) -> CGFloat { return FitHelper.heavyLoadFit(value) }
static func fitDouble(_ value: CGFloat) -> Double { return Double(FitHelper.heavyLoadFit(value)) }
static func fitFoint(_ value: CGPoint) -> CGPoint { return FitHelper.heavyLoadFit(value) }
static func fitSize(_ value: CGSize) -> CGSize { return FitHelper.heavyLoadFit(value) }
static func fitRect(_ value: CGRect) -> CGRect { return FitHelper.heavyLoadFit(value) }
static func fitEdgeInsets(_ value: UIEdgeInsets) -> UIEdgeInsets { return FitHelper.heavyLoadFit(value) }

针对于字体,我们也进行了适配。

extension UIFont {
    public var fitFont: UIFont { return FitHelper.heavyLoadFit(self) }
}

static func heavyLoadFit(_ font: UIFont) -> UIFont {
    return font.withSize(FitHelper.fitHelper(font.pointSize))
}

为了进行简写,我们对使用的FitHelper工具类进行了重命名。

typealias Fit = FitHelper

3、打开其他应用

public class JumpApplicationTools: NSObject
callPhone(_ phone:String):拨打电话
static func callPhone(_ phone:String) {
    if phone.isEmpty {
        print("电话号码异常")
    } else {
        var tel = "tel://"+phone
        // 去掉空格-不然有些电话号码会使 URL 报 nil
        tel = tel.replacingOccurrences(of: " ", with: "", options: .literal, range: nil);
        print(tel)
        if let urls = URL(string: tel) {
            UIApplication.shared.open(urls, options: [:], completionHandler: { (success) in
                print("Open \(phone): \(success)")
            })
        } else {
            print("url 为空!")
        }
    }
}
jumpApplication(urlString:String):打开应用
static func jumpApplication(urlString:String) -> Bool {
    guard let url = URL(string: urlString) else { return false }
    let can = UIApplication.shared.canOpenURL(url)
    if can {
        UIApplication.shared.open(url, options: [:]) { (success) in
            print("打开结果: \(success)")
        }
        return true
    } else {
        return false
    }
}

4、Configuration 项目的配置信息

@objc class Configuration: NSObject
引入通用三方库

在这个配置文件之中,我们引入了提供各种扩展方法的工具三方库和进行网络请求的第三方库。并对有的第三方库进行了重命名,方便使用。

import ToolKit
import NetWorking

typealias Fit = FitHelper
配置项目运行环境

可以在这个配置文件中提供项目的运行环境,包括开发、测试和上线。

@objc enum HTTPBaseURLType: Int {
    /// 正式
    case product
    /// qa环境
    case qa
    /// dev环境
    case dev
    /// sandbox预发布环境
    case sandbox
}

然后我们设置一个静态变量用于获取项目的启动环境。

@objc static var RealEnvType: HTTPBaseURLType {
    get {
        var cEnvType: HTTPBaseURLType
        #if DEBUGMENU
        let selectedEnv:EnvironmentType = DebugMenuManager.shareInstance().currentSelectedEnv()
        switch selectedEnv {
        case EnvironmentType.product:
            cEnvType = .product
        case EnvironmentType.qa:
            cEnvType = .qa
        case EnvironmentType.dev:
            cEnvType = .dev
        case EnvironmentType.sandbox:
            cEnvType = .sandbox
        default:
            cEnvType = .qa
        }
        #else
            #if DEBUG // ****** 我是开发的时候设置环境 ******
            return .qa
            #else     // ****** 我是打包的时候设置环境 ******
            return isSandbox ? HTTPBaseURLType.sandbox : .product
            #endif
        #endif
        return cEnvType
    }
}

这样当我们需要使用的时候,比如接口要求测试和线上环境传入的参数是不同的,我们就可以直接进行设置。

//qa传1694,online 1734
var number = 1694
if Configuration.RealEnvType == .product || Configuration.RealEnvType == .sandbox {
    number = 1734
}
设置网络请求的baseURL

项目在测试、开发、上线等不同环境的baseURL可能并不相同,我们可以在这里提供一个静态数组,将上述不同环境的baseURL全部容纳到其中,再根据项目启动时候实际传入的RealEnvType.rawValue来进行获取对应baseURLRealEnvType.rawValue是一个数字,因为我们对其枚举类型定义的是int类型。

static let kApiServiceHosts = ["https://api.medlinker.com",
                            "https://\(Configuration.getYongDaoName(domain_name:"api-qa.medlinker.com") ?? "api-qa.medlinker.com")",
                            "https://api-qa.medlinker.com",
                            "https://api-pre.medlinker.com"]

当我们需要获取baseURL的时候就可以通过下面的方式来进行获取:

var kApiServiceHost = Configuration.kApiServiceHosts[Configuration.RealEnvType.rawValue]

通过baseURL就可以发起网络请求来获取网络数据了。

@discardableResult
static func apiGet(path: String,
                   parameters: [String : Any]?,
                   success: HTTPSuccess?,
                   failure : HTTPFailure?,
                   cache : HTTPCache? = nil) -> Alamofire.DataRequest? {
    
    return constructHttpDataRequest(baseURL: kApiServiceHost,
                                    path: path,
                                    method: .get,
                                    parameters: parameters,
                                    success: success,
                                    failure: failure,
                                    cache: cache)
}
设置第三方服务的key值和id值

这里以发起即时通讯的TIM服务为例,当我们获取到TIM生成的accountTypeappIDpushID的时候,我们就可以将获取到的值写入到此处,提供给全局使用。由于TIM针对开发和发布环境提供了不同的值,所以我们也需要相应的设置两套值。

struct TIM {
    struct Production {
        static let accountType = "271011"
        static let appID = 14000938892
        static let pushID =  129493
    }
    struct Development {
        static let accountType = "286875"
        static let appID = 14001905101
        static let pushID =  128433
    }
}

当外界想要获取到accountTypeappIDpushID的时候,我们根据项目目前所处的环境为其提供了相对应的值,这里以accountType为例。

static var timAccountType: String {
    get {
        return RealEnvType == .product ? Configuration.TIM.Production.accountType : Configuration.TIM.Development.accountType
    }
}

类似的还有QQ、微信、新浪的相关配置信息等。

struct ThirdPlatform {
    struct QQ {
        static let appKey = "11032804411"
    }
    struct WeiChat {
        static let appKey = "wxab74a2a38150f11c1"
        static let appSecret = "a000c00932d526da0c78c800659418371"
    }
    struct Sina {
        static let appKey = "102051425"
        static let redirectURL = "http://sns.whalecloud.com/sina2/callback"
        static let textLimit = 140
    }
}

5、模型和JSON转换基类

在使用ObjectMapper库进行模型和JSON之间相互转换的时候,我们需要让模型实现Mappable协议,并且实现init初始化方法,每次都重复写这个的话比较麻烦,于是我们可以考虑使用一个继承自NSObject的基类来实现这个操作。

open class Entity: NSObject, Mappable {
    required public init?(map: Map) {}
    open func mapping(map: Map) {}
    required public override init() {
        super.init()
    }
}

这样模型就只需要继承自该类直接使用了。

class PrescriptionModel: Entity {
    var packetNo:Int64 = 0// 服务包编号
    
    override func mapping(map: Map) {
        packetNo           <- map["packetNo"]
    }
}
guard let json = data as? [String: Any] else{return}
let model = Mapper<PrescriptionModel>().map(JSON: json) ?? nil

6、监测网络连接状态

ReachabilitySwift框架提供了两种获取网络连接状态的方式,一种是通过闭包的方式,连接成功和未连接分别进行处理。一种是通过通知观察的方式,及时获取网络连接状态。这里先看一下闭包的方式:

let reachability = try! Reachability()

reachability.whenReachable = { reachability in
    if reachability.connection == .wifi {
        print("Reachable via WiFi")
    } else {
        print("Reachable via Cellular")
    }
}
reachability.whenUnreachable = { _ in
    print("Not reachable")
}

do {
    try reachability.startNotifier()
} catch {
    print("Unable to start notifier")
}

倘若想要停止监测网络状态了直接调用该方法即可:

reachability.stopNotifier()

接下来再看看通知的方式来监测网络状态,注意这里所有的通知都在主线程上面进行。我们可以在viewwillappear的时候注册通知。

let reachability = try! Reachability()

NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(note:)), name: .reachabilityChanged, object: reachability)
do {
    try reachability.startNotifier()
} catch {
    print("could not start reachability notifier")
}

接下来是通知监测到网络状态时候会调用到的方法,这里会直接获取到所有的连接状态。

@objc func reachabilityChanged(note: Notification) {
    let reachability = note.object as! Reachability
    
    switch reachability.connection {
    case .wifi:
        print("Reachable via WiFi")
    case .cellular:
        print("Reachable via Cellular")
    case .unavailable:
        print("Network not reachable")
    }
}

在停止监测网络状态的时候注意需要移除掉通知:

reachability.stopNotifier()
NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: reachability)

通过上面对使用方法的描述我们来封装一个简单的工具类方便使用。

public class MLReachabilityManager: NSObject {
    public var reachability: Reachability?
    public typealias ReachabilityBlock = (Reachability.Connection)->()
    ...
}

当我们需要监测网络状态时候直接调用startNotifier传入回调闭包来就获取到网络状态之后进行处理。

public func startNotifier(changeHandler: @escaping ReachabilityBlock) {
    self.reachability = try? Reachability()

    reachability?.whenReachable = { reachability in
        DispatchQueue.main.async {
            changeHandler(reachability.connection)
        }
    }
    
    reachability?.whenUnreachable = { reachability in
        DispatchQueue.main.async {
            changeHandler(reachability.connection)
        }
    }
    
    do {
        try reachability?.startNotifier()
    } catch {
        print("Unable to start notifier")
    }
}

当我们不再监测网络状态的时候就调用停止方法即可。

public func stopNotifier() {
    reachability?.stopNotifier()
    reachability = nil
}

使用的方式就变得简单很多了。

reachability = MLReachabilityManager()
reachability.startNotifier { connection in
    switch connection {
    case .unavailable:
        print("unavailable")
    case .cellular:
        print("cellular")
    case .wifi:
        print("wifi")
    case .none:
        break
    }
}
deinit {
    reachability.stopNotifier()
}

7、HandyJSON 使用

HandyJSON 是阿里开发的一个在swift上把JSON数据转化为对应model的框架。与其他流行的Swift JSON库相比,HandyJSON的特点是,它支持纯swift类,使用也简单。它反序列化时(把JSON转换为Model)不要求ModelNSObject继承(因为它不是基于KVC机制),也不要求你为Model定义一个Mapping函数。只要你定义好Model类,声明它服从HandyJSON协议,HandyJSON就能自行以各个属性的属性名为Key,从JSON串中解析值。不过因为HandyJSON是基于swift的metadata来做的,如果swift的metadata的结构改了,HandyJSON可能就直接不能用了。当然阿里一直在维护这个框架,swift的源码有变化,相信框架也是相对于有改变的。

Entity 基类

自定义类声明它服从HandyJSON协议后,必须实现一个空的初始化方法。

class Person: HandyJSON {
    var doubleOptional: Double?
    var stringImplicitlyUnwrapped: String = ""
    var int:Int?
    var name: String?
    
    required init(){} 
}

所以我们可以创建一个Entity基类直接将空的初始化方法放入其中避免每次都多写一个初始化方法。

import HandyJSON

open class Entity: NSObject, HandyJSON {
    required public override init() {
    }
}
属性名称映射

我们先把JSON字符串贴出来:

let jsonString = "{\"cat_id\":12345,\"name\":\"Kitty\",\"friend\":{\"id\":54321,\"name\":\"Lily\"}}"

接着是我们的属性类:

class Cat: Entity {
    var id: Int64!
    var name: String!
    var friendName: String?
}

model的属性名和json里的对应不上的时候,model里实现mapping函数去对应key。可以看到我们需要把id转换为json数据里的key-cat_idfriendName转化为json数据里的friend字典中的name字段,我们可以通过实现mapping函数来达到此目的。

override func mapping(mapper: HelpingMapper) {
    mapper <<< self.id <-- "cat_id"
    mapper <<< self.friendName <-- "friend.name"
}
互不存在
let jsonString = "{\"doubleOptional\":1.1,\"stringImplicitlyUnwrapped\":\"hello\",\"int\":1,\"msg\":\"world\"}"
class Person: Entity {
    var doubleOptional: Double?
    var stringImplicitlyUnwrapped: String = ""
    var int:Int?
    var name: String?
}

可以看到上面的对应关系中包括model中有json里不存在的属性namejson中也有model里不存在的内容msg。这种情况不需要我们做额外操作,直接调用deserialize方法进行解析即可。

if let p: Person = Person.deserialize(from: jsonString) {
    print(p.doubleOptional!)
    print(p.stringImplicitlyUnwrapped)
    print(p.int!)
    print(p.name)
} else {
    print("解析失败")
}
对象嵌套
let jsonString = "{\"num\":12345,\"comp1\":{\"aInt\":1,\"aString\":\"aaaaa\"},\"comp2\":{\"aInt\":2,\"aString\":\"bbbbb\"}}"
class Component: Entity {
    var num: Int?
    var comp1: SubComponent?
    var comp2: SubComponent?
}

class SubComponent: Entity {
    var aInt: Int?
    var aString: String = ""

上面Component中嵌套了SubComponent模型,针对这种情况我们也不需要做额外的操作,只需要直接调用deserialize方法进行解析即可。

if let mainCom: Component = Component.deserialize(from: jsonString)  {
    print(mainCom.num!)
}
数组对象
let jsonString = "{\"num\":12345,\"compArr\":[{\"aInt\":1,\"aString\":\"aaaaa\"},{\"aInt\":2,\"aString\":\"bbbbb\"}]}"
class ComponentArr: Entity {
    var num: Int?
    var compArr: [SubComponent]?
}

可以看到ComponentArr包含了一个compArr模型数组,这种情况下我们也不需要做额外的操作,只需要直接调用deserialize方法进行解析即可,但是在使用的时候需要我们依次从数组中取出每个SubComponent来使用。

if let mainCom: ComponentArr = ComponentArr.deserialize(from: jsonString)  {
    print(mainCom.num!)
    for com in mainCom.compArr! {
        print(com.aInt!)
        print(com.aString)
    }
}
把字典转成对象

这次待转化的不再是JSON字符串了,而是一个字典:

var dict = [String: Any]()
dict["doubleOptional"] = 1.1
dict["stringImplicitlyUnwrapped"] = "hello"
dict["int"] = 1

我们需要将这个字典转化为Person模型,这种情况下我们也不需要做额外的操作,只需要直接调用deserialize方法进行解析即可,但是有一点需要注意的是倘若字典里没有对应的值,则模型里面该属性值为nil

if let p: Person = Person.deserialize(from: dict) {
    print(p.doubleOptional!)
    print(p.stringImplicitlyUnwrapped)
    print(p.int!)
    print(p.name)
}

8、私有库的升级方式

当我们想要调整私有库中的某些代码的时候,显然我们不能每修改一点代码就打一次tag,更好的方式是通过commit或者branch的方式来进行。

pod 'MLDataCache', :git => '[https://git.medlinker.com/ios/mldatacache.git](https://git.medlinker.com/ios/mldatacache.git)',:commit => '8485c6c'
pod 'MLToolKit', :git =>  'https://git.medlinker.com/ios/mltoolkit', :branch => 'swift5'

其中commit是我们每次提交的时候系统生成的一串字符,包括长字符和短字符两种,均可使用,通常使用的是短的。

commit方式的缺点是每次提交都需要到Podfile文件中去更改commit值,这种操作太频繁了不太好,所以可以改为branch的方式来进行,这种方式由系统去自动保持和branch分支代码同步,不再需要频繁更改commit值了。


9、调整私有库的MLNetWorking.podspec文件

以网络库MLNetWorking为例,其podspec文件如下。我们先只看其工程配置,项目文件等暂且不去管它,下面的是其仓库名称和tag版本号。

Pod::Spec.new do |s|
  s.name             = 'MLNetWorking'
  s.version          = '0.7.9'
  s.summary          = 'A short description of MLNetWorking.'
  ...
end

下面是该私有库在远端如Github中的所在位置:

s.homepage         = 'https://git.medlinker.com/liushuoyu/mlnetworking'
s.license          = { :type => 'MIT', :file => 'LICENSE' }
s.author           = { 'liushuoyu' => 'liushuoyu@medlinker.com' }
s.source           = { :git => 'https://git.medlinker.com/liushuoyu/mlnetworking', :tag => s.version.to_s }

我们可以为该私有库指定ios和swift版本:

s.ios.deployment_target = '10.0'
s.swift_version = '5.0'

我们可以为最外层class目录下的文件指定其依赖的动态库:

s.dependency 'Alamofire'
s.dependency 'ios_report_sdk'
s.dependency 'MLDataCache'
s.dependency 'KeychainAccess'

也支持指定动态库的版本号:

s.dependency 'ReachabilitySwift', '= 3.0'

在上面我们看到的一系列class目录下的文件可以由s.source_files来指定:

s.source_files = 'MLNetWorking/**/*'

上面使用了*进行了模糊匹配,因为在MLNetWorking目录下只存在class目录,倘若同时存在Assets目录的话,我们也可以直接指定目录名称:

s.source_files = 'MLTRTC/Classes/**/*'

针对class目录下面的二级目录,我们需要进行额外的处理,这里的子目录包括cacheurlmapping,其中mappingcache目录还包括了依赖库。

倘若不包含依赖库的话,比如这里的url目录,直接可以写成如下方式,其实这时候也可以省略不写。

s.subspec 'url' do |url|
  url.source_files = 'MLNetWorking/Classes/url/**/*'
end

mapping目录包含了ObjectMapper依赖库,我们可以使用如下方式来声明:

s.subspec 'mapping' do |mapping|
  mapping.dependency 'ObjectMapper'
  mapping.source_files = 'MLNetWorking/Classes/mapping/**/*'
end

cache目录虽然包括了AlamofireMLDataCache,但是这两个库在外层已经依赖过了,所以不用再重新指定,由于cache目录不需要再做任何额外操作了,所以也可以直接省略掉不写。

s.dependency 'Alamofire'
s.dependency 'MLDataCache'

倘若项目中存在资源文件的话,我们可以使用如下的方式来进行设置,比如下面这个库中包括了图片资源和xib的视图,我们可以这样指定:

s.resource_bundles = {
    'MLTRTC' => [
    'MLTRTC/Classes/MLTRTC/View/*.xib',
    'MLTRTC/Assets/*'
    ]
}

项目中存在两个这样的文件,包括外面的根目录下总共有3个,改动任何一个,其余两个都会自动同步。

我们可以在example目录下的podfile文件中重新指定依赖库版本,此处指定的依赖库版本会覆盖掉项目里的,比如项目中的MLToolKit虽然拉取的是最新版本,但是主分支仍然是swift4版本的,我们虽然在swift5分支更新了MLToolKit库,但是还没有合并到主分支,但是我们想要升级MLRTC仓库到swift5的话,就需要依赖的MLToolKit库也是swift5版本的,否则会报错,这样就产生了矛盾,解决的办法就是在podfile文件中重新指定依赖库版本,指定其为swift5分支就可以了。

target 'MLTRTC_Example' do
  pod 'MLTRTC', :path => '../'
  pod 'MLToolKit', :git =>  'https://git.medlinker.com/ios/mltoolkit', :branch => 'swift5'  
  pod 'MLBaseUIKit', :git =>  'https://git.medlinker.com/ios/mlbaseuikit', :branch => 'master-swift5'  
  pod 'MLNetWorking', :git => 'https://git.medlinker.com/liushuoyu/mlnetworking', :branch => 'xjp_restructure'

  target 'MLTRTC_Tests' do
    inherit! :search_paths

  end
end
拓展

除了上面最基本的修改方式外,我们还可以增加一些知识点,比如倘若目录中同时存在OC和Swift的文件,这时候源文件地址又改写成什么?此时我们可以按照如下的方式填写:

s.source_files = 'MLTracking/Classes/**/*.{h,m,swift}','MLTracking/Classes/LocalDataBase/*.{h,m}'

倘若我们想要明确该APP使用的架构,那么可以使用:

s.pod_target_xcconfig = { 'VALID_ARCHS' => 'x86_64 armv7 arm64' }

10、HandyJSON 使用映射

当我们从接口请求到某种枚举类型的时候,其返回的是数字,我们想要将数字和本地的枚举类型匹配起来,这个应该如何操作呢?在HandyJSON中如果你定义的枚举类型实现了RawRepresentable协议,那么可以参考支持枚举类型文档或者使用EnumTransform。倘若使用文档中的方法,就要支持值类型的enum,且需要声明服从HandyJSONEnum协议,除此之外不再需要其他特殊处理了。

enum AnimalType: String, HandyJSONEnum {
    case Cat = "cat"
    case Dog = "dog"
    case Bird = "bird"
}
struct Animal: HandyJSON {
    var name: String?
    var type: AnimalType?
}
let jsonString = "{\"type\":\"cat\",\"name\":\"Tom\"}"
if let animal = Animal.deserialize(from: jsonString) {
    print(animal.type?.rawValue)
}

倘若不使用上面的方式而是直接改为使用EnumTransform的话,可以改为如下方式:

enum EnumType: String {
    case type1, type2
}
class BasicTypes: HandyJSON {
    var type: EnumType?

    func mapping(mapper: HelpingMapper) {
        mapper <<<
            type <-- EnumTransform()
    }

    required init() {}
}
let object = BasicTypes()
object.type = EnumType.type2
print(object.toJSONString()!)
let mappedObject = BasicTypes.deserialize(from: object.toJSONString()!)!
print(mappedObject.type)

除了上面我们举例的枚举类型转化之外,还存在着其他常用的类型转化。

Int64和NSNumber之间的相互转化
public class Int64Transform: TransformType {
    public typealias Object = Int64
    public typealias JSON = NSNumber
    public init() {}
    ...
}
public func transformFromJSON(_ value: Any?) -> Int64? {
    if let numValue = value as? NSNumber {
        return numValue.int64Value
    }
    
    if let numValue = value as? String {
        return (numValue as NSString).longLongValue
    }
    
    return nil
}
public func transformToJSON(_ value: Object?) -> NSNumber? {
    if let intValue = value {
        return NSNumber(value: intValue)
    }

    return nil
}
Date和Double之间的相互转化
public class DateWithoutZeroTransform: TransformType {
    public typealias Object = Date
    public typealias JSON = Double
    public init() {}
    ...
}
public func transformFromJSON(_ value: Any?) -> Date? {
    if let timeInt = value as? Double, timeInt > 0 {
        return Date(timeIntervalSince1970: TimeInterval(timeInt))
    }
    
    if let timeInt = (value as? NSNumber)?.int64Value, timeInt > 0 {
        return Date(timeIntervalSince1970: TimeInterval(timeInt))
    }

    if let timeStr = value as? String {
        let timeInt = atof(timeStr)
        if timeInt > 0 {
            return Date(timeIntervalSince1970: TimeInterval())
        }
    }

    return nil
}
public func transformToJSON(_ value: Date?) -> Double? {
    if let date = value {
        return Double(date.timeIntervalSince1970)
    }
    return nil
}
0 1和Bool之间的相互转化
public class OneTwoBoolTransform: TransformType {
    public typealias Object = Bool
    public typealias JSON = Int
    public init() {}
    ...
}
public func transformFromJSON(_ value: Any?) -> Bool? {
    if let numValue = value as? Int {
        return numValue != 1
    }
    return nil
}
public func transformToJSON(_ value: Object?) -> Int? {
    if let boolValue = value {
        return boolValue == true ? 2 : 1
    }
    return nil
}
1 2和Bool之间的相互转化
public class OneTwoBoolTransform: TransformType {
    public typealias Object = Bool
    public typealias JSON = Int
    public init() {}
}
public func transformFromJSON(_ value: Any?) -> Bool? {
    if let numValue = value as? Int {
        return numValue != 1
    }
    return nil
}
public func transformToJSON(_ value: Object?) -> Int? {
    if let boolValue = value {
        return boolValue == true ? 2 : 1
    }
    return nil
}
String和Int之间的相互转化
public class StringToIntTransform: TransformType {
    public typealias Object = Int
    public typealias JSON = String
    public init() {}
    ...
}
public func transformFromJSON(_ value: Any?) -> Int? {
    if let validValue = value as? String {
        return Int(validValue)
    }
    return nil
}
public func transformToJSON(_ value: Object?) -> String? {
    if let validValue = value {
        return "\(validValue)"
    }
    return nil
}
上一篇下一篇

猜你喜欢

热点阅读