Swift入门教程:iOS开发第一步

机器学习厨子
2025-12-18 14:13
阅读 2260

上周五晚上十一点半,我正窝在工位上用 Vim 撸 Rust 代码,试图搞明白 Pin<Box<dyn Future>> 到底是个什么鬼东西。突然手机一震,钉钉弹出一条消息:“@所有人:老板说下周要上线一个 iOS 小工具 App,展示我们新 AI 功能的移动端入口,Swift 写,两周 deadline。”

我差点把咖啡喷到机械键盘上——我可是个纯血 Vim 党啊!连 Xcode 都没装过!

但没办法,谁让我是组里唯一一个“好像会点移动端”的人(其实是因为我去年帮测试同学改过一个 React Native 的小 bug)。于是周末两天,我硬着头皮从零开始学 Swift,一边被 Apple 生态的文档绕晕,一边在 Cursor 里疯狂 Ctrl+K 让它帮我生成 SwiftUI 代码。还好有 AI 搭把手,不然真得跪在 Storyboard 里。

今天这篇笔记,就是给和我一样——后端/系统出身、习惯命令行、对 GUI 开发一脸懵——但又被业务逼上 iOS 赛道的朋友准备的。不讲理论大道理,直接上手搞个项目,边做边学。毕竟,在我们组,“能跑就行”永远比“优雅”更重要(产品经理原话)。


被迫营业:为什么选 Swift?

先说清楚,我不是果粉,也不是为了情怀。选择 Swift 纯粹因为:

  • Apple 官方力推:Objective-C 已经基本没人写了,Swift 是未来
  • 语法现代:有类型推断、模式匹配、函数式支持,对我这种 Rust 爱好者很友好(虽然远不如 Rust 严谨)
  • SwiftUI 真香:声明式 UI,写起来像 React + Tailwind,比 Autolayout 那堆约束清爽一万倍

而且说实话,Swift 的错误处理(Result, throws)和 Optionals(Optional<T>)设计得还挺合理,至少比 Java 的 NullPointerException 有提前量。


项目目标:做一个“AI 今日推荐”小工具

需求很简单:

  • 启动 App 显示一句 AI 生成的每日金句
  • 点击刷新按钮换一句
  • 支持深色模式
  • 能打包上架 TestFlight

听起来 trivial?但在 Apple 生态里,光是“联网请求”和“App 上架”就能卡住 80% 的新手。别笑,我第一天就因为 ATS(App Transport Security)被拦在门外,HTTP 请求直接被干掉,报错信息还特别 cryptic:

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure.

后来才知道必须用 HTTPS,或者在 Info.plist 里加白名单(别在生产环境这么干!)。


环境搭建:告别命令行,拥抱 Xcode(暂时)

我知道,作为 Vim 党,看到 GUI IDE 就想逃。但现实是:Xcode 是 iOS 开发绕不开的爹。你可以在终端用 swift build 编译,但调试、模拟器、签名、上架……全得靠它。

安装步骤极简:

  1. App Store 搜 Xcode(注意:30GB 起步,建议清理硬盘)
  2. 打开,同意一堆协议(Apple:我让你点 10 次“同意”)
  3. 等它下载完额外组件(可能又要半小时)

装完后,新建项目:

  • Template 选 App
  • Interface 选 SwiftUI
  • Language 选 Swift
  • 勾上 Include Tests(虽然我们大概率不会写)

项目名我叫 AIDailyQuote,Bundle Identifier 用你自己的(比如 com.yourname.AIDailyQuote),后面 App Store Connect 要用。


写第一行 Swift 代码:Hello, SwiftUI!

打开 ContentView.swift,你会看到默认模板:

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
        }
        .padding()
    }
}

这就是你的主界面。VStack 是垂直布局容器,Text 是文本,.padding() 是修饰符(类似 CSS class)。SwiftUI 的核心思想就是:UI = 函数(state) → View

现在,我们要加一个按钮和网络请求。先定义状态:

import SwiftUI

struct ContentView: View {
    @State private var quote = "Loading..." // ← 关键!@State 让变量可变且驱动 UI 更新
    @State private var isLoading = false
    
    var body: some View {
        VStack(spacing: 20) {
            Text(quote)
                .font(.title2)
                .multilineTextAlignment(.center)
                .padding()
            
            Button("Refresh") {
                fetchQuote()
            }
            .disabled(isLoading)
        }
        .padding()
        .onAppear {
            fetchQuote() // 首次加载
        }
    }
    
    private func fetchError() {
        // 稍后实现
    }
}

注意到 @State 了吗?这是 SwiftUI 的响应式魔法。只要 quote 变了,整个 body 自动重绘。不用 setState,不用 useEffect,Apple 替你做了 diff。


联网请求:别被 URLSession 吓到

Swift 的原生网络库 URLSession 其实挺干净,配合 async/await 更爽。但我们得处理错误、loading 状态、JSON 解析。

先定义 API 返回结构(假设我们的后端返回 {"quote": "Life is short", "author": "Someone"}):

struct QuoteResponse: Codable {
    let quote: String
    let author: String
}

然后实现 fetchQuote

private func fetchQuote() {
    guard let url = URL(string: "https://api.example.com/daily-quote") else {
        quote = "Invalid URL"
        return
    }
    
    isLoading = true
    
    Task {
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            let response = try JSONDecoder().decode(QuoteResponse.self, from: data)
            await MainActor.run {
                self.quote = "\(response.quote)\n— \(response.author)"
                self.isLoading = false
            }
        } catch {
            await MainActor.run {
                self.quote = "Failed to load: \(error.localizedDescription)"
                self.isLoading = false
            }
        }
    }
}

重点解释:

  • Task { } 启动异步任务
  • try await 是 Swift 5.5+ 的并发模型,类似 Rust 的 async/await(但没那么严格)
  • MainActor.run 确保 UI 更新在主线程(否则崩溃!)

坑点提醒:如果你用的是自签名证书或 HTTP,记得在 Info.plist 加:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

但上架前务必删掉!否则 App Store 审核直接拒。


资源管理:图片、本地化、深色模式

Apple 对资源有严格规范。所有图片、字符串都要放进 Assets CatalogLocalizable.strings

图标 & Launch Screen

  • 把 App Icon 拖进 Assets.xcassets/AppIcon(需要多尺寸,可用 App Icon Generator 批量生成)
  • 启动图用 LaunchScreen.storyboard(别改,除非你想被设计师追杀)

深色模式适配

SwiftUI 默认支持!只要别写死颜色。比如:

Text("Hello")
    .foregroundColor(.primary) // 自动适配 light/dark

如果要用自定义色,定义在 Assets.xcassets 里,勾选 Appearances → Any, Dark,分别填色值。

本地化(国际化)

  1. 项目设置 → Info → Localizations → + 添加语言(如 zh-Hans)
  2. 创建 Localizable.strings 文件
  3. 写 key-value:
"refresh_button" = "刷新";
"loading" = "加载中...";
  1. 代码中调用:
Text(NSLocalizedString("refresh_button", comment: ""))

打包上架:闯过 Apple 的十八道关卡

终于写完了?别高兴太早,App Store 审核才是终极 BOSS

步骤概览

步骤 工具 耗时 痛点
1. 注册开发者账号 Apple Developer ¥688/年 要信用卡
2. 创建 App ID Certificates, Identifiers & Profiles 10分钟 Bundle ID 不能改
3. 生成证书 & Provisioning Profile Xcode 自动处理 5分钟 网络差时卡住
4. Archive & Validate Xcode → Product → Archive 10分钟 依赖检查严
5. 提交到 App Store Connect Organizer 5分钟 元数据要填全

我踩过的雷

  • 隐私描述缺失:即使没用相机/定位,也要在 Info.plist 声明 NSLocationWhenInUseUsageDescription 等(哪怕留空),否则拒审
  • 截图分辨率不对:iPhone 需要 1242×2688(Pro Max),别拿模拟器随便截
  • 测试账号没加:TestFlight 外部测试需审核,内部测试需在 App Store Connect 加成员邮箱

最离谱的一次:因为 App 名字叫 “AI Daily”,审核员以为我们用了 Apple 的 AI 商标,要求改名。最后改成 “Daily Wisdom” 才过。


总结:Swift 没那么可怕,但 Apple 生态很傲娇

两周 deadline 最终还是赶上了。虽然 App 只有一页,但走通了从编码 → 调试 → 打包 → 上架全流程。现在每次看到 TestFlight 里同事的反馈,都有一种“老子居然搞定了 iOS”的荒诞成就感。

回头看看,Swift 其实对后端工程师很友好:

  • 强类型减少运行时错误
  • SwiftUI 声明式写法接近现代前端
  • Package Manager(SPM)比 CocoaPods 清爽多了

但 Apple 的封闭生态依然让人抓狂:调试日志藏得深、模拟器吃内存、审核规则不透明……不过嘛,只要产品能上线,这些都可以忍(毕竟工资照发)。

最后放个彩蛋:我全程用 Cursor + Vim 模式 写 Swift。虽然 Xcode 的 Vim 插件(如 XVim2)有点 lag,但总比用鼠标点来点去强。Rust 和 Swift 交替写,感觉脑子在两种范式间反复横跳,还挺刺激。

如果你也正被业务逼着学 iOS,别慌。从一个小项目开始,用 AI 辅助(比如让 Cursor 解释 @StateObject@ObservedObject 区别),很快就能上手。记住:在程序员的世界里,没有“不会”,只有“还没被 deadline 逼到”

Happy coding, and may your App Review be swift! 🍏

评论 0

最热最新
暂无评论
机器学习厨子Lv.1
0
影响力
0
文章
0
粉丝