Swift

SwiftUI 页面导航最佳实践

Dev

SwiftUI 页面导航最佳实践

通过全局 Router 1. 定义一个全局 Router 对象,维护页面跳转类型和参数。 @Observable final class Router { public enum Destination: Codable, Hashable { case pageA(models: [Model]) case pageB } var navPath = NavigationPath() func navigate(to destination: Destination) { navPath.append(destination) } func navigateBack() { navPath.removeLast() } func navigateToRoot() { navPath.removeLast(navPath.count) } } 枚举 Destination 可以指

By Gray
Swift Server Push Notification 配置

Dev

Swift Server Push Notification 配置

获取证书 在 Apple Developer 开发者账号 Certificates, Identifiers & Profiles 里选择 Keys。新增一个 key, configure 里选择 Sandbox & Production。下载该 p8 证书,并且保存好(只能下载一次)。 终端 cd 到证书所在路径,输入下面指令。 openssl pkcs8 -nocrypt -in AuthKey_XXXXXXXXX.p8 -out ~/Downloads/key.pem cat key.pem 得到 PRIVATE KEY 字符串,复制好。 服务端配置 服务端有多种技术栈方案,包括 Java、

By Gray
SwiftUI|监测视图更新及更新原因

Dev

SwiftUI|监测视图更新及更新原因

SwiftUI 的优势之一就是可以自动让 UI 和数据状态保持同步。频繁的视图更新也会带来一定的性能问题,尽管 Apple 已经让 SwiftUI 在更新时尽量只渲染视图树中必须重绘的部分,但我们还是需要知道某种可以监测视图更新以及触发其更新的原因的手段。这样我们才能在业务层面做一些针对性的性能优化。 我们可以通过在 View 的 body 视图构造器中插入一条 print 语句来监测到哪些视图的 body 正在被执行。这条语句还必须是一个赋值语句,不能是仅一个 print。因为 print 返回的是一个 Void ,而不是一个 View ,视图构造器会报错。 struct ContentView: View { var body: some View { let _ = print("ContentView Changed!") VStack { Image(systemName: "globe"

By Gray

Swift Concurrency 异步感染问题

Swift Concurrency 异步感染问题 当我们在一个非 async 的方法 A 里面调用 async 的方法 B 时,Xcode 会提示我们无法这么做: 'async' call in a function that does not support concurrency,并且引导我们给 A 加上 async 标识。 ⬆️ 这就是异步感染问题,async 方法的调用方在不知不觉中也变成了 async 方法。 为了避免 async 向上一直感染,可以使用 Task 方式调用 async 方法: func A() { Task { await B() } } func

By Gray

Swift 零碎知识

关于继承 SwiftUI 的解决方案 struct没有继承的功能,所以SwiftUI里面也不能像UIKit那样做写一个BaseView,然后子类继承里面的所有功能。网上有大神给出了这一点的解决方案 Creating BaseView class in SwiftUI 重写 1. 子类重写父类的属性后,也能通过super.someProperty获取父类的属性 关于类初始化 1. 通过给存储属性赋默认值,或者在初始化函数(包括便利构造器)中给存储属性赋值,不会触发该变量的didSet 2. 初始化构造器的三条法则: * A designated initializer must call a designated initializer from its immediate superclass. * A convenience initializer must call another initializer from the same class. * A

By Gray

Swift Package 设置 file header

今天给自己的 Swift Package 仓库新增文件的时候,发现文件头的作者、时间、copy right 等信息不太全,一时兴起研究了一下。 一般来说,给 Xcode Project 新增文件时会带上 organization name。但是单独打开 Swift Package 时,是没有办法设置 organization name 的。这样新建的文件头部就会光秃秃的,不甚美观 一番研究之后发现,可以这样设置 Swift Package 的 file header: 1. 首先新建一个 IDETemplateMacros.plist 文件,并写入下面的内容: <?xml version="1.0" encoding="UTF-

By Gray

Dispatch Semaphore VS Dispatch Group

Dispatch Group Dispatch Group 在项目中比较常见,用于多任务多线程之间的协作。比如,使用两个队列分别请求不同的接口,等请求全部完成后,刷新页面。下面这段代码使用两个队列分别执行不同任务,当两个任务都完成后,通知主队列执行完成代码 import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let group = DispatchGroup() let q1 = DispatchQueue(label: "q1") group.notify(queue: .main) { print("Task All Done!") } q1.async(group: group) { for i in 1 ... 5

By Gray

Swift 单例中的线程安全问题

单例是常见的一种设计模式。最近在编写单例代码的时候,发现公司很多同事的 Swift 单例写法都是这样的, extension NSObject { @discardableResult static func kep_synchronized<T>(_ lock: AnyObject, closure: () -> T) -> T { objc_sync_enter(lock) defer { objc_sync_exit(lock) } return closure() } } class DefaultDict: NSObject { private static var manager: DefaultDict? static var sharedManager: DefaultDict { get { var newShared = manager kep_

By Gray

Swift GCD

基础知识 iOS 多线程实现方式 * Grand Central Dispatch (GCD): 自动管理线程生命周期 * NSOperation: 自动管理线程生命周期,基于 GCD 实现 * NSThread: 手动管理线程生命周期 并发和并行 * 并发: 并发不是真正的多线程,而是通过CPU(单核)在多个线程间快速切换调度,实现接近同时执行的效果 * 并行: 并行是真正的多线程,多个线程能同时执行 同步和异步 * 同步:在当前线程里按顺序执行多项任务,且任务结束的顺序和任务开始执行的顺序是一致的。执行同步任务不会开启新线程,所有的任务都在当前线程执行。把一个同步任务分配出去后,当前线程会一直等到这个任务执行完才能接着运行后面的代码(因为同步任务本身就是分配给当前线程干的) * 异步:异步任务也是按顺序分配给线程的,但是可能会放在多个线程里执行的,所以每个任务结束的顺序有可能是随机的。执行异步任务可能会开启新线程,要具体情况具体分析。把一个异步任务分配出去后,当前线程不用等这个任务执行完就可以执行后面的代码,因为这个异步任务有可能交给别的线程去做了

By Gray

Swift Package 技巧及混编兼容问题

创建 Package mkdir somePath cd somePath swift package init (--type library/executable/empty/system module) 其中,type 的四种类型分别对应: * library: 库(默认) * executable: 可执行文件 * empty: 空项目 * system module: 系统模板项目 一般情况下默认即可 创建 package 之后,还可以使用 swift package generate-xcodeproj 创建一个Xcode项目来编译和调试代码 使用 Package 在 Xcode 菜单栏中,选择 file -> add packages 可以指定 package

By Gray