1. Property wrappers in Swift【属性包装】
官方文档 -- Property wrappers
Swift 5 属性包装器Property Wrappers完整指南 -- 掘金
0258-property-wrappers
- 它在管理属性存储方式与定义属性的代码之间增加了一层封装
A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property.
主旨就是:通过property Wrapper机制,对一些类似的属性的实现代码做同一封装。
说简单点: 通过@propertyWrapper可以移除掉一些重复或者类似的代码。
- 定义
@propertyWrapper
struct TwelveOrLess {
private var number: Int
init() { self.number = 0 }
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
- 使用
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"
rectangle.height = 10
print(rectangle.height)
// Prints "10"
rectangle.height = 24
print(rectangle.height)
// Prints "12"
- 使用限制
Property wrappers并非没有限制。 他们强加了许多限制:
带有包装器的属性不能在子类中覆盖。
具有包装器的属性不能是lazy,@NSCopying,@NSManaged,weak或unowned。
具有包装器的属性不能具有自定义的set或get方法。
wrappedValue,init(wrappedValue :)和projectedValue必须具有与包装类型本身相同的访问控制级别
不能在协议或扩展中声明带有包装器的属性。
属性包装器需要Swift 5.1,Xcode 11和iOS 13。
2. JSON Parsing in Swift
-
SwiftyJSON Github 上 Star 最多的 Swift JSON 解析框架. 其本质其实只是将 JSON 解析成了字典类型的数据,而实际使用时依然需要使用下标方式去取值,非常繁琐且容易出错,不易阅读和维护。
-
HandyJSON 阿里推出的一个用于 Swift 语言中的 JSON 解析框架。采用的是 Swift 反射 + 内存赋值的方式来构造 Model 实例。但是有内存泄露,兼容性差等问题。思路与 Codable 殊途同归。
- ObjectMapper 面向协议的 Swift JSON 解析框架, 使用范型机制进行模型解析,但是需要手动对每一个属性写映射关系。
struct Record: Mappable {
var id: Int?
var quarter: String?
var volumeOfMobileData: String?
var volume: NSDecimalNumber {
return NSDecimalNumber(string: volumeOfMobileData)
}
init?(map: Map) {}
init() {}
mutating func mapping(map: Map) {
id <- map["_id"]
quarter <- map["quarter"]
volumeOfMobileData <- map["volume_of_mobile_data"]
}
}
-
JSONDecoder Apple 官方推出的基于
Codable
的 JSON 解析类 - Codable是一个协议组合
/// A type that can convert itself into and out of an external representation.
public typealias Codable = Decodable & Encodable
/// A type that can encode itself to an external representation.
public protocol Encodable {
public func encode(to encoder: Encoder) throws
}
/// A type that can decode itself from an external representation.
public protocol Decodable {
public init(from decoder: Decoder) throws
}
- 二次封装: 因为Codable 是
Data <-> Object
之间的转化,而通常我们可能更需要的是Dictionary <-> Object
之间的转化。
import Foundation
public typealias JSONObject = [String: Any]
public typealias JSONCodable = JSONEncodable & JSONDecodable
public protocol JSONDecodable: Decodable {
init(json: JSONObject) throws
}
public protocol JSONEncodable: Encodable {
func toDictionary() -> JSONObject?
}
public extension JSONDecodable {
init(json: JSONObject) throws {
let data: Data = try JSONSerialization.data(withJSONObject: json, options: [])
self = try JSONDecoder().decode(Self.self, from: data)
}
}
public extension JSONEncodable {
func toDictionary() -> JSONObject? {
guard let data = try? JSONEncoder().encode(self) else {
return nil
}
guard let serializedObject = try?
JSONSerialization.jsonObject(with: data, options: []) else {
return nil
}
guard let jsonObject = serializedObject as? JSONObject else {
return nil
}
return jsonObject
}
}
3. Unit Test:UserDefaults
对单例的测试
对数据持久化的测试
class UserDefaultsManager {
static let shared = UserDefaultsManager(UserDefaults.standard)
let userDefaults: UserDefaults!
init(_ userDefaults: UserDefaults) {
self.userDefaults = userDefaults
}
}
extension UserDefaultsManager {
func setBannersEnabled(_ enabled: Bool, for key: String) {
self.userDefaults.set(enabled, forKey: key)
}
func getBannersEnabled(by key: String) -> Bool {
return self.userDefaults.bool(forKey: key)
}
}
//
// UserDefaultsManagerTests.swift
// SphDemoTests
//
// Created by shengnan liu on 19/5/20.
// Copyright © 2020 sph. All rights reserved.
//
import Quick
import Nimble
import RxBlocking
@testable import SphDemo
class UserDefaultsManagerTests: QuickSpec {
override func spec() {
var manager: UserDefaultsManager!
var userDefaults: UserDefaults!
let suitName = "Test"
beforeEach {
userDefaults = UserDefaults(suiteName: suitName)
manager = UserDefaultsManager(userDefaults)
}
afterEach {
userDefaults.removePersistentDomain(forName: suitName)
manager = nil
userDefaults = nil
}
describe("UserDefaultsManager") {
it("should get BannersEnabled after saved") {
let mockValue = true
manager.setBannersEnabled(mockValue, for: "BannersEnabled")
let bannersEnabled = manager.getBannersEnabled(by: "BannersEnabled")
expect(bannersEnabled) == mockValue
}
it("shouldn't get BannersEnabled before saved") {
let bannersEnabled = manager.getBannersEnabled(by: "BannersEnabled")
expect(bannersEnabled) == false
}
}
}
}
网友评论