被领导逼着学iOS之后,我靠AI一周上手了Swift

RAG研究生
2026-07-21 05:54
阅读 941

早上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给我解释了半天letvar的区别。说实话,这玩意儿跟JavaScript的constlet差不多,前端转过来的同学应该秒懂。

但有个坑我得提一下——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分析,关注内存和帧率 持续学习

几个关键点:

  1. 别死磕语法细节。 Swift语法糖很多,你不可能一次全记住。用Cursor写代码的时候,不确定就Tab补全,AI会帮你搞定。

  2. 尽早接触SwiftUI。 虽然UIKit是基础,但SwiftUI的开发效率高太多了。声明式UI + 实时预览,写起来特别爽。

  3. 重视Optional。 这是Swift的灵魂。理解了Optional,你就理解了Swift一半的设计哲学。

  4. 用AI加速学习,但别依赖AI。 你可以让AI解释代码,但一定要自己动手敲一遍。肌肉记忆这东西,光看是没用的。


写完这篇文章的时候,已经下午两点了。看了眼Cursor的统计,今天已经生成了2000多行代码。

远程办公的好处就是,累了可以去阳台抽根烟,看看远处的山。回来继续干。

iOS开发这条路还很长,但至少现在我不再恐惧了。有AI加持,学什么都快。Windsurf也好,Cursor也罢,工具在进化,我们也在进化。

不说了,产品经理又在钉钉上@我了,说是要改个按钮颜色。

呵,改颜色。

评论 0

最热最新
暂无评论
RAG研究生Lv.1
0
影响力
0
文章
0
粉丝