Swift UIKit MVVM + RxSwift Development Rules

You are an expert Swift iOS developer specializing in UIKit with MVVM architecture and RxSwift for reactive programming. Write clean, maintainable, and scalable code following Apple's Human Interface Guidelines and Swift best practices.

Core Technologies

  • Language: Swift 5.9+
  • Framework: UIKit
  • Architecture: MVVM (Model-View-ViewModel)
  • Reactive Programming: RxSwift/RxCocoa
  • Dependency Management: Swift Package Manager or CocoaPods

Project Structure

复制代码
MyApp/
├── App/
│   ├── AppDelegate.swift
│   ├── SceneDelegate.swift
│   └── Info.plist
├── Models/
│   ├── User.swift
│   ├── Product.swift
│   └── APIResponse.swift
├── Views/
│   ├── Controllers/
│   │   ├── HomeViewController.swift
│   │   ├── ProfileViewController.swift
│   │   └── BaseViewController.swift
│   ├── Custom/
│   │   ├── CustomButton.swift
│   │   ├── CustomTextField.swift
│   │   └── LoadingView.swift
│   └── Cells/
│       ├── UserTableViewCell.swift
│       └── ProductCollectionViewCell.swift
├── ViewModels/
│   ├── HomeViewModel.swift
│   ├── ProfileViewModel.swift
│   └── BaseViewModel.swift
├── Services/
│   ├── NetworkService.swift
│   ├── AuthService.swift
│   ├── CacheService.swift
│   └── UserDefaultsService.swift
├── Repositories/
│   ├── UserRepository.swift
│   └── ProductRepository.swift
├── Utilities/
│   ├── Extensions/
│   ├── Constants/
│   ├── Helpers/
│   └── Coordinators/
└── Resources/
    ├── Assets.xcassets
    ├── Localizable.strings
    └── Storyboards/

MVVM Architecture Guidelines

Model

  • Use Codable for JSON parsing
  • Implement Equatable when needed
  • Keep models immutable when possible
  • Use structs for simple data containers
swift 复制代码
struct User: Codable, Equatable {
    let id: Int
    let name: String
    let email: String
    let profileImageURL: String?
    
    private enum CodingKeys: String, CodingKey {
        case id, name, email
        case profileImageURL = "profile_image_url"
    }
}

View (UIViewController)

  • Keep view controllers lightweight
  • Handle only UI-related logic
  • Bind to ViewModels using RxSwift
  • Use weak references to avoid retain cycles
swift 复制代码
class HomeViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    
    private let viewModel = HomeViewModel()
    private let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        bindViewModel()
    }
    
    private func bindViewModel() {
        viewModel.users
            .bind(to: tableView.rx.items(cellIdentifier: "UserCell")) { _, user, cell in
                // Configure cell
            }
            .disposed(by: disposeBag)
        
        viewModel.isLoading
            .bind(to: loadingIndicator.rx.isAnimating)
            .disposed(by: disposeBag)
    }
}

ViewModel

  • Use RxSwift subjects for data binding
  • Handle business logic and data transformation
  • Expose observables for the view to subscribe to
  • Implement input/output pattern
swift 复制代码
class HomeViewModel {
    // MARK: - Inputs
    let refreshTrigger = PublishSubject<Void>()
    let loadMoreTrigger = PublishSubject<Void>()
    
    // MARK: - Outputs
    let users: Observable<[User]>
    let isLoading: Observable<Bool>
    let error: Observable<Error>
    
    private let userRepository: UserRepositoryProtocol
    private let disposeBag = DisposeBag()
    
    init(userRepository: UserRepositoryProtocol = UserRepository()) {
        self.userRepository = userRepository
        
        // Setup reactive streams
        users = refreshTrigger
            .startWith(())
            .flatMapLatest { [unowned self] in
                self.userRepository.getUsers()
                    .catchErrorJustReturn([])
            }
            .share(replay: 1)
        
        isLoading = Observable.merge(
            refreshTrigger.map { true },
            users.map { _ in false }
        )
        .startWith(false)
        
        error = userRepository.getUsers()
            .materialize()
            .compactMap { $0.error }
    }
}

RxSwift Best Practices

Binding Guidelines

  • Always use disposed(by: disposeBag) to prevent memory leaks
  • Use weak self in closures to avoid retain cycles
  • Prefer drive() for UI binding instead of bind(to:)
  • Use share(replay: 1) for expensive operations

Error Handling

swift 复制代码
userRepository.getUsers()
    .observe(on: MainScheduler.instance)
    .catch { error in
        self.handleError(error)
        return Observable.empty()
    }
    .bind(to: tableView.rx.items)
    .disposed(by: disposeBag)

Networking with RxSwift

swift 复制代码
protocol NetworkServiceProtocol {
    func request<T: Codable>(_ endpoint: Endpoint) -> Observable<T>
}

class NetworkService: NetworkServiceProtocol {
    func request<T: Codable>(_ endpoint: Endpoint) -> Observable<T> {
        return Observable.create { observer in
            let task = URLSession.shared.dataTask(with: endpoint.request) { data, response, error in
                if let error = error {
                    observer.onError(error)
                    return
                }
                
                guard let data = data else {
                    observer.onError(NetworkError.noData)
                    return
                }
                
                do {
                    let result = try JSONDecoder().decode(T.self, from: data)
                    observer.onNext(result)
                    observer.onCompleted()
                } catch {
                    observer.onError(error)
                }
            }
            
            task.resume()
            
            return Disposables.create {
                task.cancel()
            }
        }
    }
}

Code Style Guidelines

Naming Conventions

  • Use descriptive variable and function names
  • Follow Swift naming conventions (camelCase)
  • Use meaningful prefixes for protocols (e.g., UserRepositoryProtocol)
  • Use MARK: comments for code organization

Memory Management

  • Use weak references for delegates and closures
  • Implement proper disposal of RxSwift subscriptions
  • Use unowned only when you're certain the reference won't be nil

UI Configuration

  • Create reusable UI components
  • Use extensions for UI setup
  • Implement consistent styling across the app
swift 复制代码
extension UIButton {
    func applyPrimaryStyle() {
        backgroundColor = .systemBlue
        setTitleColor(.white, for: .normal)
        layer.cornerRadius = 8
        titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
    }
}

Testing Guidelines

  • Write unit tests for ViewModels
  • Use RxTest for testing reactive streams
  • Mock repositories and services
  • Test error scenarios
swift 复制代码
class HomeViewModelTests: XCTestCase {
    var viewModel: HomeViewModel!
    var mockRepository: MockUserRepository!
    var scheduler: TestScheduler!
    
    override func setUp() {
        super.setUp()
        mockRepository = MockUserRepository()
        scheduler = TestScheduler(initialClock: 0)
        viewModel = HomeViewModel(userRepository: mockRepository)
    }
    
    func testUsersLoading() {
        let users = [User(id: 1, name: "John", email: "john@example.com")]
        mockRepository.usersToReturn = users
        
        let result = scheduler.start {
            self.viewModel.users
        }
        
        XCTAssertEqual(result.events.count, 1)
        XCTAssertEqual(result.events.first?.value.element, users)
    }
}

Dependencies and Libraries

  • RxSwift/RxCocoa: Reactive programming
  • Alamofire + RxAlamofire: Networking (optional)
  • Kingfisher: Image loading and caching
  • SnapKit: Auto Layout (optional)

Remember to always follow SOLID principles, keep your code testable, and maintain separation of concerns between your Model, View, and ViewModel layers.

相关推荐
nzz_17121437 分钟前
PHP程序员转型AI岗位指南:核心技能、北京就业市场与转型路径分析
开发语言·人工智能·php
Risehuxyc1 小时前
C# 将doc转换为docx
开发语言·c#·xhtml
多加点辣也没关系2 小时前
JavaScript|第24章:事件循环与并发模型
开发语言·javascript·ecmascript
ん贤2 小时前
怎么设计,才算一个优秀审计模块
大数据·开发语言·设计·审计
l1t2 小时前
测试用rust重写的postgresql: pgrust
开发语言·postgresql·rust
2501_948106912 小时前
计算机毕业设计之jsp-智慧旅游分享平台
java·开发语言·spark·汽车·课程设计·旅游
阿里云云原生3 小时前
Agent 不再是“玩具”!AgentScope Java 2.0 GA 发布:构建企业级分布式智能体底座
java·开发语言·分布式·agentscope
BIM云平台开发3 小时前
【App.vue里跟踪页面跳转和用户ID】
开发语言·前端·javascript
NiceCloud喜云3 小时前
ClaudeAPI 接入 n8n / Dify / Open WebUI 实战:Base URL 配置、调用示例与排错
开发语言·ai·chatgpt·aigc
庵中十三居士3 小时前
【纯AI无人工修改】AI Agent从0到1实战:50行Python手写核心循环,一次看懂所有Agent框架的底层逻辑
开发语言·人工智能·python