Appearance
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 18 | 2024 | Apple 智能(AI)、控制中心自定义、密码应用 |
| iOS 17 | 2023 | StandBy 待机模式、交互式小组件、FaceTime 留言 |
| iOS 16 | 2022 | 锁屏自定义、实时活动、Swift Charts |
| iOS 15 | 2021 | Focus 模式、SharePlay、FaceTime 空间音频 |
| iOS 14 | 2020 | App 资源库、小组件、画中画 |
| iOS 13 | 2019 | 暗黑模式、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 devicesXcodebuild 常用命令
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 MyAppSwift 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 BindingsSwift 环境
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.ipaApp 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