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.

相关推荐
IT陈图图3 小时前
构建 Flutter × OpenHarmony 跨端带文本输入对话框示例
开发语言·javascript·flutter
叫我辉哥e18 小时前
### 技术文章大纲:C语言造轮子大赛
c语言·开发语言
guygg889 小时前
NOMA功率分配与64 QAM调制中的SIC的MATLAB仿真
开发语言·matlab
flushmeteor9 小时前
JDK源码-基础类-String
java·开发语言
u01092727110 小时前
C++中的策略模式变体
开发语言·c++·算法
雨季66611 小时前
构建 OpenHarmony 简易文字行数统计器:用字符串分割实现纯文本结构感知
开发语言·前端·javascript·flutter·ui·dart
雨季66611 小时前
Flutter 三端应用实战:OpenHarmony 简易倒序文本查看器开发指南
开发语言·javascript·flutter·ui
进击的小头11 小时前
行为型模式:策略模式的C语言实战指南
c语言·开发语言·策略模式
天马379811 小时前
Canvas 倾斜矩形绘制波浪效果
开发语言·前端·javascript
Tansmjs11 小时前
C++与GPU计算(CUDA)
开发语言·c++·算法