苹果 WWDC 2023 公布了新的 Swift / SwiftUI 特性

  苹果公司在 WWDC23 全球开发者大会上公开了 Swift 编程语言及其生态系统(如 SwiftUI )的新特性。

  值得注意的是,两者本身同属统一生态下,新特性和改进并不相互独立。

  Swift 编程语言

  If /let 和 switch 语句作为表达式

  可以使用内联 if 语句

struct Account {
    var balance: Double
}

let account = Account (balance: 500)

let status =
    if account.balance < 0 { "debt" }
    else if account.balance == 0 { "break even" }
    else { "Profit" }

print("Your account is in \(status).")

  同样可以使用 switch 语句

enum WeatherCondition {
    case sunny
    case cloudy
    case rainy
}

let weather = WeatherCondition.sunny

let advice =
    switch weather {
    case .sunny: "Feeling hot!"
    case .cloudy: "Oooooooh"
    case .rainy: "Don't forget your rain jacket"
    }

print("Today's advice: \(advice)")

  改进的错误检测

struct ContentView: View {
    enum Destination { case one, two }

    var body: some View {
        List {
            NavigationLink (value: .one) { //In 5.9, Errors provide a more accurate diagnostic
                Text ("one")
            }
            NavigationLink (value: .two) {
                Text ("two")
            }
        }.navigationDestination (for: Destination.self) {
            $0.view // Error occurs here in 5.7
        }
    }
}

  值和类型参数包

  Parameter Packs ,一种接受任意数量参数的函数的方法,使它们成为可变函数。此外,这些参数可以是不同的类型。参数包与泛型等概念相结合时,可以使代码更加灵活和可重用。

  下面的示例来自苹果官方 swift 示例仓库

struct Pair<First, Second> {
    let first: First
    let second: Second
    init (_ first: First, _ second: Second) {
        self.first = first
        self.second = second
    }
}

func makePairs<each First, each Second>(
  firsts first: repeat each First,
  seconds second: repeat each Second
) -> (repeat Pair<each First, each Second>) {
    (repeat Pair (each first, each second))
}

let pairs = makePairs (firsts: 1, "hello", seconds: true, 1.0)
print(pairs)

  Macros 宏指令

  宏就像类型或函数一样是 API,可以在编译时生成重复代码,比如自定义 Codable 等模块,也可以集成进入 Swift Package 中。

  Swift Data

  不用自定义数据库,告别了复杂数据流,只需要一个 @Model 关键词。

  文章无法一一介绍改动,感兴趣的朋友可以在 WWDC23 回放中查看完整视频,了解所有关于 Swift 改动的内容。

  SwiftUI 的新东西

  Data flow

  SwiftUI 不再使用 Combine,而是使用新的 Observation 框架。 Observation 框架提供了 Observable 协议,必须使用它来允许 SwiftUI 订阅更改和更新视图。

  今年的更新把重点放在了 @State 和 @Environment 上,其它的都用 Macro 解决了。这点可以大幅度了减轻代码中对于 EnvironmentState 和 ObjectState 的使用。

  Animations 动画

  SwiftUI 框架有新版本的 withAnimation 函数,允许提供动画完成处理程序。这是一个很好的补充,可以构建阶段性动画。

  此外引入了新的 PhaseAnimator 视图,该视图迭代阶段序列,允许为每个阶段提供不同的动画,并在阶段发生变化时更新内容。另外还有 KeyframeAnimator 视图,允许使用关键帧动画。

struct AnimationExample: View {
    @State private var value = false
    
    var body: some View {
        Text (verbatim: "Hello")
            .scaleEffect (value ? 2 : 1)
            .onTapGesture {
                withAnimation {
                    value.toggle ()
                } completion: {
                    print("Animation have finished")
                }
            }
    }
}

  ScrollView 滚动视图

  ScrollView 有一些更新,比如可以使用 scrollPosition 视图修饰符来观察内容偏移。

struct ContentView: View {
    @State private var scrollPosition: Int? = 0
    
    var body: some View {
        ScrollView {
            Button ("Scroll") {
                scrollPosition = 80
            }
            
            ForEach (1..<100, id: \.self) { number in
                Text (verbatim: number.formatted ())
            }
            .scrollTargetLayout ()
        }
        .scrollPosition (id: $scrollPosition)
    }
}

  其次还可以使用 scrollTargetBehavior 视图修饰符更改滚动行为,它允许在滚动视图中启用分页。

  Search 搜索

  与搜索相关的视图修改器也有一些补充,例如可以以编程方式关注搜索字段。

struct ProductsView: View {
    @State private var store = Store ()
    @State private var query = ""
    @State private var scope: Scope = .default
    
    var body: some View {
        List (store.products, id: \.self) { product in
            Text (verbatim: product)
        }
        .task {
            if store.products.isEmpty {
                await store.fetch ()
            }
        }
        .searchable (text: $query, isPresented: .constant (true), prompt: "Query")
        .searchScopes ($scope, activation: .onTextEntry) {
            Text (verbatim: scope.rawValue)
        }
    }
}

  在上面的示例,可以使用可搜索视图修饰符的 isPresented 参数来显示 / 隐藏搜索字段,还可以使用 searchScopes 视图修饰符的激活参数来定义范围可见性的逻辑。

  New gestures 新手势

  新的 RotateGesture 和 MagnifyGesture 允许跟踪视图的旋转和放大。

struct RotateGestureView: View {
    @State private var angle = Angle (degrees: 0.0)

    var rotation: some Gesture {
        RotateGesture ()
            .onChanged { value in
                angle = value.rotation
            }
    }

    var body: some View {
        Rectangle ()
            .frame (width: 200, height: 200, alignment: .center)
            .rotationEffect (angle)
            .gesture (rotation)
    }
}

  WWDC 关于 SwiftUI 的演讲长达 34 分钟,文章无法一一介绍其改动,感兴趣的朋友可以在 WWDC23 官方回放中查看完整视频。

  值得一提的是,watchOS 已全部用 SwiftUI 重写。可以看到诸如 NavigationSplitView 的应用,诸如半透明材质效果,右侧小边栏按钮,半透明 Tab 。