Skip to content

iOS

https://developer.apple.com.cn/ios/

核心特性

系统架构

iOS 系统架构分为四个层次,从底层到顶层依次为:

┌─────────────────────────────────────────────────┐
│        Cocoa Touch 层                          │
│   UIKit、MapKit、MessageUI、PhotoUI 等         │
├─────────────────────────────────────────────────┤
│         媒体层 (Media)                         │
│   Core Graphics、Core Animation、              │
│   AVFoundation、Core Audio 等                  │
├─────────────────────────────────────────────────┤
│       核心服务层 (Core Services)               │
│   Core Data、Core Location、                  │
│   Foundation、Security 等                     │
├─────────────────────────────────────────────────┤
│        核心操作系统层 (Core OS)                │
│   Mach、BSD、System Configuration、            │
│   Security、Keychain 等                       │
└─────────────────────────────────────────────────┘

主要特性

  • 安全性:沙盒机制、应用签名、数据加密
  • 流畅体验:Metal 图形 API、流畅的动画效果
  • 生态系统:与 macOS、iPadOS、watchOS、tvOS 无缝协作
  • 应用商店:App Store 拥有数百万款审核通过的应用
  • 多任务处理:支持后台应用运行和切换
  • 硬件集成:深度集成苹果芯片、摄像头、传感器等

版本演进

版本发布时间主要特性
iOS 182024Apple 智能(AI)、控制中心自定义、密码应用
iOS 172023StandBy 待机模式、交互式小组件、FaceTime 留言
iOS 162022锁屏自定义、实时活动、Swift Charts
iOS 152021Focus 模式、SharePlay、FaceTime 空间音频
iOS 142020App 资源库、小组件、画中画
iOS 132019暗黑模式、Sign in with Apple、SwiftUI

系统配置与管理

开发者选项

bash
# 通过 Xcode 管理设备
# Window -> Devices and Simulators

# 启用开发者功能
# 设置 -> 隐私与安全性 -> 开发者模式

# 查看设备日志
# Xcode -> Window -> Devices and Simulators -> View Device Logs

应用管理

bash
# 通过 Xcode 安装应用
# 1. 连接设备
# 2. 选择目标设备
# 3. 点击 Run 按钮

# 通过命令行安装(需要 Xcode 命令行工具)
xcodebuild -install -project MyApp.xcodeproj -scheme MyApp -destination 'platform=iOS,name=iPhone' CONFIGURATION_BUILD_DIR=./build

# 查看已安装应用
# Xcode -> Window -> Devices and Simulators -> Installed Apps

# 导出应用
# Xcode -> Product -> Archive -> Export

权限管理

swift
// 在 Info.plist 中配置权限
// Info.plist
<key>NSCameraUsageDescription</key>
<string>需要访问相机以拍摄照片</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册以选择照片</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要获取位置以提供服务</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要访问麦克风以录制音频</string>

// 在运行时请求权限
import PhotosUI

// 相机权限
func requestCameraPermission() {
    AVCaptureDevice.requestAccess(for: .video) { granted in
        if granted {
            // 使用相机
        }
    }
}

// 位置权限
func requestLocationPermission() {
    locationManager.requestWhenInUseAuthorization()
}

// 相册权限
func requestPhotoPermission() {
    PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
        // 处理权限状态
    }
}

命令行工具

Xcode 命令行工具

bash
# 安装命令行工具
xcode-select --install

# 查看已安装版本
xcode-select --version

# 选择 Xcode 版本
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

# 查看可用的模拟器
xcrun simctl list devices available

# 启动模拟器
xcrun simctl boot "iPhone 15"

# 列出可用的 SDK
xcodebuild -showsdks

# 获取设备信息
xcrun simctl list devices
xcrun instruments -s devices

Xcodebuild 常用命令

bash
# 构建项目
xcodebuild -project MyApp.xcodeproj \
    -scheme MyApp \
    -destination 'platform=iOS Simulator,name=iPhone 15' \
    build

# 构建 Archive
xcodebuild -project MyApp.xcodeproj \
    -scheme MyApp \
    -archivePath build/MyApp \
    archive

# 导出 IPA
xcodebuild -exportArchive \
    -archivePath build/MyApp.xcarchive \
    -exportPath ./output \
    -exportOptionsPlist ExportOptions.plist

# 运行测试
xcodebuild test \
    -project MyApp.xcodeproj \
    -scheme MyApp \
    -destination 'platform=iOS Simulator,name=iPhone 15'

# 代码分析
xcodebuild analyze \
    -project MyApp.xcodeproj \
    -scheme MyApp

# 清理构建
xcodebuild clean \
    -project MyApp.xcodeproj \
    -scheme MyApp

Swift Package Manager

bash
# 初始化 Package
swift package init

# 解析依赖
swift package resolve

# 更新依赖
swift package update

# 构建 Package
swift build

# 运行测试
swift test

# 生成 Xcode 项目
swift package generate-xcodeproj

# 列出可用的 Package
swift package search Alamofire

# 获取 Package 信息
swift package show-dependencies

其他常用命令

bash
# 截屏
xcrun simctl io booted screenshot screenshot.png

# 录制屏幕
xcrun simctl io booted recordVideo screenRecording.mov

# 安装应用到模拟器
xcrun simctl install booted app.app

# 启动应用
xcrun simctl launch booted com.example.app

# 列出正在运行的进程
xcrun simctl list_processes booted

# 发送模拟操作
xcrun simctl bootstatus booted -b  # 模拟按下电源键

# 重置模拟器
xcrun simctl erase all

开发环境配置

Xcode 安装与配置

bash
# 通过 App Store 安装
# 或从 Apple Developer 网站下载

# 验证安装
xcodebuild -version
# 输出:Xcode 15.0 Build version 15A240d

# 查看组件
xcodebuild -showsdks
# 输出:
# iOS SDK 17.0
# iOS Simulator 17.0
# macOS SDK 14.0

# 安装额外的模拟器
xcodebuild -downloadPlatform iOS

# 配置主题和快捷键
# Preferences -> Themes
# Preferences -> Key Bindings

Swift 环境

bash
# 查看 Swift 版本
swift --version
# 输出:Swift version 5.9

# 启动 Swift REPL
swift

# 编译 Swift 文件
swiftc -o hello hello.swift

# 运行脚本
swift hello.swift

# 使用 Swift Package Manager
swift package init
swift package update
swift build

模拟器管理

bash
# 列出所有模拟器
xcrun simctl list devices all

# 创建自定义模拟器
xcrutl simctl create "My iPhone" "iPhone 15" "com.apple.CoreSimulator.SimRuntime.iOS-17-0"

# 删除模拟器
xcrun simctl delete "My iPhone"

# 启动模拟器
open -a Simulator

# 模拟器快捷键
# Command + 1/2/3/4  调整窗口大小
# Command + S        截屏
# Command + R        旋转设备
# Command + Shift + H  返回主屏幕
# Command + Shift + M  模拟主屏幕按钮

环境变量配置

bash
# 在 ~/.zshrc 或 ~/.bash_profile 中配置

# 设置 Xcode 默认版本
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer

# 设置 iOS 模拟器路径
export SIMULATOR_HOME=~/Library/Developer/CoreSimulator/Devices

# 设置 Swift Package 镜像
export SWIFT_BINARY_RE=https://example.com/swift-pm
export SWIFT_FORMAT_BINARY_RE=https://example.com/swift-format

# 设置代码签名身份
export CODE_SIGN_IDENTITY="Apple Development"
export CODE_SIGN_STYLE=Automatic

# 设置Provisioning Profile路径
export PROVISIONING_PROFILE=~/Library/MobileDevice/Provisioning\ Profiles

开发最佳实践

项目结构推荐

swift
// 推荐的目录结构
MyApp/
├── MyApp/
   ├── App/
   ├── MyAppApp.swift
   └── AppDelegate.swift
   ├── Core/
   ├── Networking/
   ├── Database/
   └── Utilities/
   ├── Features/
   ├── Home/
   ├── Profile/
   └── Settings/
   ├── Models/
   ├── Views/
   ├── ViewModels/
   └── Resources/
       ├── Assets.xcassets
       └── Localizable.strings
├── MyAppTests/
├── MyAppUITests/
├── Pods/                # 如果使用 CocoaPods
├── project.yml          # XcodeGen 配置
├── Podfile              # CocoaPods 配置
├── Podfile.lock
├── setup.sh             # 环境设置脚本
└── MyApp.xcworkspace/   # Xcode 工作区

Swift 代码规范

swift
// 1. 使用访问控制
public struct User: Identifiable, Codable {
    public let id: UUID
    public var name: String
    public var email: String

    public init(id: UUID, name: String, email: String) {
        self.id = id
        self.name = name
        self.email = email
    }
}

// 2. 使用协议扩展
protocol NetworkServiceProtocol {
    func fetchData() async throws -> Data
}

extension NetworkServiceProtocol {
    func fetchData() async throws -> Data {
        // 默认实现
    }
}

// 3. 使用 Property Wrappers
@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T

    var wrappedValue: T {
        get {
            UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }

// 4. 使用 Result Builder
@resultBuilder
struct ViewBuilder {
    static func buildBlock(_ components: View...) -> some View {
        VStack(alignment: .leading) {
            ForEach(Array(components.enumerated()), id: \.offset) { index, component in
                component
                if index < components.count - 1 {
                    Divider()
                }
            }
        }
    }
}

性能优化技巧

swift
// 1. 使用 LazyView 延迟加载视图
struct LazyView<Content: View>: View {
    let build: () -> Content
    init(_ build: @escaping () -> Content) {
        self.build = build
    }
    var body: Content {
        build()
    }
}

// 使用方式
NavigationLink(destination: LazyView(DetailView())) {
    Text("Navigate")
}

// 2. 使用 AsyncImage 异步加载图片
AsyncImage(url: URL(string: "https://example.com/image.jpg")) { phase in
    switch phase {
    case .empty:
        ProgressView()
    case .success(let image):
        image.resizable()
    case .failure:
        Image(systemName: "photo")
    @unknown default:
        EmptyView()
    }
}

// 3. 使用 List 的惰性加载
List(0..<1000) { index in
    LazyView(Text("Row \(index)"))
}
.listStyle(.plain)

// 4. 使用 @MainActor 确保在主线程更新 UI
@MainActor
class ViewModel: ObservableObject {
    @Published var items: [Item] = []

    func loadData() async {
        items = await fetchItems()
    }
}

// 5. 使用 Task Group 并发处理
func fetchAllData() async throws -> [Data] {
    try await withThrowingTaskGroup(of: Data.self) { group in
        for url in urls {
            group.addTask {
                try await URLSession.shared.data(from: url).0
            }
        }
        var results: [Data] = []
        for try await data in group {
            results.append(data)
        }
        return results
    }
}

网络请求封装

swift
// NetworkService.swift
enum NetworkError: Error, LocalizedError {
    case invalidURL
    case noData
    case decodingError(Error)
    case serverError(Int)
    case unknown

    var errorDescription: String? {
        switch self {
        case .invalidURL:
            return "无效的 URL"
        case .noData:
            return "没有数据返回"
        case .decodingError(let error):
            return "解码错误:\(error.localizedDescription)"
        case .serverError(let code):
            return "服务器错误:\(code)"
        case .unknown:
            return "未知错误"
        }
    }
}

actor NetworkService {
    static let shared = NetworkService()
    private let session: URLSession
    private let decoder: JSONDecoder

    private init() {
        let config = URLSessionConfiguration.default
        config.timeoutIntervalForRequest = 30
        config.timeoutIntervalForResource = 60
        config.waitsForConnectivity = true
        self.session = URLSession(configuration: config)
        self.decoder = JSONDecoder()
        self.decoder.dateDecodingStrategy = .iso8601
    }

    func request<T: Decodable>(_ endpoint: APIEndpoint) async throws -> T {
        guard let url = endpoint.url else {
            throw NetworkError.invalidURL
        }

        let (data, response) = try await session.data(from: url)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw NetworkError.unknown
        }

        guard (200...299).contains(httpResponse.statusCode) else {
            throw NetworkError.serverError(httpResponse.statusCode)
        }

        do {
            return try decoder.decode(T.self, from: data)
        } catch {
            throw NetworkError.decodingError(error)
        }
    }
}

enum APIEndpoint {
    case users
    case user(id: Int)
    case posts
    case post(id: Int)

    var url: URL? {
        switch self {
        case .users:
            return URL(string: "https://api.example.com/users")
        case .user(let id):
            return URL(string: "https://api.example.com/users/\(id)")
        case .posts:
            return URL(string: "https://api.example.com/posts")
        case .post(let id):
            return URL(string: "https://api.example.com/posts/\(id)")
        }
    }
}

本地数据存储

swift
// 1. UserDefaults 封装
final class UserDefaultsManager {
    private let defaults: UserDefaults

    enum Keys {
        static let isLoggedIn = "isLoggedIn"
        static let userToken = "userToken"
        static let userPreferences = "userPreferences"
    }

    static let shared = UserDefaultsManager()

    private init() {
        if let suiteName = Bundle.main.infoDictionary?["AppGroupIdentifier"] as? String {
            self.defaults = UserDefaults(suiteName: suiteName) ?? .standard
        } else {
            self.defaults = .standard
        }
    }

    var isLoggedIn: Bool {
        get { defaults.bool(forKey: Keys.isLoggedIn) }
        set { defaults.set(newValue, forKey: Keys.isLoggedIn) }
    }

    var userToken: String? {
        get { defaults.string(forKey: Keys.userToken) }
        set { defaults.set(newValue, forKey: Keys.userToken) }
    }
}

// 2. Core Data 堆栈
final class CoreDataStack {
    static let shared = CoreDataStack()

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "MyApp")
        container.loadPersistentStores { _, error in
            if let error = error as NSError? {
                fatalError("无法加载持久化存储:\(error), \(error.userInfo)")
            }
        }
        container.viewContext.automaticallyMergesChangesFromParent = true
        return container
    }()

    var viewContext: NSManagedObjectContext {
        persistentContainer.viewContext
    }

    func saveContext() {
        let context = viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                print("保存上下文错误:\(nserror), \(nserror.userInfo)")
            }
        }
    }

    func newBackgroundContext() -> NSManagedObjectContext {
        persistentContainer.newBackgroundContext()
    }
}

// 3. SwiftData 示例(iOS 17+)
import SwiftData

@Model
final class TodoItem {
    var id: UUID
    var title: String
    var isCompleted: Bool
    var createdAt: Date
    var dueDate: Date?

    init(id: UUID = UUID(), title: String, isCompleted: Bool = false, createdAt: Date = Date(), dueDate: Date? = nil) {
        self.id = id
        self.title = title
        self.isCompleted = isCompleted
        self.createdAt = createdAt
        self.dueDate = dueDate
    }
}

依赖管理

swift
// 使用 Swift Package Manager 的 Package.swift
// swift-tools-version:5.9
import PackageDescription

let package = Package(
    name: "MyApp",
    platforms: [
        .iOS(.v15),
        .macOS(.v12)
    ],
    products: [
        .library(
            name: "MyApp",
            targets: ["MyApp"])
    ],
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0"),
        .package(url: "https://github.com/pointfreeco/swift-composable-architecture.git", from: "1.0.0"),
        .package(url: "https://github.com/realm/realm-swift.git", exact: "10.45.0")
    ],
    targets: [
        .target(
            name: "MyApp",
            dependencies: [
                "Alamofire",
                .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
                "RealmSwift"
            ]),
        .testTarget(
            name: "MyAppTests",
            dependencies: ["MyApp"])
    ]
)

测试实践

单元测试

swift
// Calculator.swift
struct Calculator {
    func add(_ a: Double, _ b: Double) -> Double {
        a + b
    }

    func divide(_ a: Double, by b: Double) throws -> Double {
        guard b != 0 else {
            throw CalculatorError.divisionByZero
        }
        return a / b
    }
}

enum CalculatorError: Error {
    case divisionByZero
}

// CalculatorTests.swift
import XCTest
@testable import MyApp

final class CalculatorTests: XCTestCase {
    var sut: Calculator!

    override func setUp() {
        super.setUp()
        sut = Calculator()
    }

    override func tearDown() {
        sut = nil
        super.tearDown()
    }

    func testAdd_twoPositiveNumbers_returnsCorrectSum() {
        // Given
        let a = 5.0
        let b = 3.0

        // When
        let result = sut.add(a, b)

        // Then
        XCTAssertEqual(result, 8.0)
    }

    func testDivide_byZero_throwsError() {
        // Given
        let a = 10.0
        let b = 0.0

        // When/Then
        XCTAssertThrowsError(try sut.divide(a, by: b)) { error in
            XCTAssertEqual(error as? CalculatorError, .divisionByZero)
        }
    }

    func testPerformanceExample() throws {
        measure {
            for _ in 0..<1000 {
                _ = sut.add(1, 2)
            }
        }
    }
}

UI 测试

swift
// MyAppUITests.swift
import XCTest

final class MyAppUITests: XCTestCase {
    var app: XCUIApplication!

    override func setUpWithError() throws {
        continueAfterFailure = false
        app = XCUIApplication()
        app.launch()
    }

    override func tearDownWithError() throws {
        app = nil
    }

    func testLoginFlow() throws {
        // 导航到登录页面
        app.navigationBars["Home"].buttons["Settings"].tap()

        // 输入账号密码
        let emailTextField = app.textFields["Email"]
        emailTextField.tap()
        emailTextField.typeText("test@example.com")

        let passwordSecureTextField = app.secureTextFields["Password"]
        passwordSecureTextField.tap()
        passwordSecureTextField.typeText("password123")

        // 点击登录按钮
        app.buttons["Login"].tap()

        // 验证登录成功
        let welcomeLabel = app.staticTexts["Welcome, test@example.com"]
        XCTAssertTrue(welcomeLabel.waitForExistence(timeout: 5))
    }

    func testLaunchPerformance() throws {
        measure(metrics: [XCTApplicationLaunchMetric()]) {
            XCUIApplication().launch()
        }
    }
}

常见问题解决

Xcode 构建问题

bash
# 清理构建缓存
xcodebuild clean -project MyApp.xcodeproj -scheme MyApp

# 删除派生数据
rm -rf ~/Library/Developer/Xcode/DerivedData/MyApp-*

# 重启 Xcode
killall Xcode

# 更新 Swift Package
swift package update

# 检查依赖冲突
swift package resolve --verbose

# 查看详细的构建日志
xcodebuild -project MyApp.xcodeproj -scheme MyApp build OTHER_SWIFT_FLAGS="-verbose" 2>&1 | head -100

模拟器问题

bash
# 重置模拟器
xcrun simctl erase all

# 删除所有模拟器并重新创建
xcrun simctl delete all
xcodebuild -downloadPlatform iOS

# 清除模拟器日志
rm -rf ~/Library/Logs/CoreSimulator/*

# 重置模拟器服务
killall -9 com.apple.CoreSimulator.CoreSimulatorService

# 检查模拟器状态
xcrun simctl list devices available

# 修复模拟器权限问题
sudo chmod -R 777 ~/Library/Developer/CoreSimulator/Devices

证书和签名问题

bash
# 查看可用的代码签名身份
security find-identity -v -p codesigning

# 查看证书
security find-certificate -c "Apple Development"

# 修复钥匙串访问权限
sudo killall -9 securityd

# 重新下载Provisioning Profile
xcodebuild -mapsDownload

# 重置签署状态
rm -rf ~/Library/MobileDevice/Provisioning\ Profiles
xcodebuild -mapsDownload

# 手动签署
codesign --force --sign "Apple Development" --entitlements entitlements.plist MyApp.ipa

App Store 提交流程

bash
# 1. 配置 App Store Connect
# 在 Xcode 中:Product -> Archive -> Upload to App Store Connect

# 2. 使用 altool 上传
xcrun altool --upload-app -f MyApp.ipa -u "apple_id@email.com" -p "app_specific_password"

# 3. 检查上传状态
xcrun altool --list-apps -u "apple_id@email.com" -p "app_specific_password"

# 4. 使用 Transporter 上传(图形界面)
open -a Transporter MyApp.ipa

性能分析

bash
# 使用 Instruments 分析
xcrun instruments -template "Time Profiler" -target MyApp.app -output trace.tracedata

# 使用 SwiftUI 调试
# 在代码中添加
# .onAppear { print("View appeared") }
# .onDisappear { print("View disappeared") }

# 内存泄漏检测
# Xcode -> Debug -> Simulate Memory Warning

# Core Animation 调试
# Xcode -> Debug -> View Debugging -> Rendering -> Color Offscreen Rendered
# Xcode -> Debug -> View Debugging -> Rendering -> Flash Updated Regions