被领导逼着学iOS之后,我靠AI一周上手了Swift
早上8点,咖啡刚泡好,我熟练地打开Cursor,准备开始今天的工作。
窗外是小区里遛狗的大爷,屋里是我和屏幕里那个永远不知疲倦的AI搭档。远程办公第三年了,我已经完全离不开这种节奏——不用挤地铁,不用应付无效会议,8点准时坐在书桌前,打开电脑,Tab一按,代码就来。
说实话,写这篇文章的时候我心情挺复杂的。
三个月前,我还是一个坚定的"前端+后端通吃"的全栈选手,iOS开发对我来说就是个黑洞。什么Swift、Xcode、CocoaPods,听着就头大。但架不住领导一句话:"咱们App要重构,你iOS也搞一搞。"得,得嘞,搞就搞呗。
当时我的内心OS是:我连OC都没写过,你让我搞iOS?但转念一想,现在都2024年了,有AI怕啥?
先说说我为什么选Cursor而不是别的
在聊Swift之前,得先交代一下我的工具链。
作为一个重度AI编程用户,我试过不少工具。GitHub Copilot用过,体验嘛,只能说"能用";ChatGPT写代码也行,但来回复制粘贴太累了,打断心流;后来发现了Cursor,直接起飞。
但最近我又发现了一个新玩意儿——Windsurf。说实话,刚听说这名字的时候我还以为是某个冲浪品牌(笑)。用了一段时间之后,我的评价是:它在AI提效这块确实有自己的独到之处,特别是 Cascade 那个多步推理的能力,处理复杂重构的时候特别爽。
不过日常写代码,我还是更习惯Cursor。为啥?因为快,因为稳,因为Cmd+K一敲,它就知道我要干嘛。
扯远了,回到正题。
学Swift的第一天:变量和常量
领导给了我两周时间"熟悉iOS开发",两周!两周能熟悉个啥?但没关系,我有AI。
打开Cursor,新建一个Swift文件,我直接开问:
// 我让AI给我解释最基础的概念
let name = "张三" // 常量,不能改
var age = 28 // 变量,可以改
就这么两行代码,AI给我解释了半天let和var的区别。说实话,这玩意儿跟JavaScript的const和let差不多,前端转过来的同学应该秒懂。
但有个坑我得提一下——Swift的类型推断。
let price = 9.9 // 自动推断为 Double
let count = 10 // 自动推断为 Int
let greeting = "你好" // 自动推断为 String
JavaScript写多了,总觉得这玩意儿有点"多此一举"——你不声明类型,它怎么知道是啥?后来才明白,Swift是强类型语言,编译期就给你卡死了,不像JS那样运行时才炸。
这点其实对性能优化特别友好。 类型确定了,编译器就能做更多的优化。我之前做过一个列表滚动的性能优化,就是因为类型明确,编译器自动内联了一些操作,帧率直接从45fps拉到了58fps。当然这是后话了。
Optionals:让我怀疑人生的东西
如果说Swift有什么东西让我第一天就想砸电脑,那一定是Optional。
var nickname: String? = nil
这个?到底是个啥?
我当时在Cursor里敲了一大段话问AI,它给我解释了半天"Optional本质上是一个枚举"。我:???
后来我自己琢磨明白了,简单说就是:Swift不让你偷偷摸摸用可能为空的值。 你得显式地告诉编译器"我知道这可能为空,我会处理的"。
// 错误写法,编译都过不了
let length = nickname.count // ❌ 编译报错
// 正确写法1:强制解包(不推荐,可能crash)
let length1 = nickname!.count // ⚠️ 如果nickname是nil就炸了
// 正确写法2:if let 安全解包(推荐)
if let name = nickname {
print("昵称是:\(name)")
} else {
print("没有设置昵称")
}
// 正确写法3:guard let(更优雅)
func printNickname() {
guard let name = nickname else {
print("没有昵称")
return
}
print("昵称是:\(name)")
}
说实话,刚开始用guard let的时候觉得语法好丑,但用多了之后发现——真香。这种"提前处理异常情况"的写法,比嵌套一堆if-else清爽太多了。
而且从AI提效的角度来说,Optional机制让AI生成的代码更安全。你让Cursor帮你写一段处理网络响应的代码,它自动就会加上Optional处理,不会给你留个nil crash的隐患。这在AI办公场景下特别重要——毕竟你不可能每行AI生成的代码都仔细review对吧(虽然理论上应该)。
函数和闭包:跟JS殊途同归
Swift的函数写法:
func greet(name: String, times: Int = 1) -> String {
var result = ""
for _ in 0..<times {
result += "你好,\(name)!\n"
}
return result
}
// 调用
print(greet(name: "老王", times: 3))
注意看那个name: String,参数名和类型都要写。刚接触的时候觉得好啰嗦,但后来发现这样写代码可读性确实高——你不用猜这个参数是啥意思。
闭包这块,Swift和JS的箭头函数很像:
// Swift闭包
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
// 结果: [2, 4, 6, 8, 10]
// 对比JS
// const doubled = numbers.map(n => n * 2)
$0、$1这种简写,第一次见的时候我是一脸懵逼的。但用Cursor写代码的时候,AI经常给我生成这种写法,看多了就习惯了。
说到这儿,不得不提一个AI办公的小技巧:你在Cursor里写代码的时候,如果不确定某个API怎么用,直接写个注释描述你想要的效果,然后Tab补全,它大概率能给你写对。
// 我想要一个函数,接收一个整数数组,返回其中所有偶数的平方
// 然后我按下Tab...
func evenSquares(_ numbers: [Int]) -> [Int] {
numbers.filter { $0 % 2 == 0 }.map { $0 * $0 }
}
一次就对,连调试都省了。这就是AI提效的魅力。
结构体和类:值类型 vs 引用类型
这块是Swift跟其他语言最大的区别之一。
// 结构体(值类型)
struct Point {
var x: Double
var y: Double
}
var p1 = Point(x: 1.0, y: 2.0)
var p2 = p1 // 拷贝了一份
p2.x = 3.0
print(p1.x) // 还是1.0,没变
// 类(引用类型)
class User {
var name: String
init(name: String) {
self.name = name
}
}
let user1 = User(name: "张三")
let user2 = user1 // 引用同一个对象
user2.name = "李四"
print(user1.name) // 变成"李四"了!
值类型和引用类型的区别,在做性能优化的时候特别关键。
我之前遇到一个列表卡顿的问题,排查了半天发现是因为数据模型用了class,每次更新都触发了一堆引用计数操作。后来改成struct,配合Swift的值语义,编译器自动做了copy-on-write优化,性能直接起飞。
这段经历让我对Swift的类型系统有了更深理解——选对类型,性能问题就解决了一半。
枚举:比你想的强大得多
Swift的枚举不是C语言那种简单的整数常量,它是个狠角色:
enum NetworkResult {
case success(data: Data, statusCode: Int)
case failure(error: Error)
var isSuccess: Bool {
switch self {
case .success: return true
case .failure: return false
}
}
}
// 使用
func handleResult(_ result: NetworkResult) {
switch result {
case .success(let data, let statusCode):
print("成功,状态码:\(statusCode),数据大小:\(data.count)")
case .failure(let error):
print("失败:\(error.localizedDescription)")
}
}
关联值这个特性,在处理网络请求结果的时候特别好用。你不需要定义一堆额外的类来包装不同的返回情况,一个枚举就搞定了。
而且Swift要求switch必须穷举所有case,漏了编译就报错。这意味着你不可能忘记处理某种情况——这对代码健壮性的提升是巨大的。
协议和扩展:Swift的"接口"和"Mixin"
协议(Protocol)类似于其他语言的接口:
protocol Drawable {
func draw()
var color: String { get }
}
struct Circle: Drawable {
var radius: Double
var color: String = "红色"
func draw() {
print("画一个\(color)的圆,半径\(radius)")
}
}
扩展(Extension)则可以在不修改原始代码的情况下给类型添加新功能:
extension String {
var isEmail: Bool {
// 简单的邮箱验证
contains("@") && contains(".")
}
func repeated(_ times: Int) -> String {
(0..<times).map { _ in self }.joined()
}
}
"hello".repeated(3) // "hellohellohello"
"test@example.com".isEmail // true
这两个特性组合起来,就是Swift面向协议编程(Protocol-Oriented Programming)的基础。说实话,刚开始学的时候觉得这玩意儿有点过度设计,但后来写复杂业务的时候发现——真好用。特别是做组件化的时候,用协议定义组件的行为规范,用扩展提供默认实现,代码结构特别清晰。
错误处理:别再用try-catch了
Swift的错误处理方式跟大多数语言都不一样:
enum AppError: Error {
case networkFailed
case invalidData
case unauthorized
}
func fetchData(from url: String) throws -> Data {
guard !url.isEmpty else {
throw AppError.invalidData
}
// 模拟网络请求...
return Data()
}
// 使用do-catch处理
do {
let data = try fetchData(from: "https://api.example.com")
print("获取数据成功,大小:\(data.count)")
} catch AppError.networkFailed {
print("网络连接失败")
} catch AppError.invalidData {
print("数据无效")
} catch {
print("未知错误:\(error)")
}
// 或者用try?忽略错误
let data = try? fetchData(from: "https://api.example.com")
// data的类型是 Data?,如果出错就是nil
这种设计强迫你显式处理错误,不会像Java那样一个unchecked exception就把程序搞崩。而且throws关键字写在函数签名上,一眼就知道这函数可能出错,代码可读性拉满。
实战:用AI写一个完整的网络请求封装
说了这么多基础概念,来个实战的。
上周产品经理突然加了个需求,要对接一个新的后端API。我直接在Cursor里写了一段注释:
// 我需要封装一个通用的网络请求工具
// 支持GET和POST
// 能自动解析JSON
// 有完善的错误处理
// 支持请求取消
然后让AI帮我生成代码,它给了我一版:
import Foundation
enum HTTPMethod: String {
case GET, POST, PUT, DELETE
}
enum APIError: Error, LocalizedError {
case invalidURL
case requestFailed(statusCode: Int)
case decodingFailed
case cancelled
var errorDescription: String? {
switch self {
case .invalidURL: return "无效的URL"
case .requestFailed(let code): return "请求失败,状态码:\(code)"
case .decodingFailed: return "数据解析失败"
case .cancelled: return "请求已取消"
}
}
}
class APIClient {
private let session: URLSession
private let decoder: JSONDecoder
init(session: URLSession = .shared) {
self.session = session
self.decoder = JSONDecoder()
self.decoder.keyDecodingStrategy = .convertFromSnakeCase
}
func request<T: Decodable>(
url: String,
method: HTTPMethod = .GET,
body: [String: Any]? = nil
) async throws -> T {
guard let url = URL(string: url) else {
throw APIError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = method.rawValue
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
if let body = body {
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
}
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw APIError.requestFailed(statusCode: -1)
}
guard (200...299).contains(httpResponse.statusCode) else {
throw APIError.requestFailed(statusCode: httpResponse.statusCode)
}
do {
return try decoder.decode(T.self, from: data)
} catch {
throw APIError.decodingFailed
}
}
}
// 使用示例
struct UserResponse: Codable {
let id: Int
let name: String
let email: String
}
// 在ViewModel中调用
func fetchUser() async {
let client = APIClient()
do {
let user: UserResponse = try await client.request(
url: "https://api.example.com/users/1"
)
print("用户:\(user.name)")
} catch {
print("请求失败:\(error.localizedDescription)")
}
}
代码质量相当可以,async/await、泛型、错误处理都考虑到了。我只需要微调一下加上token刷新逻辑就完事了。
这就是AI提效的真实体验——不是让AI替你思考架构,而是让它帮你把想法快速变成可运行的代码。你负责想清楚"要做什么",AI负责"怎么做"。
一些学习建议
最后分享几点我学Swift的心得,给同样想入门iOS的同学:
| 学习阶段 | 建议 | 耗时 |
|---|---|---|
| 语法基础 | 用AI辅助理解概念,多写小demo | 3-5天 |
| UIKit/SwiftUI | 先学SwiftUI,更现代更简单 | 1周 |
| 网络和数据 | 结合实战项目练手 | 1周 |
| 性能优化 | 用Instruments分析,关注内存和帧率 | 持续学习 |
几个关键点:
别死磕语法细节。 Swift语法糖很多,你不可能一次全记住。用Cursor写代码的时候,不确定就Tab补全,AI会帮你搞定。
尽早接触SwiftUI。 虽然UIKit是基础,但SwiftUI的开发效率高太多了。声明式UI + 实时预览,写起来特别爽。
重视Optional。 这是Swift的灵魂。理解了Optional,你就理解了Swift一半的设计哲学。
用AI加速学习,但别依赖AI。 你可以让AI解释代码,但一定要自己动手敲一遍。肌肉记忆这东西,光看是没用的。
写完这篇文章的时候,已经下午两点了。看了眼Cursor的统计,今天已经生成了2000多行代码。
远程办公的好处就是,累了可以去阳台抽根烟,看看远处的山。回来继续干。
iOS开发这条路还很长,但至少现在我不再恐惧了。有AI加持,学什么都快。Windsurf也好,Cursor也罢,工具在进化,我们也在进化。
不说了,产品经理又在钉钉上@我了,说是要改个按钮颜色。
呵,改颜色。


评论 0