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

100 Days of SwiftUI

  •  
  •   samray
    ramsayleung 2024-04-10 14:51:25 +08:00 3661 次点击
    这是一个创建于 627 天前的主题,其中的信息可能已经有所发展或是发生改变。

    花了大半年的时间,体验了一下用 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

    5 条回复    2025-02-19 19:51:49 +08:00
    iOCZS
        1
    iOCZS  
       2024-04-10 15:27:36 +08:00
    的确,SwiftUI 不升级到高版本系统,很多功能没法用或者不好用
    netabare
        2
    netabare  
       2024-04-11 03:48:10 +08:00   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
       2024-04-11 12:12:10 +08:00
    @netabare 对初学者不友好这个真的是深有体会,很多内容着实太新,搜索引擎也搜不到,只能靠自己对着 IDE 自行尝试。
    xieren58
        4
    xieren58  
       
    SwiftUI 是趋势...
    Meijer
        5
    Meijer  
       311 天前
    作者现在怎么样,好纠结啊,学了一段时间了,这个 swift ui ,用着别扭我也客服了,xcode 也是屎中屎。现在基本都是 ios16 了吧。最低 16 基本是不是就覆盖有效用户了。用来开发行不行啊,不太想再学个 UIKit 和 Objective-C 了。有什么建议吗
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2695 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 11:42 PVG 19:42 LAX 03:42 JFK 06:42
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86