門市最常被問的一句是:「我有一台 MacBook,想自己做 App,到底要從哪開始?」通常我會反問對方一句:你卡的是「不知道學什麼」,還是「東學一塊西學一塊、永遠做不出一個完整 App」?十之八九是後者。網路上 iOS 教學多到爆,但很少有人把順序講清楚,結果語法背一堆、畫面卻拼不出來。
所以這篇我把它當成一條路線在走。四段:先把 Swift 語法摸熟,再用 SwiftUI 把畫面做出來,接著讓 App 真的去抓資料、存資料,最後簽名打包送上架。一段大概一兩週,慢一點也沒關係。環境上你只要一台跑 macOS 14(Sonoma)以上的 Mac、加上 Xcode 16,就是現在最穩的組合;Swift 5.9 跟 SwiftUI 的新東西都內建在裡面,不用另外裝一堆第三方框架。上架那一步才需要 Apple Developer 帳號,前面練功階段都不用花錢。
Xcode 直接從 Mac App Store 裝最新版(現在是 16.x),不想等 App Store 排隊的話,也可以去 Apple Developer 頁面抓 .xip 自己解。裝完別急著開新專案,先讓它跑一次「Install Additional Components」(第一次開 Xcode 會自己跳),不跑這步 Simulator 是叫不起來的,這個坑門市每隔幾天就會遇到一次。
裝好後習慣在終端機確認一下版本:
# 確認版本
xcode-select --version
# 確認 Swift 版本
swift --version
老實講,Xcode 是個肥東西,本體就超過 8 GB。下載前先看一眼磁碟,至少留 15 GB 比較保險,不然裝到一半空間不夠最讓人崩潰。安裝路徑就維持預設的 /Applications/Xcode.app,因為有些命令列工具的路徑是寫死在這的,搬走會出怪事。
接著才是語法。我不建議一開始就開大專案,太容易迷路。Xcode → File → New → Playground(選 macOS),改一行就看一行結果,學語法用這個最快。新手真正要先吃透的,其實就這幾個概念,其他的用到再查也來得及。
第一個是型別推導跟 Optional,這是 Swift 最核心的東西:
var name: String = "小明" // 明確型別
var age = 25 // Swift 自動推導為 Int
var nickname: String? = nil // Optional,可能沒有值
let display = nickname ?? "未設定" // ?? 提供預設值
再來是 struct 跟 class 的差別。為什麼先講這個?因為 SwiftUI 整套幾乎都建在 struct 上,你不熟它後面會卡得很莫名其妙:
struct User {
var name: String
var score: Int
}
var u = User(name: "小明", score: 90)
u.score = 95 // struct 要用 var 才能修改
閉包(Closure)一定會碰到,排序、callback 到處都是:
let numbers = [3, 1, 4, 1, 5]
let sorted = numbers.sorted { $0 < $1 }
// 或更簡寫:numbers.sorted(by: <)
只要你的 App 要連網,async / await 就躲不掉,iOS 15 之後寫非同步基本都靠它:
func fetchData() async throws -> String {
let url = URL(string: "https://api.example.com/data")!
let (data, _) = try await URLSession.shared.data(from: url)
return String(data: data, encoding: .utf8) ?? ""
}
最後是 protocol 跟 extension,一開始覺得抽象,但這是 Swift 整個生態的骨架:
protocol Greetable {
var greeting: String { get }
}
extension String: Greetable {
var greeting: String { "Hello, \(self)!" }
}
"iLab".greeting // "Hello, iLab!"
@escaping 閉包搭配 weak self。非同步 callback 裡記得寫 { [weak self] in … },可以避免循環強引用(retain cycle)。這種記憶體問題平常不一定發作,但很容易在上架審核或長時間使用時露餡,早點養成習慣比較省事。語法有個底了,就可以開真的專案:Xcode → File → New → Project → iOS → App,Interface 記得選 SwiftUI,Language 選 Swift。
SwiftUI 跟很多人以前學的 UI 寫法不一樣,它是「宣告式」的——你不是一步步指揮畫面怎麼動,而是描述它「現在應該長什麼樣」,剩下系統幫你算。只要 @State 的值一變,SwiftUI 就自動把受影響的部分重畫。一個最小可動的例子長這樣:
import SwiftUI
struct ContentView: View {
@State private var count = 0
var body: some View {
VStack(spacing: 20) {
Text("目前次數:\(count)")
.font(.title)
Button("點我 +1") {
count += 1
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
寫的時候不用每次都跑 Simulator,右側的 Preview Canvas 按 ▶ 就能即時看到結果,改畫面的效率差很多。
排版會反覆用到的就那幾個元件,先記住這張對照表就夠應付大半情況:
| 元件 | 用途 |
|---|---|
VStack | 垂直排列子 View |
HStack | 水平排列子 View |
ZStack | 圖層疊加(做背景/遮罩) |
List | 可捲動列表,自動支援 swipe-to-delete |
NavigationStack | iOS 16+ 推薦,取代舊版 NavigationView |
到這步畫面會動了,但東西都是寫死的。第三階段要做的,就是讓 App 從網路抓資料、把資料存在本機、還能在好幾個畫面之間共用同一份狀態。
共用狀態這塊,iOS 17 / Swift 5.9 之後我直接推新的 @Observable macro,比舊的 ObservableObject 乾淨太多,少寫一堆 @Published:
import Observation
@Observable
class AppViewModel {
var items: [String] = []
var isLoading = false
func loadItems() async {
isLoading = true
// 模擬網路請求
try? await Task.sleep(for: .seconds(1))
items = ["iPhone 16", "MacBook Air", "iPad Pro"]
isLoading = false
}
}
接到 View 裡,搭 .task 在畫面出現時自動載入,載入中順手疊一個轉圈:
struct ItemListView: View {
@State private var vm = AppViewModel()
var body: some View {
List(vm.items, id: \.self) { item in
Text(item)
}
.task {
await vm.loadItems()
}
.overlay {
if vm.isLoading { ProgressView() }
}
}
}
存資料的地方,新手常糾結要用哪個。我的判斷很簡單:UserDefaults 拿來存設定值就好,布林、字串、數字這種小東西,別塞大型物件進去。要存有結構的資料、要查詢,就用 SwiftData(iOS 17 起)——這是 Apple 拿來接 Core Data 棒子的新框架,用 macro 宣告模型就行,比起當年寫 Core Data 那一套,門檻低非常多:
import SwiftData
@Model
class Note {
var title: String
var content: String
var createdAt: Date
init(title: String, content: String) {
self.title = title
self.content = content
self.createdAt = .now
}
}
.modelContainer(for: Note.self, cloudKitDatabase: .automatic),就把 CloudKit 備份開起來了,幾乎零成本讓用戶換手機資料還在。這是不少台灣 indie dev 上架前忘了加的隱藏加分項,加了體驗差很多。到了真要上架,第一件事是加入 Apple Developer Program,一年 99 美金。路徑是前往 developer.apple.com → 登入 Apple ID → Enroll → 選 Individual 或 Organization → 完成付款。台灣用戶刷信用卡,一般當天就審核過了,不用等太久。
帳號好了,回 Xcode 設定簽名。專案設定裡盯緊這三欄就對了:Bundle Identifier 建議用 tw.ilab.yourappname 這種格式,全球不能重複;Team 選你剛加入的 Developer Program 帳號;Signing Certificate 選 Automatic,讓 Xcode 自己管憑證,新手別手動搞這塊,很容易卡住。想確認本機憑證狀況可以順手查一下:
# 確認本機已有的憑證(可選)
security find-identity -v -p codesigning
送審前有幾樣東西一定要備齊,缺一個就會被打回票。App Icon 所有尺寸要到位,Xcode 16 現在丟一張 1024×1024 就能自動產生其他尺寸。PrivacyInfo.xcprivacy 這個隱私資訊清單從 iOS 17.4 起是強制的,要宣告你用到哪些 API、用途是什麼,沒填直接擋。正式送審前先走 TestFlight,找幾個 beta 用戶實測,把 Crashes 壓到歸零再送,免得審到一半被退。截圖部分,iPhone 6.9 吋(也就是 iPhone 16 Pro Max 的尺寸)那組是必填的。
最後送審:Xcode → Product → Archive → 點 Distribute App → App Store Connect,上傳完再回 App Store Connect 把 metadata 填一填,按下送審就完成了。
還沒付費加入 Developer Program,能先在自己手機上跑嗎?
可以。免費的 Apple ID 就能把 App 側載到最多 3 台設備上,缺點是每 7 天要重新簽一次,而且 Push Notification、In-App Purchase 這些進階功能用不了。但要測基本 UI 跟邏輯,這樣完全夠,等真的確定要上架再花那 99 美金也不遲。做法是 Xcode → Signing & Capabilities → Team 選「Personal Team(你的 Apple ID)」,手機接上去直接 Run。
2026 年了,SwiftUI 跟 UIKit 先學哪個?
新專案就直接學 SwiftUI,不用猶豫。Apple 從 iOS 14 之後幾乎把新功能全押在 SwiftUI 上——Charts、MapKit、StoreKit 2、TipKit 一路加進來,UIKit 這邊的新 API 基本停在原地。會需要碰 UIKit 的場合只剩兩種:把某個舊元件包進 SwiftUI(用 UIViewRepresentable),或是接手維護別人留下的舊專案。以台灣現在的 iOS 職缺來看,純 SwiftUI 的經驗大多也吃得開。
把這條路走完一輪,你手上就會有一個真正能用、也真的上架過的 App,那種成就感跟看教學影片完全是兩回事。如果你連第一步那台 Mac 還沒決定——學 iOS 開發到底該配哪台、記憶體要不要往上加——可以先用我們的 選機小幫手 抓個方向,或直接來 iLab 門市坐下來聊,把你想做的 App 講給我們聽,我們幫你配到剛好夠用、不浪費。