V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
samray
V2EX  ›  分享创造

100 Days of SwiftUI

  •  
  •   samray ·
    ramsayleung · 19 天前 · 1327 次点击

    花了大半年的时间,体验了一下用 Swift 的野生 IOS 开发的感觉, 原文发在博客上


    1 缘起

    我花了半年多的时间,在闲暇时间,学习了苹果的 Swift 语言和 SwiftUI 框架,想体验下 IOS 开发,再看下有没有机会通过写软件来做点副业。

    先花了大概 3 个月时间,通过阅读 The Swift Programming Language 这本官方电子书[^1]来学习 Swift 这门语言,又花了接近 4 个月的时候来学习 100 Days of SwiftUI 这门课程[^2],每天花费 1 到 2 小时来学习一课,总共 100 课,所以顾名思义叫 100 Days of SwiftUI, 课程非常新且好,讲师功力深厚,课讲得深入浅出,娓娓道来。

    每完成一课,就在 Twitter 上发一条推文,今天刚好把第 100 天的推文发了.




    今天是结课之日,我通过了结课的考试,总分 100 分,考了 91 分,喜提课程证书一枚.


    在整个课程中,我写了 19 个 IOS App(虽说大部分是功能简单的 App), 源码也基本放在 GitHub [^3]上了,不过所有的 App 都没有上架 App Store ,因为我还没有给苹果交税(99 美刀的开发者注册费).

    经过这 100 节课和 19 个 APP 的训练,我自觉已经掌握了使用 Swift 和 SwiftUI 的基础开发技能,算是个入门的 IOS 开发了, 现在我可以说自己是前端,后端,数据开发,IOS 开发都搞过的全栈()工程师了(不是)

    但是在苹果对 SwiftUI 开发思路做出改变之前,我 SwiftUI 之旅可能就先到此为止了,原因下文再谈

    2 Swift 初体验

    Swift 是由 LLVM 之父 Chris Lattner [^4]在 2010 开始开发,在 2014 年的 WWDC 苹果开发者大会正式推出的一门编程语言。

    按照官方的说法,Swift 从 Objective-C, Rust, Haskell, Ruby, Python, C#身上都有不同程度的借鉴和学习。

    因为我对上面提到的语言多少有涉猎,所以学习 Swift 起来基本没有什么困难, Optional, Error Handling, Result, Generic, Enumerations, Protocol 这些概念都和 Rust 的大同小异。

    又是由 LLVM 之父来操刀,所以语言本身也设计得很优雅.

    让我眼前一亮的可能是借鉴自 C# Extension Methods [^5]的 extension 功能 , 可以对已有的 class, enum 或者是 protocol 类型增加新的函数,也就是在不修改源码的情况下,扩展已有的功能.

    例如,以下的代码就可以扩展内置的 Double 类型, 实现以米为单位,进行千米, 厘米,毫米,公尺的转换:

    extension Double {
        var km: Double { return self * 1_000.0 }
        var m: Double { return self }
        var cm: Double { return self / 100.0 }
        var mm: Double { return self / 1_000.0 }
        var ft: Double { return self / 3.28084 }
    }
    let oneInch = 25.4.mm
    print("One inch is \(oneInch) meters")
    // Prints "One inch is 0.0254 meters"
    let threeFeet = 3.ft
    print("Three feet is \(threeFeet) meters")
    // Prints "Three feet is 0.914399970739201 meters"
    

    总体而言, Swift 是一门吸收了众多 PL 理论的现代编程语言, 官方说支持 Linux ,Windows ,MacOS 等多个平台,不过我估计大多是在 MacOS 上用来写 IOS 和 Mac 应用

    3 SwiftUI

    SwiftUI 使用的声明式语法,让开发者写页面布局和效果变得简洁清晰, 例如通过 VStack, HStack, ZStack 就可以实现 X 轴,Y 轴,和 Z 轴方向的布局

    例如下面这个就是通过 ZStack 几行代码实现的叠加效果:

        let colors: [Color] =
        [.red, .orange, .yellow, .green, .blue, .purple]
    
    
    var body: some View {
        ZStack {
            ForEach(0..<colors.count) {
                Rectangle()
                    .fill(colors[$0])
                    .frame(width: 100, height: 100)
                    .offset(x: CGFloat($0) * 10.0,
                            y: CGFloat($0) * 10.0)
            }
        }
    }
    


    除了声明式语法之外,SwiftUI 让人赏心悦目的就是动画。好的动画在 App 里面绝对能起到画龙点睛的作用,而 SwiftUI 的内置动画已经非常强大了,下面就是使用内置动画实现的动画效果:

    struct ContentView: View {
        @State private var dragAmount = CGSize.zero
        @State private var enable = false
        let letters = "Hello, World"
        var body: some View{
            HStack(spacing: 0) {
                ForEach(0..<letters.count, id: \.self) { index in
                    Text(String(letters[letters.index(letters.startIndex, offsetBy: index)]))
                        .padding(5)
                        .font(.title)
                        .background(enable ? .green : .blue)
                        .offset(dragAmount)
                        .animation(.linear.delay(Double(index) / 20), value: dragAmount)
                }
            }.gesture(
                DragGesture()
                    .onChanged {
                        dragAmount = $0.translation
                    }
                    .onEnded { _ in
                        dragAmount = CGSize.zero
                        enable.toggle()
                    }
            )
        }
    }
    


      struct HeartBeatView: View {
        @State private var animationAmount = 1.0
        var body: some View {
            Button("SOS"){
            }
            .padding(50)
            .background(.red)
            .foregroundColor(.white)
            .clipShape(Circle())
            .overlay(
                Circle()
                    .stroke(.red)
                    .scaleEffect(animationAmount)
                    .opacity(2 - animationAmount)
                    .animation(.easeOut(duration: 1)
                        .repeatForever(autoreverses: false), value: animationAmount)
            )
            .onAppear {
                animationAmount = 2
            }
        }
    }
    


    而 Xcode 15 新增的预览功能也很好用,可以让开发者不需要启动 iPhone 模拟器就能预览页面效果,节省了非常多的等待时间。


    4 问题

    听起来好像很美好: IDE 新功能好用,编程语言优雅, UI 框架简洁好用; 但是苹果的开发思路却有问题: 苹果开发的 SwiftUI 不向后兼容老版本的 IOS 。

    SwiftUI 大部分功能都是只支持 IOS16 及以后的版本,而苹果新出来的数据持久框架 SwiftData 甚至只支持 IOS17,
    更离谱的是,SwiftUI 的 BugFix 也只支持高版本 IOS, 这就意味着用户不升级 IOS 版本,甚至 SwiftUI 的 bug 开发者都没法修复。

    我自己的手机也只更新到 IOS16 ,所以我时常会遇到我自己写的 App 没法运行到我自己手机上的情况。

    不支持旧版本的 IOS 就让一大批的开发者和公司都没有动力去使用 SwiftUI:

    对于开发新应用的开发者而言,只支持 IOS17 就意味着会流失一大群使用 IOS16 及以下版本的用户,
    而对于拥有存量用户的公司而言,更没有动力去使用 SwiftUI ,用了之后,旧版本 IOS 的用户可能直接无法打开应用。

    因此 SwiftUI 就陷入了一个尴尬的境地,东西做得好,但是不会有人用;

    没有人自然就不用有人分享,宣传这门技术,自然就导致相关的学习资料非常匮乏, 进一步加深了初学者的学习难度;

    开发遇到问题连懂的人都不用,官方文档写了又约等于没有写, 直接劝退初学者,恶性循环。

    又因为接受 SwiftUI 的开发者还不多,苹果版本迭代起来更加肆无忌惮,新版本又引入一堆的 Breaking change ,导致开发者更新版本非常痛苦.

    另外一个问题就是 SwiftUI 与苹果现有框架整合得不够好,如 CoreImage 框架,顾名思义是用来作图片处理.

    但之前是使用 Objective-C 写的,通过 SwiftUI 来调用,就会变成相当恶心,需要把 Swift 的数据结构传换成 Objective-C 来处理, 如:

    func applyProcess(){
        guard let outputImage = currentFilter.outputImage else {return}
        guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else{return}
        let uiImage = UIImage(cgImage: cgImage)
        processedImage = Image(uiImage: uiImage)
    }
    
    func loadImage() {
        Task{
            guard let imageData = try await selectedItem?.loadTransferable(type: Data.self) else {return}
    
            guard let inputImage = UIImage(data: imageData) else {return}
    
            let beginImage = CIImage(image: inputImage)
            currentFilter.setValue(beginImage, forKey: kCIInputImageKey)
            applyProcess()
        }
    }
    

    CoreImage 框架的 CIImage 转成 CoreGraphics 框架的 CGImage, 然后再把 CGImage 转换成 UIKit 框架 UIImage, 然后再转换回 SwiftUI 内置的 Image 类型, 可谓是相当麻烦了.

    但是对比 SwiftUI 只支持高版本的问题,Objective-C 和 Swift 的互操作问题也只能算是恶心,但是起码有解决方法,对于前者,开发者是完全没法自行解决.

    5 总结

    过了一把野生 IOS 开发的瘾,但是除非是苹果愿意让 SwiftUI 支持低版本的 IOS ,
    不然我是没有太大意愿继续使用 SwiftUI 来开发 IOS 了,受众比较有限了。

    想要支持低版本的 IOS ,就只能走 UIKit 和 Objective-C 这条历史老路,我对此着实是望而生畏,有空还是学习点其他有趣的东西。

    [^1]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/guidedtour/
    [^2]: https://www.hackingwithswift.com/100/swiftui
    [^3]: https://github.com/ramsayleung?tab=repositories&q=&type=&language=swift&sort=
    [^4]: https://en.wikipedia.org/wiki/Chris_Lattner
    [^5]: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods

    4 条回复    2024-04-11 13:49:02 +08:00
    iOCZS
        1
    iOCZS  
       19 天前
    的确,SwiftUI 不升级到高版本系统,很多功能没法用或者不好用
    netabare
        2
    netabare  
       19 天前   ❤️ 1
    之前也稍微有用 Swift 来开发,感觉 Swift 和 SwiftUI 这一套技术栈确实是蛮有趣的。不过也确实目前而言有许多不方便的地方。

    首先说 Swift 的话,我觉得这门语言是一个有点苦涩的语言……当然这可能和我比较偏向于 Kotlin/C#/ML 这类托管语言有关。首先就是这个 protocol 似乎和 OOP 的 interface 并不完全等价,所以在实际使用的时候会有许多小坑(例如泛型里面的`& any`),虽然也是很细枝末节的事情了。然后在实际编程的时候,虽然从 Java 过来的可能会觉得很方便,但用惯了 Kotlin/Scala 的总觉得 lambda 和流式编程缺点味道,那个 void 也比较恶心人,再加上时不时摆烂的类型推导/类型检查,经常给人一种累觉不爱的感觉。

    虽然说,考虑到 Swift 也蛮老了,再加上历史包袱/性能要求还有和 ObjC 这个老古董的兼容,其实也没法要求更多就是。反过来说,Swift 至少在 Optional 和 Actor 的支持上,似乎走的比 Kotlin 这种一般通过 OOP 语言更远,那个 throws 的设计就感觉蛮惊艳的( op 用 Rust 倒是可能会蛮习惯了)。

    SwiftUI 的话,我比较喜欢的也是这种充分和背后的编程语言结合的设计,比如说 Binding 还有 Observable 的语法。

    不过就我自己感觉,这个生态圈最大的问题可能还是新手不友好。稍微形容一下,大概类似于一开始有很贴心的官方教材辅导告诉你 1+1=2 和怎么解二元一次方程,但然后等你开始学微积分的时候就发现只能自己摸索了。op 提到的课程我之前也有找到过,但那时候我大概处于「课程或者书本上的东西我都会,但我不会的东西找不到教材」的程度,所以这些书或者教程一个也用不了,最后也是靠着 v2ex+stackoveflow+自己摸索来解决的。也许归根结底,还是生态圈太小了。

    整体来说,感觉 Swift/SwiftUI 算是那种慢慢演进会变得越来越好用的,代价就是得丢掉低版本的软硬件,还有越来越高的配置要求了。

    还是希望 SwiftUI 能越来越好吧,再怎么说,写起来还是比某框架的括号地狱好多了。而且比起某企业级状态管理和清洁架构,我还是更喜欢这种声明式代码。
    samray
        3
    samray  
    OP
       18 天前
    @netabare 对初学者不友好这个真的是深有体会,很多内容着实太新,搜索引擎也搜不到,只能靠自己对着 IDE 自行尝试。
    xieren58
        4
    xieren58  
       18 天前
    SwiftUI 是趋势...
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   973 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 20:05 · PVG 04:05 · LAX 13:05 · JFK 16:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.