Firebase iOS SDK终极指南:从零构建企业级应用
引言:为什么选择Firebase iOS SDK?
还在为iOS应用的后端开发、用户认证、数据分析而烦恼吗?Firebase iOS SDK提供了一套完整的解决方案,让你专注于应用逻辑而非基础设施。本文将带你从零开始,全面掌握Firebase iOS SDK在企业级应用开发中的实战应用。
读完本文,你将获得:
- Firebase核心模块的深度理解
- 企业级应用架构的最佳实践
- 实时数据库与云存储的优化策略
- 用户认证与安全防护的完整方案
- 性能监控与错误追踪的专业技巧
环境准备与项目配置
系统要求
安装方式对比
安装方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
CocoaPods | 成熟稳定,社区支持好 | 需要维护Podfile | 传统项目,需要精确版本控制 |
Swift Package Manager | 原生支持,无需额外工具 | 部分功能可能受限 | 新项目,追求现代化开发 |
Carthage | 编译速度快,依赖清晰 | 配置相对复杂 | 大型项目,需要自定义构建 |
基础配置步骤
- 创建Firebase项目
# 在Firebase控制台创建新项目
# 下载GoogleService-Info.plist配置文件
- 配置Podfile
platform :ios, '13.0'
use_frameworks!
target 'YourApp' do
# Firebase核心服务
pod 'FirebaseCore'
pod 'FirebaseAnalytics'
# 数据库服务
pod 'FirebaseFirestore'
pod 'FirebaseDatabase'
# 用户认证
pod 'FirebaseAuth'
# 云存储
pod 'FirebaseStorage'
# 性能监控
pod 'FirebasePerformance'
# 崩溃报告
pod 'FirebaseCrashlytics'
end
- 初始化配置
import FirebaseCore
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 配置Firebase
FirebaseApp.configure()
// 设置日志级别
FirebaseConfiguration.shared.setLoggerLevel(.debug)
return true
}
}
核心模块深度解析
FirebaseApp:应用入口点
多环境配置策略
enum Environment {
case development
case staging
case production
var options: FIROptions {
switch self {
case .development:
return FIROptions(contentsOfFile: Bundle.main.path(forResource: "GoogleService-Info-Dev", ofType: "plist"))!
case .staging:
return FIROptions(contentsOfFile: Bundle.main.path(forResource: "GoogleService-Info-Staging", ofType: "plist"))!
case .production:
return FIROptions(contentsOfFile: Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist"))!
}
}
}
// 使用示例
func setupFirebase(for environment: Environment) {
if FirebaseApp.app() == nil {
FirebaseApp.configure(options: environment.options)
}
}
实时数据库:Firestore实战
数据模型设计
import FirebaseFirestore
import FirebaseFirestoreSwift
struct User: Codable, Identifiable {
@DocumentID var id: String?
let name: String
let email: String
let createdAt: Date
let updatedAt: Date
var isActive: Bool = true
enum CodingKeys: String, CodingKey {
case id
case name
case email
case createdAt = "created_at"
case updatedAt = "updated_at"
case isActive = "is_active"
}
}
struct Post: Codable, Identifiable {
@DocumentID var id: String?
let title: String
let content: String
let authorID: String
let createdAt: Date
var likes: Int = 0
var tags: [String] = []
enum CodingKeys: String, CodingKey {
case id
case title
case content
case authorID = "author_id"
case createdAt = "created_at"
case likes
case tags
}
}
高级查询优化
class FirestoreManager {
private let db = Firestore.firestore()
// 分页查询
func fetchPosts(limit: Int = 20, lastDocument: DocumentSnapshot? = nil) async throws -> ([Post], DocumentSnapshot?) {
var query = db.collection("posts")
.whereField("is_published", isEqualTo: true)
.order(by: "created_at", descending: true)
.limit(to: limit)
if let lastDocument = lastDocument {
query = query.start(afterDocument: lastDocument)
}
let snapshot = try await query.getDocuments()
let posts = try snapshot.documents.compactMap { document in
try document.data(as: Post.self)
}
return (posts, snapshot.documents.last)
}
// 实时监听
func observeUserPosts(userID: String, completion: @escaping ([Post]) -> Void) -> ListenerRegistration {
return db.collection("posts")
.whereField("author_id", isEqualTo: userID)
.whereField("is_published", isEqualTo: true)
.order(by: "created_at", descending: true)
.addSnapshotListener { snapshot, error in
guard let documents = snapshot?.documents else {
print("Error fetching documents: \(error?.localizedDescription ?? "Unknown error")")
return
}
let posts = documents.compactMap { document in
try? document.data(as: Post.self)
}
completion(posts)
}
}
// 事务操作
func likePost(postID: String, userID: String) async throws {
try await db.runTransaction { transaction, errorPointer in
let postRef = self.db.collection("posts").document(postID)
let userLikeRef = self.db.collection("user_likes").document("\(userID)_\(postID)")
let postDocument: DocumentSnapshot
do {
postDocument = try transaction.getDocument(postRef)
} catch let fetchError as NSError {
errorPointer?.pointee = fetchError
return nil
}
guard let post = try? postDocument.data(as: Post.self) else {
let error = NSError(domain: "AppError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Post not found"])
errorPointer?.pointee = error
return nil
}
// 检查是否已经点赞
let likeDocument: DocumentSnapshot
do {
likeDocument = try transaction.getDocument(userLikeRef)
} catch {
// 文档不存在,可以继续
}
if likeDocument.exists {
throw NSError(domain: "AppError", code: -2, userInfo: [NSLocalizedDescriptionKey: "Already liked"])
}
// 更新点赞数
transaction.updateData(["likes": post.likes + 1], forDocument: postRef)
transaction.setData(["created_at": Date()], forDocument: userLikeRef)
return post.likes + 1
}
}
}
安全规则配置
// firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// 用户文档:只能自己读写
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// 帖子文档:所有人可读,只能作者写
match /posts/{postId} {
allow read: if true;
allow create: if request.auth != null;
allow update, delete: if request.auth != null && request.auth.uid == resource.data.author_id;
}
// 点赞记录:只能创建,不能修改删除
match /user_likes/{likeId} {
allow create: if request.auth != null;
allow read: if request.auth != null;
allow delete: if false;
allow update: if false;
}
}
}
用户认证:Firebase Auth深度应用
多提供商认证集成
import FirebaseAuth
class AuthManager {
static let shared = AuthManager()
private let auth = Auth.auth()
// 邮箱密码认证
func signUpWithEmail(email: String, password: String) async throws -> AuthDataResult {
return try await auth.createUser(withEmail: email, password: password)
}
// 第三方认证(Apple Sign in)
func signInWithApple(idToken: String, nonce: String) async throws -> AuthDataResult {
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idToken,
rawNonce: nonce)
return try await auth.signIn(with: credential)
}
// 第三方认证(Google Sign in)
func signInWithGoogle(idToken: String, accessToken: String) async throws -> AuthDataResult {
let credential = GoogleAuthProvider.credential(withIDToken: idToken,
accessToken: accessToken)
return try await auth.signIn(with: credential)
}
// 手机号认证
func signInWithPhoneNumber(_ phoneNumber: String) async throws -> String {
return try await PhoneAuthProvider.provider()
.verifyPhoneNumber(phoneNumber, uiDelegate: nil)
}
// 验证短信验证码
func verifySMSCode(verificationID: String, verificationCode: String) async throws -> AuthDataResult {
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationID,
verificationCode: verificationCode
)
return try await auth.signIn(with: credential)
}
// 登出
func signOut() throws {
try auth.signOut()
}
// 获取当前用户
var currentUser: User? {
return auth.currentUser
}
// 监听认证状态变化
func addAuthStateListener(handler: @escaping (User?) -> Void) -> AuthStateDidChangeListenerHandle {
return auth.addStateDidChangeListener { _, user in
handler(user)
}
}
}
用户信息管理
extension AuthManager {
// 更新用户资料
func updateUserProfile(displayName: String?, photoURL: URL?) async throws {
let changeRequest = auth.currentUser?.createProfileChangeRequest()
changeRequest?.displayName = displayName
changeRequest?.photoURL = photoURL
try await changeRequest?.commitChanges()
}
// 更新邮箱
func updateEmail(_ email: String) async throws {
try await auth.currentUser?.updateEmail(to: email)
}
// 发送邮箱验证
func sendEmailVerification() async throws {
try await auth.currentUser?.sendEmailVerification()
}
// 重置密码
func resetPassword(email: String) async throws {
try await auth.sendPasswordReset(withEmail: email)
}
// 删除账户
func deleteAccount() async throws {
try await auth.currentUser?.delete()
}
}
云存储:Firebase Storage最佳实践
文件上传下载管理
import FirebaseStorage
class StorageManager {
static let shared = StorageManager()
private let storage = Storage.storage()
// 上传图片
func uploadImage(_ image: UIImage, path: String, compressionQuality: CGFloat = 0.8) async throws -> URL {
guard let imageData = image.jpegData(compressionQuality: compressionQuality) else {
throw NSError(domain: "StorageError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to convert image to data"])
}
let storageRef = storage.reference().child(path)
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
_ = try await storageRef.putDataAsync(imageData, metadata: metadata)
return try await storageRef.downloadURL()
}
// 下载文件
func downloadFile(at path: String) async throws -> Data {
let storageRef = storage.reference().child(path)
return try await storageRef.data(maxSize: 10 * 1024 * 1024) // 10MB限制
}
// 获取下载URL
func getDownloadURL(for path: String) async throws -> URL {
let storageRef = storage.reference().child(path)
return try await storageRef.downloadURL()
}
// 删除文件
func deleteFile(at path: String) async throws {
let storageRef = storage.reference().child(path)
try await storageRef.delete()
}
// 列出文件
func listFiles(in directory: String) async throws -> [StorageReference] {
let storageRef = storage.reference().child(directory)
let result = try await storageRef.listAll()
return result.items
}
// 获取文件元数据
func getFileMetadata(at path: String) async throws -> StorageMetadata {
let storageRef = storage.reference().child(path)
return try await storageRef.getMetadata()
}
// 更新文件元数据
func updateFileMetadata(at path: String, metadata: [String: Any]) async throws {
let storageRef = storage.reference().child(path)
try await storageRef.updateMetadata(StorageMetadata(dictionary: metadata))
}
}
大文件分片上传
extension StorageManager {
// 分片上传大文件
func uploadLargeFile(_ data: Data, path: String, chunkSize: Int = 1 * 1024 * 1024) async throws -> URL {
let storageRef = storage.reference().child(path)
let metadata = StorageMetadata()
metadata.contentType = "application/octet-stream"
// 计算分片数量
let totalChunks = Int(ceil(Double(data.count) / Double(chunkSize)))
var uploadTask: StorageUploadTask?
for chunkIndex in 0..<totalChunks {
let start = chunkIndex * chunkSize
let end = min(start + chunkSize, data.count)
let chunkData = data.subdata(in: start..<end)
if chunkIndex == 0 {
// 第一片
uploadTask = storageRef.putData(chunkData, metadata: metadata)
} else {
// 后续分片
uploadTask = uploadTask?.continueWith { task, error in
if let error = error {
throw error
}
return storageRef.putData(chunkData)
}
}
}
guard let finalTask = uploadTask else {
throw NSError(domain: "StorageError", code: -2, userInfo: [NSLocalizedDescriptionKey: "Upload task not created"])
}
_ = try await finalTask.value
return try await storageRef.downloadURL()
}
// 带进度监控的上传
func uploadWithProgress(_ data: Data, path: String,
onProgress: @escaping (Double) -> Void) async throws -> URL {
let storageRef = storage.reference().child(path)
let metadata = StorageMetadata()
metadata.contentType = "application/octet-stream"
return try await withCheckedThrowingContinuation { continuation in
let uploadTask = storageRef.putData(data, metadata: metadata)
uploadTask.observe(.progress) { snapshot in
guard let progress = snapshot.progress else { return }
let percentComplete = Double(progress.completedUnitCount) / Double(progress.totalUnitCount)
onProgress(percentComplete)
}
uploadTask.observe(.success) { _ in
Task {
do {
let downloadURL = try await storageRef.downloadURL()
continuation.resume(returning: downloadURL)
} catch {
continuation.resume(throwing: error)
}
}
}
uploadTask.observe(.failure) { snapshot in
if let error = snapshot.error {
continuation.resume(throwing: error)
}
}
}
}
}
性能监控与错误追踪
Firebase Performance监控
import FirebasePerformance
class PerformanceMonitor {
static let shared = PerformanceMonitor()
private let perf = Performance.sharedInstance()
// 自定义性能追踪
func trackCustomTrace(name: String, operation: () async throws -> Void) async rethrows {
let trace = perf.trace(name: name)
trace.start()
defer {
trace.stop()
}
try await operation()
}
// 网络请求监控
func monitorNetworkRequest(_ request: URLRequest,
completion: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
let httpMetric = HTTPMetric(url: request.url!, httpMethod: .get)
httpMetric.start()
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let httpResponse = response as? HTTPResponse {
httpMetric.responseCode = httpResponse.statusCode
}
httpMetric.stop()
completion(data, response, error)
}
task.resume()
return task
}
// 应用启动性能
func trackAppStart() {
let trace = perf.trace(name: "app_start_trace")
trace.start()
// 在应用启动完成后停止
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
trace.stop()
}
}
}
Crashlytics错误报告
import FirebaseCrashlytics
class ErrorReporter {
static let shared = ErrorReporter()
private let crashlytics = Crashlytics.crashlytics()
// 记录自定义错误
func recordError(_ error: Error, userInfo: [String: Any]? = nil) {
var customUserInfo = userInfo ?? [:]
customUserInfo["timestamp"] = Date().iso8601String()
customUserInfo["app_version"] = Bundle.main.appVersion
crashlytics.record(error: error, userInfo: customUserInfo)
}
// 设置用户标识
func setUserID(_ userID: String) {
crashlytics.setUserID(userID)
}
// 设置自定义键值
func setCustomValue(_ value: Any, forKey key: String) {
crashlytics.setCustomValue(value, forKey: key)
}
// 记录非致命异常
func recordNonFatalError(domain: String, code: Int, description: String) {
let error = NSError(domain: domain, code: code, userInfo: [NSLocalizedDescriptionKey: description])
recordError(error)
}
// 启用/禁用崩溃报告
func setCrashReportingEnabled(_ enabled: Bool) {
crashlytics.setCrashReportingEnabled(enabled)
}
}
// 扩展工具方法
extension Bundle {
var appVersion: String {
return infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
}
var buildNumber: String {
return infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
}
}
extension Date {
var iso8601String: String {
let formatter = ISO8601DateFormatter()
return formatter.string(from: self)
}
}
企业级架构设计
依赖注入架构
服务层实现
protocol FirestoreService {
func fetchPosts(limit: Int, lastDocument: DocumentSnapshot?) async throws -> ([Post], DocumentSnapshot?)
func observeUserPosts(userID: String, completion: @escaping ([Post]) -> Void) -> ListenerRegistration
func likePost(postID: String, userID: String) async throws
}
protocol AuthService {
func signInWithEmail(email: String, password: String) async throws -> AuthDataResult
func signOut() throws
var currentUser: User? { get }
}
protocol StorageService {
func uploadImage(_ image: UIImage, path: String) async throws -> URL
func downloadFile(at path: String) async throws -> Data
}
// 具体实现
class FirebaseFirestoreService: FirestoreService {
private let firestoreManager: FirestoreManager
init(firestoreManager: FirestoreManager) {
self.firestoreManager = firestoreManager
}
func fetchPosts(limit: Int, lastDocument: DocumentSnapshot?) async throws -> ([Post], DocumentSnapshot?) {
return try await firestoreManager.fetchPosts(limit: limit, lastDocument: lastDocument)
}
// 其他方法实现...
}
// 依赖注入容器
class AppContainer {
let firestoreService: FirestoreService
let authService: AuthService
let storageService: StorageService
init(environment: Environment) {
let firestoreManager = FirestoreManager()
let authManager = AuthManager()
let storageManager = StorageManager()
self.firestoreService = FirebaseFirestoreService(firestoreManager: firestoreManager)
self.authService = FirebaseAuthService(authManager: authManager)
self.storageService = FirebaseStorageService(storageManager: storageManager)
// 配置Firebase
setupFirebase(for: environment)
}
private func setupFirebase(for environment: Environment) {
if FirebaseApp.app() == nil {
FirebaseApp.configure(options: environment.options)
}
}
}
测试策略与质量保证
单元测试示例
import XCTest
@testable import YourApp
import FirebaseAuth
import FirebaseFirestore
class FirestoreServiceTests: XCTestCase {
var firestoreService: FirestoreService!
var mockFirestore: MockFirestore!
override func setUp() {
super.setUp()
mockFirestore = MockFirestore()
let firestoreManager = FirestoreManager(firestore: mockFirestore)
firestoreService = FirebaseFirestoreService(firestoreManager: firestoreManager)
}
func testFetchPosts() async throws {
// 准备测试数据
let mockPosts = [
Post(id: "1", title: "Test Post", content: "Content", authorID: "user1", createdAt: Date()),
Post(id: "2", title: "Test Post 2", content: "Content 2", authorID: "user1", createdAt: Date())
]
mockFirestore.mockDocuments = mockPosts.map { post in
let document = MockDocumentSnapshot()
document.mockData = try! Firestore.Encoder().encode(post)
return document
}
// 执行测试
let (posts, _) = try await firestoreService.fetchPosts(limit: 20, lastDocument: nil)
// 验证结果
XCTAssertEqual(posts.count, 2)
XCTAssertEqual(posts[0].title, "Test Post")
XCTAssertEqual(posts[1].title, "Test Post 2")
}
func testLikePost() async throws {
// 准备测试数据
let postID = "test-post"
let userID = "test-user"
// 执行测试
try await firestoreService.likePost(postID: postID, userID: userID)
// 验证事务操作
XCTAssertTrue(mockFirestore.didRunTransaction)
XCTAssertEqual(mockFirestore.updatedDocuments.count, 2)
}
}
// Mock类实现
class MockFirestore: Firestore {
var mockDocuments: [DocumentSnapshot] = []
var didRunTransaction = false
var updatedDocuments: [String: [String: Any]] = [:]
override func collection(_ collectionPath: String) -> CollectionReference {
return MockCollectionReference(firestore: self, path: collectionPath)
}
override func runTransaction(_ updateBlock: @escaping (Transaction, NSErrorPointer) -> Any?) async throws -> Any? {
didRunTransaction = true
let transaction = MockTransaction(firestore: self)
var error: NSError?
return updateBlock(transaction, &error)
}
}
集成测试策略
class IntegrationTests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launchEnvironment = ["FIREBASE_EMULATORS": "true"]
app.launch()
}
func testUserRegistrationFlow() {
// 启动注册界面
app.buttons["Sign Up"].tap()
// 填写注册信息
let emailTextField = app.textFields["Email"]
emailTextField.tap()
emailTextField.typeText("test@example.com")
let passwordTextField = app.secureTextFields["Password"]
passwordTextField.tap()
passwordTextField.typeText("password123")
// 提交注册
app.buttons["Register"].tap()
// 验证注册成功
XCTAssertTrue(app.staticTexts["Welcome"].waitForExistence(timeout: 5))
}
func testPostCreation() {
// 登录
loginIfNeeded()
// 创建新帖子
app.buttons["New Post"].tap()
let titleTextField = app.textFields["Title"]
titleTextField.tap()
titleTextField.typeText("Integration Test Post")
let contentTextView = app.textViews["Content"]
contentTextView.tap()
contentTextView.typeText("This is a test post created during integration testing.")
app.buttons["Publish"].tap()
// 验证帖子创建成功
XCTAssertTrue(app.staticTexts["Integration Test Post"].waitForExistence(timeout: 3))
}
private func loginIfNeeded() {
if app.buttons["Login"].exists {
app.textFields["Email"].tap()
app.textFields["Email"].typeText("test@example.com")
app.secureTextFields["Password"].tap()
app.secureTextFields["Password"].typeText("password123")
app.buttons["Login"].tap()
}
}
}
性能优化与最佳实践
数据库查询优化
struct QueryOptimizer {
// 使用复合索引
static func createOptimalIndexes() {
// Firestore会自动建议创建复合索引
// 对于常见查询模式,提前创建索引
}
// 查询限制优化
static func optimizeQuery(_ query: Query, for context: QueryContext) -> Query {
var optimizedQuery = query
// 添加必要的where条件
if context.requiresPublished {
optimizedQuery = optimizedQuery.whereField("is_published", isEqualTo: true)
}
// 限制返回字段
if let fields = context.fieldsToReturn {
optimizedQuery = optimizedQuery.selecting(fields)
}
// 添加分页
if let limit = context.limit {
optimizedQuery = optimizedQuery.limit(to: limit)
}
return optimizedQuery
}
// 批量操作优化
static func performBatchOperations(_ operations: [() async throws -> Void]) async throws {
// 使用Firestore的批量写入
let batch = Firestore.firestore().batch()
for operation in operations {
try await operation()
}
try await batch.commit()
}
}
struct QueryContext {
let requiresPublished: Bool
let fieldsToReturn: [String]?
let limit: Int?
let orderBy: String?
let descending: Bool?
}
网络请求优化
class NetworkOptimizer {
// 请求去重
private var ongoingRequests: [String: Task<Any, Error>] = [:]
private let lock = NSLock()
func deduplicatedRequest<T>(key: String, operation: @escaping () async throws -> T) async throws -> T {
lock.lock()
defer { lock.unlock() }
if let existingTask = ongoingRequests[key] as? Task<T, Error> {
return try await existingTask.value
}
let task = Task {
defer {
lock.lock()
ongoingRequests.removeValue(forKey: key)
lock.unlock()
}
return try await operation()
}
ongoingRequests[key] = task as? Task<Any, Error>
return try await task.value
}
// 请求缓存
private var cache: [String: (data: Any, timestamp: Date)] = [:]
private let cacheTTL: TimeInterval = 300 // 5分钟
func cachedRequest<T: Codable>(key: String, operation: @escaping () async throws -> T) async throws -> T {
if let cached = cache[key], Date().timeIntervalSince(cached.timestamp) < cacheTTL {
return cached.data as! T
}
let result = try await operation()
cache[key] = (result, Date())
return result
}
// 请求优先级管理
func prioritizedRequest<T>(priority: TaskPriority = .medium, operation: @escaping () async throws -> T) async throws -> T {
return try await Task(priority: priority) {
try await operation()
}.value
}
}
安全防护与合规性
数据安全策略
class SecurityManager {
// 数据加密
static func encryptData(_ data: Data, key: String) throws -> Data {
// 使用iOS内置的加密框架
// 实际实现应使用Keychain和CryptoKit
return data // 简化示例
}
static func decryptData(_ encryptedData: Data, key: String) throws -> Data {
return encryptedData // 简化示例
}
// 输入验证
static func validateInput(_ input: String, type: InputType) -> Bool {
switch type {
case .email:
let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
return NSPredicate(format: "SELF MATCHES %@", emailRegex).evaluate(with: input)
case .password:
return input.count >= 8 && input.count <= 128
case .username:
let usernameRegex = "^[a-zA-Z0-9_]{3,20}$"
return NSPredicate(format: "SELF MATCHES %@", usernameRegex).evaluate(with: input)
}
}
// XSS防护
static func sanitizeHTML(_ html: String) -> String {
// 使用专业的HTML清理库
// 这里使用基础过滤作为示例
return html.replacingOccurrences(of: "<script", with: "<script")
.replacingOccurrences(of: "</script>", with: "</script>")
}
// CSRF防护
static func generateCSRFToken() -> String {
return UUID().uuidString
}
static func validateCSRFToken(_ token: String) -> Bool {
// 验证token的有效性
return !token.isEmpty
}
}
enum InputType {
case email
case password
case username
}
合规性检查
class ComplianceChecker {
// GDPR合规检查
static func checkGDPRCompliance() -> [ComplianceIssue] {
var issues: [ComplianceIssue] = []
// 检查数据收集同意
if !UserDefaults.standard.bool(forKey: "hasDataCollectionConsent") {
issues.append(.missingDataCollectionConsent)
}
// 检查隐私政策版本
let acceptedPrivacyPolicyVersion = UserDefaults.standard.string(forKey: "privacyPolicyVersion") ?? ""
let currentPrivacyPolicyVersion = "2.0" // 从配置读取
if acceptedPrivacyPolicyVersion != currentPrivacyPolicyVersion {
issues.append(.privacyPolicyUpdateRequired)
}
return issues
}
// 数据本地化检查
static func checkDataLocalization() -> Bool {
// 检查用户数据存储位置是否符合当地法规
// 这里简化实现
return true
}
// 用户数据访问权限检查
static func checkUserDataAccess() -> [DataAccessIssue] {
var issues: [DataAccessIssue] = []
// 检查是否需要提供数据导出功能
// 检查数据删除功能
return issues
}
}
enum ComplianceIssue {
case missingDataCollectionConsent
case privacyPolicyUpdateRequired
case dataLocalizationViolation
}
enum DataAccessIssue {
case dataExportRequired
case dataDeletionRequired
}
部署与监控
持续集成配置
# .github/workflows/ci.yml
name: Firebase iOS CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Select Xcode
run: sudo xcode-select -switch /Applications/Xcode_15.2.app
- name: Install CocoaPods
run: gem install cocoapods
- name: Install Dependencies
run: pod install
- name: Run Unit Tests
run: xcodebuild test -workspace YourApp.xcworkspace -scheme YourApp -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2'
- name: Run Integration Tests
run: xcodebuild test -workspace YourApp.xcworkspace -scheme YourAppIntegrationTests -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2'
- name: Upload Test Results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: build/reports/tests/
build:
runs-on: macos-latest
needs: test
steps:
- uses: actions/checkout@v3
- name: Select Xcode
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考