本章内容概览:Swift自带协议、Codable、网络状态检查、动画、安全、工程、工具
上一节:【iOS】07_Swift自带属性包装
下一节:【iOS】09_Swift编程规范、资料推荐
同系列文章请查看:快乐码元 - iOS篇
1.自带协议 - Hashable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 struct H : Hashable { var p1: String var p2: Int func hash (into hasher : inout Hasher ) { hasher.combine(p1) } } let h1 = H (p1: "one" , p2: 1 )let h2 = H (p1: "two" , p2: 2 )var hs1 = Hasher ()hs1.combine(h1) hs1.combine(h2) print (h1.hashValue) print (h2.hashValue) print (hs1.finalize()) print (h1.hashValue) let h3 = H (p1: "one" , p2: 1 )print (h3.hashValue) var hs2 = Hasher ()hs2.combine(h3) hs2.combine(h2) print (hs2.finalize())
应用生命周期内,调用 combine() 添加相同属性哈希值相同,由于 Hasher 每次都会使用随机的 seed,因此不同应用生命周期,也就是下次启动的哈希值,就会和上次的哈希值不同。
2.Codable
JSON 没有 id 字段
如果SwiftUI要求数据Model都是遵循Identifiable协议的,而有的json没有id这个字段,可以使用扩展struct的方式解决:
1 2 3 4 5 6 7 8 9 10 struct CommitModel : Decodable , Hashable { var sha: String var author: AuthorModel var commit: CommitModel } extension CommitModel : Identifiable { var id: String { return sha } }
3.网络状态检查
通过 Network
库的 NWPathMonitor 来检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import Combineimport Networkfinal class Nsck : ObservableObject { static let shared = Nsck () private(set) lazy var pb = mkpb() @Published private(set) var pt: NWPath private let monitor: NWPathMonitor private lazy var sj = CurrentValueSubject <NWPath , Never >(monitor.currentPath) private var sb: AnyCancellable ? init () { monitor = NWPathMonitor () pt = monitor.currentPath monitor.pathUpdateHandler = { [weak self ] path in self ? .pt = path self ? .sj.send(path) } monitor.start(queue: DispatchQueue .global()) } deinit { monitor.cancel() sj.send(completion: .finished) } private func mkpb () -> AnyPublisher <NWPath , Never > { return sj.eraseToAnyPublisher() } }
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 var sb = Set <AnyCancellable >()var alertMsg = "" Nsck .shared.pb .sink { _ in } receiveValue: { path in alertMsg = path.debugDescription switch path.status { case .satisfied: alertMsg = "" case .unsatisfied: alertMsg = "😱" case .requiresConnection: alertMsg = "🥱" @unknown default : alertMsg = "🤔" } if path.status == .unsatisfied { switch path.unsatisfiedReason { case .notAvailable: alertMsg += "网络不可用" case .cellularDenied: alertMsg += "蜂窝网不可用" case .wifiDenied: alertMsg += "Wifi不可用" case .localNetworkDenied: alertMsg += "网线不可用" @unknown default : alertMsg += "网络不可用" } } } .store(in: & sb)
4.动画
4.1 布局动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import SwiftUIstruct AnimateLayout : View { @State var changeLayout: Bool = true @Namespace var namespace var body: some View { VStack (spacing: 30 ) { if changeLayout { HStack { items } } else { VStack { items } } Button ("切换布局" ) { withAnimation { changeLayout.toggle() } } } .padding() } @ViewBuilder var items: some View { Text ("one" ) .matchedGeometryEffect(id: "one" , in: namespace) Text ("Two" ) .matchedGeometryEffect(id: "Two" , in: namespace) Text ("Three" ) .matchedGeometryEffect(id: "Three" , in: namespace) } }
5.安全
5.1 Keychain
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 let d1 = Data ("keyChain github token" .utf8)let service = "access-token" let account = "github" let q1 = [ kSecValueData: d1, kSecClass: kSecClassGenericPassword, kSecAttrService: service, kSecAttrAccount: account ] as CFDictionary let status = SecItemAdd (q1, nil )if status == errSecDuplicateItem { let q2 = [ kSecClass: kSecClassGenericPassword, kSecAttrService: service, kSecAttrAccount: account ] as CFDictionary let q3 = [ kSecValueData: d1 ] as CFDictionary SecItemUpdate (q2, q3) } let q4 = [ kSecAttrService: service, kSecAttrAccount: account, kSecClass: kSecClassGenericPassword, kSecReturnData: true ] as CFDictionary var re: AnyObject ?SecItemCopyMatching (q4, & re)guard let reData = re as? Data else { return }print (String (decoding: reData, as: UTF8 .self )) let q5 = [ kSecAttrService: service, kSecAttrAccount: account, kSecClass: kSecClassGenericPassword, ] as CFDictionary SecItemDelete (q5)
6.工程
6.1 程序入口点
Swift 允许全局编写 Swift 代码,实际上 clang 会自动将代码包进一个模拟 C 的函数中。Swift 也能够指定入口点,比如 @UIApplicationMain 或 @NSApplicationMain,UIKit 启动后生命周期管理是 AppDelegate 和 SceneDelegate,《 Understanding the iOS 13 Scene Delegate 》这篇有详细介绍。
@UIApplicationMain 和 @NSApplicationMain 会自动生成入口点。这些入口点都是平台相关的,Swift 发展来看是多平台的,这样在 Swift 5.3 时引入了 @main,可以方便的指定入口点。代码如下:
1 2 3 4 5 6 @main struct M { static func main () { print ("let's begin" ) } }
ArgumentParser 库,Swift 官方开源的一个开发命令行工具的库,也支持 @main。使用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 import ArgumentParser@main struct C : ParsableCommand { @Argument (help: "Start" ) var phrase: String func run () throws { for _ in 1 ... 5 { print (phrase) } } }
7.工具
7.1 Swift-DocC
现在支持 Swift、OC 和 C,文档标记一样。.doccarchive
包含可部署的网站内容,兼容大多数托管服务,比如 Github pages。部署到在线服务上可参考 Generating Documentation for Hosting Online 和 Publishing to GitHub Pages 文档。
和 SPM 集成参看 SwiftDocCPlugin 。
session 有 What’s new in Swift -DocC 和 Improve the discoverability of your Swift-DocC content
SE-0356 Swift Snippets 代码片段用于示例文档的提案。
参考资料:
Tips:
Please indicate the source and original author when reprinting or quoting this article.