iOS开发入门:Swift基础与国企程序员的实战碎碎念
上周五晚上,我正躺在沙发上刷LeetCode准备跳槽面试题,突然收到领导微信:“小张啊,咱们内部用的那个设备巡检App,最近用户老说卡顿,你抽空看看能不能优化一下?顺便,听说你要学iOS?正好上手试试。”
我嘴角一抽——合着“抽空”是让我双休日加班的意思?好在我们这国企还算人性化,真不强制加班。但转念一想,反正闲着也是刷题,不如真搞个iOS项目练练手。毕竟简历上写“熟悉移动端开发”总比光写Java后端有竞争力。
于是,我火速翻出尘封已久的MacBook(感谢公司发的开发机),打开Xcode,准备从零开始搞个简化版的巡检App。本文就记录下这个过程中的开发心得,以及一个国企程序员如何靠Windsurf、Gemini和一点点倔强,在Swift世界里摸爬滚打的故事。
为什么选Swift?因为别无选择(也不是)
其实我本来想用React Native搞跨端,但领导一句“原生体验好”直接给我拍板了。得,那就Swift吧。说实话,之前只在学校里写过几行Objective-C,看到@autoreleasepool就头大。Swift倒是听人吹了多年,说是“安全、现代、快”,但到底咋样?
上手第一天,我就被它的类型推断和可选类型(Optional)整懵了。比如这段代码:
var name: String?
print(name!) // 崩溃!fatal error: unexpectedly found nil while unwrapping an Optional value
当时真的想砸电脑。但冷静下来一想:这不就是Java里的NullPointerException提前报错吗?只不过Swift逼你在编译期就处理,而不是等到线上炸了才哭。这设计,其实挺负责任的。
从“Hello World”到能跑的项目:我的第一周踩坑实录
环境搭建:别信教程,信Xcode
网上一堆教程说要装Command Line Tools、CocoaPods、Homebrew……结果我打开Xcode,新建项目 → 选iOS App → 点Run,模拟器直接跑起来了。原来Xcode已经把90%的环境给你配好了。感动!
不过,有个坑:模拟器默认是iPhone 15 Pro Max,巨占内存。我这8G内存的MacBook Air直接风扇狂转。后来在Gemini里问:“怎么降低Xcode模拟器资源占用?” 它建议我改用iPhone SE(第3代)模拟器,果然流畅多了。
小技巧:在Xcode顶部菜单栏
Product → Destination里可以切换设备型号。
第一个功能:扫码录入设备ID
巡检App的核心功能之一是扫二维码录入设备信息。我原以为得集成第三方SDK,结果发现iOS 11+自带AVFoundation就能搞定扫码!代码如下:
import AVFoundation
class ScanViewController: UIViewController {
var captureSession: AVCaptureSession!
var videoPreviewLayer: AVCaptureVideoPreviewLayer!
override func viewDidLoad() {
super.viewDidLoad()
captureSession = AVCaptureSession()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
let videoInput: AVCaptureDeviceInput
do {
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
} catch {
return
}
if (captureStream?.canAddInput(videoInput))! {
captureSession.addInput(videoInput)
} else {
// 处理错误
return
}
let metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.qr]
} else {
return
}
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer.frame = view.layer.bounds
videoPreviewLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(videoPreviewLayer)
captureSession.startRunning()
}
}
extension ScanViewController: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
captureSession.stopRunning()
// 回传扫码结果
DispatchQueue.main.async {
self.dismiss(animated: true) {
// 这里回调给上一个页面
}
}
}
}
}
这段代码看似简单,但有几个血泪教训:
- 必须在Info.plist里加权限描述,否则直接闪退:
<key>NSCameraUsageDescription</key> <string>需要访问相机以扫描设备二维码</string> - 扫码成功后要手动停止会话,否则后台持续耗电。
- 别忘了震动反馈,用户体验提升50%!
开发工具链:ChatGPT + Windsurf + Gemini = 我的外挂大脑
作为一个重度依赖AI辅助开发的程序员,我日常三件套:
- ChatGPT:写复杂逻辑、解释概念
- Windsurf(基于Claude的IDE插件):实时代码补全、重构建议
- Gemini:查文档、对比方案、生成测试用例
比如我在写TableView时,不确定cellForRowAt的最佳实践,直接在Windsurf里敲注释:
// Windsurf提示:避免在cellForRowAt中做耗时操作,用预加载模型
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DeviceCell", for: indexPath)
let device = devices[indexPath.row]
// ✅ 正确:直接赋值,不解析JSON或网络请求
cell.textLabel?.text = device.name
cell.detailTextLabel?.text = device.id
return cell
}
而当我纠结“该用Storyboard还是纯代码布局”时,Gemini给了我一张对比表:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Storyboard | 可视化拖拽,快速原型 | 冲突多,难协作,性能略低 | 小型项目、内部工具 |
| SwiftUI | 声明式,跨平台潜力大 | iOS 13+,学习曲线陡 | 新项目、追求现代化 |
| 纯代码 (UIKit) | 灵活、可控、易版本管理 | 代码量大 | 复杂交互、长期维护 |
我们这内部App用户就几十人,果断选Storyboard!省时间就是省生命,毕竟我还得刷题呢。
性能优化:国企项目也不能太摆烂
虽然只是内部工具,但用户抱怨“卡”,那肯定得治。我用了Xcode自带的Instruments工具分析,发现两个问题:
- TableView滚动卡顿:因为每次
cellForRowAt都重新创建UILabel - 内存峰值过高:图片没压缩,直接加载原图
解决方案:
- 复用Cell:确保Identifier一致,用
dequeueReusableCell - 异步加载图片:用
DispatchQueue.global().async+ 缓存 - 限制图片尺寸:用
UIImage.resize(to:)压缩
extension UIImage {
func resize(to size: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
draw(in: CGRect(origin: .zero, size: size))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resizedImage ?? self
}
}
优化后,帧率从45 FPS稳到60 FPS,内存占用降了40%。用户群里终于没人吐槽了,领导还夸我“效率高”。(其实都是AI帮的忙,嘘——)
发布上架?先过TestFlight这关
虽然只是内部分发,但走App Store太麻烦,我们用的是企业证书 + TestFlight。流程如下:
- 在Apple Developer注册App ID
- 生成Provisioning Profile
- Xcode Archive → Distribute to TestFlight
- 邀请内部测试员(用邮箱)
但第一次上传失败,报错:
ITMS-90725: SDK Version Issue - This app was built with the iOS 17.0 SDK.
查了才知道,TestFlight要求用正式版SDK构建,不能用beta版Xcode。赶紧切回Xcode 15.0正式版,重新Archive,搞定。
经验:内部App也建议走TestFlight,比直接发IPA稳定多了,还能收集崩溃日志。
最后:一个国企程序员的真实心态
说实话,在国企写代码,很多时候是“能跑就行”。但这次重拾iOS,让我找回了写代码的乐趣——不是为了应付KPI,而是真想做出点东西。
而且,准备跳槽的压力反而成了学习动力。每天刷两道算法题,再捣鼓一小时Swift,感觉技术栈又厚了一层。说不定下次面试,我就能自信地说:“我做过完整的iOS项目,从扫码到上架,全流程闭环。”
当然,也感谢那些AI助手们。没有它们,我可能还在和Optional搏斗。但记住:AI是桨,你是船长。别让它替你思考,而是帮你加速。
如果你也在国企,想学新技术但怕没机会——自己造个项目。哪怕只是个待办清单,只要跑起来,就是进步。
共勉。
关键词回顾:
- 开发心得:安全优先、用户体验、工具提效
- Windsurf:实时代码辅助,重构神器
- 项目:内部设备巡检App,从0到TestFlight
- Gemini:方案对比、文档查询、测试生成
(全文约2860字,刚好够我下周技术分享用 😎)

评论 0