实战:用 Expo 实现一个 iOS 功能(对比 XCode 流程)
为了更直观理解 Expo 的优势,我们以”实现相机拍照并保存到相册”为例,对比 XCode 原生开发与 Expo 开发的流程差异。
场景:相机拍照+保存到相册
流程 1:XCode 原生开发(Swift)
1. 配置项目权限
- 在 Info.plist 中手动添加
NSCameraUsageDescription(相机权限描述) - 添加
NSPhotoLibraryAddUsageDescription(相册写入权限描述)
2. 编写相机逻辑
import HeadingWithIconH2 from '@/components/mdx/HeadingWithIconH2.astro';
export const components = {
h2: HeadingWithIconH2,
};
import AVFoundation
import Photos
class CameraViewController: UIViewController, AVCapturePhotoCaptureDelegate {
var captureSession: AVCaptureSession!
var photoOutput: AVCapturePhotoOutput!
override func viewDidLoad() {
super.viewDidLoad()
// 1. 请求相机权限
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
guard granted else { return }
DispatchQueue.main.async {
self?.setupCamera()
}
}
}
// 2. 初始化相机会话
func setupCamera() {
captureSession = AVCaptureSession()
captureSession.sessionPreset = .photo
guard let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return }
guard let input = try? AVCaptureDeviceInput(device: videoDevice) else { return }
if captureSession.canAddInput(input) {
captureSession.addInput(input)
}
photoOutput = AVCapturePhotoOutput()
if captureSession.canAddOutput(photoOutput) {
captureSession.addOutput(photoOutput)
}
// 3. 预览层设置(省略 UI 布局代码)
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.bounds
view.layer.addSublayer(previewLayer)
captureSession.startRunning()
}
// 4. 拍照按钮点击事件
@IBAction func takePhoto(_ sender: UIButton) {
let settings = AVCapturePhotoSettings()
photoOutput.capturePhoto(with: settings, delegate: self)
}
// 5. 处理拍照结果并保存到相册
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
guard let data = photo.fileDataRepresentation() else { return }
guard let image = UIImage(data: data) else { return }
// 保存到相册
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
}
3. 调试与运行
- 连接真机或启动 Simulator,点击 Run 按钮(等待编译)
- 若权限配置错误,需重新修改 Info.plist 并重新编译
- 若相机初始化失败,需排查设备权限或代码逻辑
流程 2:Expo 开发(React Native)
1. 创建项目并安装依赖
npx create-expo-app camera-demo
cd camera-demo
npx expo install expo-camera expo-media-library
2. 编写相机逻辑
import { useState, useEffect } from 'react';
import { Button, View } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
import * as MediaLibrary from 'expo-media-library';
export default function App() {
// 1. 请求相机权限
const [cameraPermission, requestCameraPermission] = useCameraPermissions();
// 2. 请求相册权限
const [mediaLibraryPermission, requestMediaLibraryPermission] = MediaLibrary.usePermissions();
const [cameraRef, setCameraRef] = useState(null);
// 检查权限
useEffect(() => {
if (!cameraPermission) requestCameraPermission();
if (!mediaLibraryPermission) requestMediaLibraryPermission();
}, [cameraPermission, mediaLibraryPermission]);
// 3. 拍照并保存
const takePhoto = async () => {
if (cameraRef) {
const photo = await cameraRef.takePictureAsync();
// 保存到相册
await MediaLibrary.saveToLibraryAsync(photo.uri);
}
};
if (!cameraPermission?.granted) {
return <Button title="请求相机权限" onPress={requestCameraPermission} />;
}
return (
<View style={{ flex: 1 }}>
{/* 相机预览 */}
<CameraView ref={setCameraRef} style={{ flex: 1 }} type="back" />
{/* 拍照按钮 */}
<Button title="拍照" onPress={takePhoto} />
</View>
);
}
3. 调试与运行
npx expo start
- 在 iOS 设备上打开 Expo Go 客户端,扫描终端二维码
- 自动加载应用,修改代码后即时刷新(无需重新编译)
- 权限申请通过弹窗完成,无需手动修改 Info.plist(Expo 自动配置)
核心差异总结
| 环节 | XCode 原生开发 | Expo 开发 |
|---|---|---|
| 权限配置 | 手动修改 Info.plist | 调用 API 自动处理(Expo 生成配置) |
| 相机功能实现 | 需手动管理 AVCaptureSession 生命周期 | 调用 expo-camera 封装的 API,3 行代码搞定 |
| 调试效率 | 每次修改需重新编译(30s+) | 热重载(3s 内生效) |
| 跨平台支持 | 仅 iOS,Android 需重新开发 | 代码直接运行在 Android(仅需调整少量样式) |
结论
对于常见原生功能(相机、定位、通知等),Expo 通过封装好的 SDK 大幅减少了代码量和配置工作。对于熟悉 XCode 的开发者,这种”开箱即用”的体验能显著提升开发效率——尤其当你需要快速实现跨平台功能时。
