原创:问题解决型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容
目录
- 1、APP的信息
- 2、屏幕适配
- 3、打开其他应用
- 4、Configuration 项目的配置信息
- 5、模型和JSON转换基类
- 6、监测网络连接状态
- 7、HandyJSON 使用
- 8、私有库的升级方式
- 9、调整私有库的MLNetWorking.podspec文件
- 10、HandyJSON 使用映射
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
来进行获取对应baseURL
,RealEnvType.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
生成的accountType
、appID
、pushID
的时候,我们就可以将获取到的值写入到此处,提供给全局使用。由于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
}
}
当外界想要获取到accountType
、appID
、pushID
的时候,我们根据项目目前所处的环境为其提供了相对应的值,这里以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
)不要求Model
从NSObject
继承(因为它不是基于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_id
,friendName
转化为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
里不存在的属性name
,json
中也有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
目录下面的二级目录,我们需要进行额外的处理,这里的子目录包括cache
、url
、mapping
,其中mapping
和cache
目录还包括了依赖库。
倘若不包含依赖库的话,比如这里的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
目录虽然包括了Alamofire
和MLDataCache
,但是这两个库在外层已经依赖过了,所以不用再重新指定,由于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
}
网友评论