阅读视图

发现新文章,点击刷新页面。

RxSwift 框架解析

一、RxSwift 核心架构图 二、Observable:异步流的抽象 作用: RxSwift 中一切皆 Observable,它是异步事件流的载体,负责发出元素或错误/完成事件。 特性: 惰性执行,

xcode 16.2报错 Sandbox: rsync.samba(xxxx)解决方案

解决方案来源

在开发 iOS 应用时,开发者常会遇到因 macOS 系统 沙盒机制(Sandbox) 导致的文件写入权限问题,典型错误如:

Sandbox: rsync.samba(...) deny(1) file-write-create ...

Snip20250604_4.png

这类错误通常发生在构建 Flutter、React Native 或集成第三方库(如 RxCocoa、Realm、MobileVLCKit)的项目中。本文将从原理、解决方案和实践技巧三个方面,帮助你快速定位并解决问题。


一、问题原理

1. 沙盒机制的作用

macOS 的沙盒机制是系统安全策略的一部分,限制应用程序对文件系统、网络等资源的访问权限。Xcode 在构建 iOS 应用时,默认启用沙盒保护,防止恶意行为。但某些场景下(如 Flutter 插件、第三方库的构建脚本),沙盒限制可能导致文件写入失败。

2. 常见触发场景

  • Flutter/React Native 项目:构建过程中需生成临时文件(如 .xcframeworkdSYM 文件),沙盒可能阻止写入。
  • CocoaPods/Carthage 集成:依赖库的构建脚本(如 rsync)可能因权限不足失败。
  • Xcode 版本升级:Xcode 15+ 引入更严格的沙盒策略,部分旧配置失效。
  • 多 Targets 项目:未统一配置所有 Target 的 ENABLE_USER_SCRIPT_SANDBOXING 设置。

二、通用解决方案

1. 禁用用户脚本沙盒

这是最直接的解决方案,适用于大多数场景:

操作步骤

  1. 打开 Xcode 项目。
  2. 进入 Project Navigator → 选择项目 → Build Settings
  3. 搜索 ENABLE_USER_SCRIPT_SANDBOXING
  4. 将值设为 No(适用于所有 Build Configurations:Debug/Release)。

示意图

Xcode 设置 ENABLE_USER_SCRIPT_SANDBOXING.png

注意事项

  • 需同时修改所有 Target:包括主工程(Runner)、Pods 工程(如 CleverPushNotificationServiceExtension)等。
  • 重启 Xcode:修改后需重启 Xcode 生效。

2. 清理缓存并重新安装依赖

沙盒错误可能由旧缓存或依赖残留导致:

操作步骤

  1. 删除 DerivedData
    rm -rf ~/Library/Developer/Xcode/DerivedData
    
  2. 清理 Flutter 缓存(针对 Flutter 项目):
    flutter clean
    flutter pub get
    
  3. 重新安装 CocoaPods 依赖
    cd ios
    pod deintegrate
    pod install --repo-update
    

参考资料

RxSwift 开源学习项目汇总

如果你有知道更好的开源学习项目请留言给我,我会更新到我得文章中,让更多爱学习的朋友看到!


🔝 1.RxSwift 官方示例项目

  • 地址ReactiveX/RxSwift

  • 简介:RxSwift 官方仓库提供了多个示例,展示了如何在实际项目中使用 RxSwift 进行异步操作和数据流处理。

  • 特点

    • 涵盖了 KVO 观察、异步操作、UI 事件等多个方面。
    • 提供了丰富的操作符使用示例。
  • 适合人群:希望深入理解 RxSwift 基础和高级用法的开发者。


📚 2.RxSwift 学习资料合集

  • 地址LeoMobileDeveloper/awesome-rxswift

  • 简介:这是一个精心整理的 RxSwift 学习资源合集,包含了开源应用、库、教程和社区资源。

  • 特点

    • 收录了多个开源项目,如 RxTodo、RxChat、RxGithub 等。
    • 提供了丰富的教程和学习资料链接。
  • 适合人群:希望通过多个项目和教程全面学习 RxSwift 的开发者。


🧱 3.iOS Clean Architecture MVVM + RxSwift 示例

  • 地址kwontaewan/iOS-Clean-Architecture-MVVM-RxSwift

  • 简介:该项目展示了如何使用 Clean Architecture、MVVM 架构和 RxSwift 构建 iOS 应用。

  • 特点

    • 采用分层架构,代码结构清晰。
    • 集成了 Moya 进行网络请求,使用 Kingfisher 进行图片加载。
  • 适合人群:希望学习如何在实际项目中应用 Clean Architecture 和 RxSwift 的开发者。


📖 4.RxSwift 教程配套项目

  • 地址kodecocodes/rxs-materials

  • 简介:这是 Raywenderlich.com 上 RxSwift 教程的配套项目,涵盖了从基础到高级的多个示例。

  • 特点

    • 每个章节都有对应的示例项目,便于学习和实践。
    • 涵盖了 RxSwift 的各个方面,包括操作符、调度器、错误处理等。
  • 适合人群:希望系统学习 RxSwift 的开发者。


🎨 5.MVVM + RxSwift 示例应用

  • 地址alexey-savchenko/MVVM-RxSwift-sample-app

  • 简介:该项目展示了如何结合 MVVM 架构和 RxSwift 构建 iOS 应用,使用了 SnapKit 进行布局。

  • 特点

    • 实现了相册、帖子和评论等功能。
    • 使用了 Coordinator 模式进行导航管理。
  • 适合人群:希望学习如何在实际项目中应用 MVVM 和 RxSwift 的开发者。


📱 6.SwiftHub - GitHub iOS 客户端

  • 地址khoren93/SwiftHub

  • 简介:SwiftHub 是一个使用 RxSwift 和 MVVM-C 架构构建的 GitHub iOS 客户端,展示了如何在实际应用中使用这些技术。

  • 特点

    • 实现了 GitHub 的多个功能模块,如仓库浏览、搜索等。
    • 采用了 Clean Architecture 和 Coordinator 模式。
  • 适合人群:希望学习如何构建复杂应用并应用 RxSwift 和 MVVM-C 架构的开发者。


📘 7.RxSwift 教程示例合集

  • 地址DroidsOnRoids/RxSwiftExamples

  • 简介:该项目包含了多个 RxSwift 教程的示例,涵盖了基础、网络请求、多线程等内容。

  • 特点

    • 每个示例都有详细的说明,便于理解和学习。
    • 涵盖了从基础到高级的多个主题。
  • 适合人群:希望通过多个小项目逐步学习 RxSwift 的开发者。


SwiftUI 值得学习的一些项目汇总

如果还有更好的学习项目欢迎留言,我会更新到我得文章中,让更多的人看到的!

🚀 推荐的 SwiftUI 开源项目

1.GeekMadeBySwiftUI

这是一个功能丰富的 SwiftUI 项目,展示了如何构建一个完整的应用程序。项目涵盖了用户界面设计、数据管理、网络请求等多个方面,非常适合初学者学习。

2.ZYSwiftUIFrame

该项目提供了一个完整的 SwiftUI 应用示例,包含网络请求、下拉刷新、上拉加载更多、数据增删改查、图片上传和预览等功能。项目还包含了服务端代码,使用 Go 语言编写,模拟真实的项目场景。

3.swiftui-example

这是一个 SwiftUI 示例、技巧和技术集合,旨在帮助开发者构建应用程序、解决问题,并了解 SwiftUI 的实际工作方式。项目主要内容来源于 hackingwithswift.com,适合想要深入了解 SwiftUI 的开发者。

4.Food Truck(苹果官方示例)

这是苹果在 WWDC22 发布的官方示例项目,展示了如何使用 SwiftUI 构建一个完整的应用程序。项目涵盖了以下内容:

  • 使用 NavigationSplitView 管理视图

  • 使用 Charts 展示趋势数据

  • 使用 WeatherService 获取天气数据

  • 实现了 Live Activities 和 Dynamic Island

2.Clean Architecture SwiftUI

该项目展示了如何在 SwiftUI 中应用 Clean Architecture 架构,涵盖了以下内容:

  • 使用 SwiftData 进行数据持久化

  • 实现网络请求

  • 依赖注入

  • 单元测试

适合希望构建可维护、可测试的 SwiftUI 应用的开发者。 


3.Fun SwiftUI Projects

该项目集合了 50 多个 SwiftUI 示例项目,包括:

  • 3D 柱状图

  • 饼图

  • 贪吃蛇游戏

  • 表情符号识别游戏

  • Reddit 客户端

每个项目都附有详细的教程,适合希望通过实践学习 SwiftUI 的开发者。


4.Simple SwiftUI

该项目由著名的 Swift 教程作者 Paul Hudson 创建,包含多个小型 SwiftUI 示例项目,如:

  • 新闻阅读器

  • 记事本

  • 记分板

  • 待办事项列表

适合初学者快速上手 SwiftUI。


5.SwiftUI Weather App

这是一个简单的天气应用示例,包含以下功能:

  • 主界面
  • 每日天气列表
  • 天气详情页面

适合初学者练习 SwiftUI 的基本布局和导航。 


6.SwiftUI App by Mindinventory

该项目展示了一个完整的应用流程,包括:

  • 引导页面

  • 登录/注册/忘记密码页面

  • 主界面

  • 收藏页面

  • 退出登录功能

适合希望了解完整应用流程的开发者。


7.SwiftUI 30 Projects

该项目包含了 30 个使用 SwiftUI 构建的应用示例,涵盖了各种常见的 UI 组件和功能,适合希望通过大量练习掌握 SwiftUI 的开发者。


8.SwiftUI Projects by MattWong

该项目包含多个 SwiftUI 示例项目,包括:

  • 宝可梦图鉴
  • 搜索功能
  • 列表和详情页面

适合希望通过实际项目学习 SwiftUI 的开发者。


📚 学习建议

  • 从基础开始:如果你是初学者,建议先从简单的项目入手,逐步了解 SwiftUI 的布局、视图和数据绑定等基础知识。
  • 逐步深入:在掌握基础知识后,可以尝试更复杂的项目,如 ZYSwiftUIFrame,学习如何处理网络请求、数据管理等实际开发中常见的问题。
  • 参考官方文档:苹果官方的 SwiftUI 文档 是学习 SwiftUI 的权威资料,建议结合项目实践进行学习。

从一个简单的登录示例开始解析Combine + MVVM

下边是一个登录的简单示例,直接复制到iOS项目工程中运行即可,URL需要替换成真实的才能起作用。这里的简单示例仅供学习!

// Combine + MVVM 示例:一个简单的登录界面,加入真实网络请求(使用 URLSession)

import SwiftUI
import Combine

// MARK: - 网络模型
struct LoginResponse: Decodable {
    let success: Bool
    let message: String
}

// MARK: - ViewModel
class LoginViewModel: ObservableObject {
    // 输入
    @Published var username: String = ""
    @Published var password: String = ""

    // 输出
    @Published var isLoginEnabled: Bool = false
    @Published var loginStatus: String = ""

    private var cancellables = Set<AnyCancellable>()

    init() {
        // 验证输入是否满足登录条件(用户名和密码长度 >= 3)
        Publishers.CombineLatest($username, $password)
            .map { username, password in
                return username.count >= 3 && password.count >= 3
            }
            .assign(to: &$isLoginEnabled)
    }

    func login() {
        guard let url = URL(string: "https://example.com/api/login") else {
            loginStatus = "无效的请求地址"
            return
        }

        let params = ["username": username, "password": password]
        let requestBody = try? JSONSerialization.data(withJSONObject: params)

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = requestBody

        loginStatus = "登录中..."

        URLSession.shared.dataTaskPublisher(for: request)
            .map { $0.data }
            .decode(type: LoginResponse.self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { completion in
                if case let .failure(error) = completion {
                    self.loginStatus = "请求失败:\(error.localizedDescription)"
                }
            }, receiveValue: { response in
                self.loginStatus = response.success ? "登录成功 ✅" : "登录失败 ❌:\(response.message)"
            })
            .store(in: &cancellables)
    }
}

// MARK: - View
struct LoginView: View {
    @StateObject private var viewModel = LoginViewModel()

    var body: some View {
        VStack(spacing: 20) {
            TextField("用户名", text: $viewModel.username)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .autocapitalization(.none)

            SecureField("密码", text: $viewModel.password)
                .textFieldStyle(RoundedBorderTextFieldStyle())

            Button("登录") {
                viewModel.login()
            }
            .disabled(!viewModel.isLoginEnabled)
            .padding()
            .background(viewModel.isLoginEnabled ? Color.blue : Color.gray)
            .foregroundColor(.white)
            .cornerRadius(8)

            Text(viewModel.loginStatus)
                .foregroundColor(.green)
        }
        .padding()
    }
}

// MARK: - 预览
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        LoginView()
    }
}

一、Combine 框架核心概念解析

1. 发布者 (Publisher)

@Published var username: String = ""
  • @Published 属性包装器:将普通属性转换为发布者
  • 当属性值改变时自动发送新值
  • 本质是创建了 Publisher 的派生类型:Published<Value>.Publisher

2. 操作符 (Operators)

Publishers.CombineLatest($username, $password)
    .map { ... } // 转换操作符
    .assign(to: &$isLoginEnabled) // 绑定操作符
  • CombineLatest:合并两个发布者的最新值(此处监听两个输入框)
  • map:值转换(将元组转换为布尔值)
  • assign:将值直接绑定到属性(自动管理内存)

3. 订阅者 (Subscriber)

.sink(receiveCompletion: { ... }, receiveValue: { ... })
  • sink:最常用的订阅者,提供两个闭包:
    • receiveValue:处理正常数据流
    • receiveCompletion:处理完成事件(成功/失败)
  • 返回 AnyCancellable 对象

4. 订阅管理

private var cancellables = Set<AnyCancellable>()
.store(in: &cancellables) 
  • AnyCancellable:类型擦除的取消令牌
  • 内存管理原理
    • cancellables 集合被释放时,自动取消所有订阅
    • 防止内存泄漏(尤其重要网络请求场景)

二、MVVM 架构实现细节

1. ViewModel 设计

class LoginViewModel: ObservableObject {
    // 输入属性
    @Published var username = ""
    @Published var password = ""
    
    // 输出属性
    @Published var isLoginEnabled = false
    @Published var loginStatus = ""
}
  • 数据流向
    graph LR
    View-->|绑定| ViewModel-->|发布| View
    
  • 双向绑定@Published 属性 + SwiftUI 的 $ 前缀语法
  • 状态驱动:所有 UI 变化都源自状态改变

2. 输入验证的实现

Publishers.CombineLatest($username, $password)
    .map { u, p in u.count >= 3 && p.count >= 3 }
    .assign(to: &$isLoginEnabled)
  • 响应式验证:当任一输入变化时自动重新验证
  • 无副作用:纯函数式转换(map 内部无状态修改)

三、网络请求关键实现

1. URLSession 的 Combine 扩展

URLSession.shared.dataTaskPublisher(for: request)
  • 返回 DataTaskPublisher 类型(遵守 Publisher 协议)
  • 发出值类型:(data: Data, response: URLResponse)
  • 错误类型:URLError

2. 响应处理链

.map { $0.data }  // 提取数据
.decode(type: LoginResponseModel.self, decoder: JSONDecoder()) // JSON解码
.receive(on: DispatchQueue.main) // 切换主线程
.sink(...) // 处理结果
  • 操作符链:典型的数据处理管道(Pipeline)
  • 线程切换
    • 网络请求默认在后台线程
    • receive(on:) 确保 UI 更新在主线程

3. 错误处理机制

sink(receiveCompletion: { 
    if case .failure(let error) = completion {
        self.loginStatus = "失败: \(error.localizedDescription)"
    }
}, ...)
  • 分离处理:成功走 receiveValue,失败走 receiveCompletion
  • 错误转换:保留原始错误信息(开发时可扩展为自定义错误类型)

四、SwiftUI 视图层实现要点

1. 视图与 ViewModel 的连接

@StateObject private var viewModel = LoginViewModel()
  • @StateObject:视图持有 ViewModel 的生命周期
  • @ObservedObject 的区别:创建者负责管理内存

2. 控件绑定

TextField("用户名", text: $viewModel.username)
SecureField("密码", text: $viewModel.password)
  • $ 语法糖:访问 @Published 属性的投影(Projection)
  • 等价于:Binding<String> 类型

3. 条件渲染

.background(viewModel.isLoginEnabled ? Color.blue : Color.gray)
.disabled(!viewModel.isLoginEnabled)
  • 响应式 UI:属性变化自动触发视图更新
  • 无命令式代码:无需手动 reloadupdate

五、关键优化点与注意事项

  1. 网络请求取消

    // 在 ViewModel 中增加
    func cancelRequests() {
        cancellables.forEach { $0.cancel() }
    }
    
    • 在视图消失时调用(防止后台更新销毁的视图)
  2. 输入防抖处理

    .debounce(for: .seconds(0.5), scheduler: RunLoop.main)
    
    • 避免频繁触发验证(尤其快速输入场景)
  3. 错误分类处理

    enum LoginError: Error {
        case invalidURL, networkError(URLError), decodingError
    }
    
    • 使用自定义错误类型提升可维护性
  4. 测试方案

    • ViewModel 单元测试:Mock URLSession
    • UI 测试:验证不同状态下的界面表现

六、MVVM + Combine 优势总结

特性 传统方式 Combine + MVVM
数据绑定 手动更新 自动响应
异步处理 回调地狱 声明式管道
状态管理 分散控制 集中处理
代码量 冗余 简洁(减少30%+)
可测试性 困难 依赖注入友好

完整技术栈:SwiftUI 声明式UI + Combine 响应式编程 + MVVM 架构模式


这个示例完整展示了现代 Swift 开发的核心理念:通过声明式语法描述 UI,通过响应式编程处理数据流,通过架构模式解耦逻辑

❌