{"filename":"swift-uikit-cursorrules-prompt-file.mdc","content":"---\ndescription: \"Cursor rules for Swift Uikit.\"\nglobs: **/*\nalwaysApply: false\n---\n# .cursorrules-mvvm-rxswift\n\n# Swift UIKit MVVM + RxSwift Development Rules\n\nYou 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.\n\n## Core Technologies\n- **Language**: Swift 5.9+\n- **Framework**: UIKit\n- **Architecture**: MVVM (Model-View-ViewModel)\n- **Reactive Programming**: RxSwift/RxCocoa\n- **Dependency Management**: Swift Package Manager or CocoaPods\n\n## Project Structure\n\n```\nMyApp/\n├── App/\n│   ├── AppDelegate.swift\n│   ├── SceneDelegate.swift\n│   └── Info.plist\n├── Models/\n│   ├── User.swift\n│   ├── Product.swift\n│   └── APIResponse.swift\n├── Views/\n│   ├── Controllers/\n│   │   ├── HomeViewController.swift\n│   │   ├── ProfileViewController.swift\n│   │   └── BaseViewController.swift\n│   ├── Custom/\n│   │   ├── CustomButton.swift\n│   │   ├── CustomTextField.swift\n│   │   └── LoadingView.swift\n│   └── Cells/\n│       ├── UserTableViewCell.swift\n│       └── ProductCollectionViewCell.swift\n├── ViewModels/\n│   ├── HomeViewModel.swift\n│   ├── ProfileViewModel.swift\n│   └── BaseViewModel.swift\n├── Services/\n│   ├── NetworkService.swift\n│   ├── AuthService.swift\n│   ├── CacheService.swift\n│   └── UserDefaultsService.swift\n├── Repositories/\n│   ├── UserRepository.swift\n│   └── ProductRepository.swift\n├── Utilities/\n│   ├── Extensions/\n│   ├── Constants/\n│   ├── Helpers/\n│   └── Coordinators/\n└── Resources/\n    ├── Assets.xcassets\n    ├── Localizable.strings\n    └── Storyboards/\n```\n\n## MVVM Architecture Guidelines\n\n### Model\n- Use `Codable` for JSON parsing\n- Implement `Equatable` when needed\n- Keep models immutable when possible\n- Use structs for simple data containers\n\n```swift\nstruct User: Codable, Equatable {\n    let id: Int\n    let name: String\n    let email: String\n    let profileImageURL: String?\n    \n    private enum CodingKeys: String, CodingKey {\n        case id, name, email\n        case profileImageURL = \"profile_image_url\"\n    }\n}\n```\n\n### View (UIViewController)\n- Keep view controllers lightweight\n- Handle only UI-related logic\n- Bind to ViewModels using RxSwift\n- Use weak references to avoid retain cycles\n\n```swift\nclass HomeViewController: UIViewController {\n    @IBOutlet weak var tableView: UITableView!\n    \n    private let viewModel = HomeViewModel()\n    private let disposeBag = DisposeBag()\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        setupUI()\n        bindViewModel()\n    }\n    \n    private func bindViewModel() {\n        viewModel.users\n            .bind(to: tableView.rx.items(cellIdentifier: \"UserCell\")) { _, user, cell in\n                // Configure cell\n            }\n            .disposed(by: disposeBag)\n        \n        viewModel.isLoading\n            .bind(to: loadingIndicator.rx.isAnimating)\n            .disposed(by: disposeBag)\n    }\n}\n```\n\n### ViewModel\n- Use RxSwift subjects for data binding\n- Handle business logic and data transformation\n- Expose observables for the view to subscribe to\n- Implement input/output pattern\n\n```swift\nclass HomeViewModel {\n    // MARK: - Inputs\n    let refreshTrigger = PublishSubject<Void>()\n    let loadMoreTrigger = PublishSubject<Void>()\n    \n    // MARK: - Outputs\n    let users: Observable<[User]>\n    let isLoading: Observable<Bool>\n    let error: Observable<Error>\n    \n    private let userRepository: UserRepositoryProtocol\n    private let disposeBag = DisposeBag()\n    \n    init(userRepository: UserRepositoryProtocol = UserRepository()) {\n        self.userRepository = userRepository\n        \n        // Setup reactive streams\n        users = refreshTrigger\n            .startWith(())\n            .flatMapLatest { [unowned self] in\n                self.userRepository.getUsers()\n                    .catchErrorJustReturn([])\n            }\n            .share(replay: 1)\n        \n        isLoading = Observable.merge(\n            refreshTrigger.map { true },\n            users.map { _ in false }\n        )\n        .startWith(false)\n        \n        error = userRepository.getUsers()\n            .materialize()\n            .compactMap { $0.error }\n    }\n}\n```\n\n## RxSwift Best Practices\n\n### Binding Guidelines\n- Always use `disposed(by: disposeBag)` to prevent memory leaks\n- Use `weak self` in closures to avoid retain cycles\n- Prefer `drive()` for UI binding instead of `bind(to:)`\n- Use `share(replay: 1)` for expensive operations\n\n### Error Handling\n```swift\nuserRepository.getUsers()\n    .observe(on: MainScheduler.instance)\n    .catch { error in\n        self.handleError(error)\n        return Observable.empty()\n    }\n    .bind(to: tableView.rx.items)\n    .disposed(by: disposeBag)\n```\n\n### Networking with RxSwift\n```swift\nprotocol NetworkServiceProtocol {\n    func request<T: Codable>(_ endpoint: Endpoint) -> Observable<T>\n}\n\nclass NetworkService: NetworkServiceProtocol {\n    func request<T: Codable>(_ endpoint: Endpoint) -> Observable<T> {\n        return Observable.create { observer in\n            let task = URLSession.shared.dataTask(with: endpoint.request) { data, response, error in\n                if let error = error {\n                    observer.onError(error)\n                    return\n                }\n                \n                guard let data = data else {\n                    observer.onError(NetworkError.noData)\n                    return\n                }\n                \n                do {\n                    let result = try JSONDecoder().decode(T.self, from: data)\n                    observer.onNext(result)\n                    observer.onCompleted()\n                } catch {\n                    observer.onError(error)\n                }\n            }\n            \n            task.resume()\n            \n            return Disposables.create {\n                task.cancel()\n            }\n        }\n    }\n}\n```\n\n## Code Style Guidelines\n\n### Naming Conventions\n- Use descriptive variable and function names\n- Follow Swift naming conventions (camelCase)\n- Use meaningful prefixes for protocols (e.g., `UserRepositoryProtocol`)\n- Use `MARK:` comments for code organization\n\n### Memory Management\n- Use `weak` references for delegates and closures\n- Implement proper disposal of RxSwift subscriptions\n- Use `unowned` only when you're certain the reference won't be nil\n\n### UI Configuration\n- Create reusable UI components\n- Use extensions for UI setup\n- Implement consistent styling across the app\n\n```swift\nextension UIButton {\n    func applyPrimaryStyle() {\n        backgroundColor = .systemBlue\n        setTitleColor(.white, for: .normal)\n        layer.cornerRadius = 8\n        titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)\n    }\n}\n```\n\n## Testing Guidelines\n- Write unit tests for ViewModels\n- Use RxTest for testing reactive streams\n- Mock repositories and services\n- Test error scenarios\n\n```swift\nclass HomeViewModelTests: XCTestCase {\n    var viewModel: HomeViewModel!\n    var mockRepository: MockUserRepository!\n    var scheduler: TestScheduler!\n    \n    override func setUp() {\n        super.setUp()\n        mockRepository = MockUserRepository()\n        scheduler = TestScheduler(initialClock: 0)\n        viewModel = HomeViewModel(userRepository: mockRepository)\n    }\n    \n    func testUsersLoading() {\n        let users = [User(id: 1, name: \"John\", email: \"john@example.com\")]\n        mockRepository.usersToReturn = users\n        \n        let result = scheduler.start {\n            self.viewModel.users\n        }\n        \n        XCTAssertEqual(result.events.count, 1)\n        XCTAssertEqual(result.events.first?.value.element, users)\n    }\n}\n```\n\n## Dependencies and Libraries\n- **RxSwift/RxCocoa**: Reactive programming\n- **Alamofire + RxAlamofire**: Networking (optional)\n- **Kingfisher**: Image loading and caching\n- **SnapKit**: Auto Layout (optional)\n\nRemember to always follow SOLID principles, keep your code testable, and maintain separation of concerns between your Model, View, and ViewModel layers.\n\n# .cursorrules-mvvm-rxswift_2\n\n# iOS Swift MVVM + RxSwift Generic Rules\n\nYou are an expert iOS Swift developer specializing in MVVM architecture with RxSwift. Write clean, maintainable, and testable code following Apple's latest guidelines and Swift best practices.\n\n## Core Stack\n- **Language**: Swift 5.8+\n- **UI Framework**: UIKit\n- **Architecture**: MVVM (Model-View-ViewModel)\n- **Reactive Framework**: RxSwift + RxCocoa\n- **Minimum Deployment**: iOS 13.0+\n\n## Generic Project Structure\n\n```\nApp/\n├── Models/\n│   ├── User.swift\n│   ├── APIResponse.swift\n│   └── CoreDataModels/\n├── ViewModels/\n│   ├── HomeViewModel.swift\n│   ├── ProfileViewModel.swift\n│   └── BaseViewModel.swift\n├── Views/\n│   ├── ViewControllers/\n│   ├── CustomViews/\n│   └── Cells/\n├── Services/\n│   ├── NetworkService.swift\n│   ├── AuthService.swift\n│   └── DataService.swift\n├── Repositories/\n│   └── UserRepository.swift\n├── Extensions/\n│   ├── UIView+Rx.swift\n│   └── Observable+Extensions.swift\n├── Utilities/\n│   ├── Constants.swift\n│   └── Helpers/\n└── Resources/\n```\n\n## MVVM Implementation Patterns\n\n### 1. Model Layer\nKeep models simple and focused on data representation.\n\n```swift\nstruct User: Codable, Equatable {\n    let id: Int\n    let name: String\n    let email: String\n    let avatar: URL?\n}\n\nstruct APIResponse<T: Codable>: Codable {\n    let data: T\n    let success: Bool\n    let message: String?\n}\n\nenum LoadingState {\n    case idle\n    case loading\n    case loaded\n    case error(Error)\n}\n```\n\n### 2. ViewModel Pattern\nUse Input/Output pattern for clear separation of concerns.\n\n```swift\nprotocol ViewModelType {\n    associatedtype Input\n    associatedtype Output\n    \n    func transform(input: Input) -> Output\n}\n\nfinal class UserListViewModel: ViewModelType {\n    private let userRepository: UserRepositoryProtocol\n    private let disposeBag = DisposeBag()\n    \n    struct Input {\n        let viewDidLoad: Observable<Void>\n        let refresh: Observable<Void>\n        let selection: Observable<IndexPath>\n    }\n    \n    struct Output {\n        let users: Driver<[User]>\n        let loading: Driver<Bool>\n        let error: Driver<String?>\n        let selectedUser: Driver<User?>\n    }\n    \n    init(userRepository: UserRepositoryProtocol) {\n        self.userRepository = userRepository\n    }\n    \n    func transform(input: Input) -> Output {\n        let activityTracker = ActivityIndicator()\n        let errorTracker = ErrorTracker()\n        \n        let users = Observable.merge(input.viewDidLoad, input.refresh)\n            .flatMapLatest { [unowned self] in\n                self.userRepository.fetchUsers()\n                    .trackActivity(activityTracker)\n                    .trackError(errorTracker)\n                    .catchErrorJustReturn([])\n            }\n            .asDriver(onErrorJustReturn: [])\n        \n        let loading = activityTracker.asDriver()\n        \n        let error = errorTracker\n            .map { $0.localizedDescription }\n            .asDriver(onErrorJustReturn: nil)\n        \n        let selectedUser = input.selection\n            .withLatestFrom(users.asObservable()) { indexPath, users in\n                users[safe: indexPath.row]\n            }\n            .asDriver(onErrorJustReturn: nil)\n        \n        return Output(\n            users: users,\n            loading: loading,\n            error: error,\n            selectedUser: selectedUser\n        )\n    }\n}\n```\n\n### 3. View Controller Implementation\nKeep view controllers focused on UI binding and user interaction.\n\n```swift\nfinal class UserListViewController: UIViewController {\n    @IBOutlet private weak var tableView: UITableView!\n    @IBOutlet private weak var refreshButton: UIButton!\n    \n    private let viewModel: UserListViewModel\n    private let disposeBag = DisposeBag()\n    \n    init(viewModel: UserListViewModel) {\n        self.viewModel = viewModel\n        super.init(nibName: nil, bundle: nil)\n    }\n    \n    required init?(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        setupUI()\n        bindViewModel()\n    }\n    \n    private func bindViewModel() {\n        let input = UserListViewModel.Input(\n            viewDidLoad: rx.viewDidLoad.asObservable(),\n            refresh: refreshButton.rx.tap.asObservable(),\n            selection: tableView.rx.itemSelected.asObservable()\n        )\n        \n        let output = viewModel.transform(input: input)\n        \n        output.users\n            .drive(tableView.rx.items(cellIdentifier: \"UserCell\")) { _, user, cell in\n                if let userCell = cell as? UserTableViewCell {\n                    userCell.configure(with: user)\n                }\n            }\n            .disposed(by: disposeBag)\n        \n        output.loading\n            .drive(onNext: { [weak self] isLoading in\n                self?.updateLoadingState(isLoading)\n            })\n            .disposed(by: disposeBag)\n        \n        output.error\n            .compactMap { $0 }\n            .drive(onNext: { [weak self] error in\n                self?.showError(message: error)\n            })\n            .disposed(by: disposeBag)\n        \n        output.selectedUser\n            .compactMap { $0 }\n            .drive(onNext: { [weak self] user in\n                self?.navigateToUserDetail(user)\n            })\n            .disposed(by: disposeBag)\n    }\n}\n```\n\n### 4. Repository Pattern\nAbstract data sources and provide reactive interfaces.\n\n```swift\nprotocol UserRepositoryProtocol {\n    func fetchUsers() -> Observable<[User]>\n    func fetchUser(id: Int) -> Observable<User>\n    func updateUser(_ user: User) -> Observable<User>\n}\n\nfinal class UserRepository: UserRepositoryProtocol {\n    private let networkService: NetworkServiceProtocol\n    private let localService: LocalDataServiceProtocol\n    \n    init(\n        networkService: NetworkServiceProtocol,\n        localService: LocalDataServiceProtocol\n    ) {\n        self.networkService = networkService\n        self.localService = localService\n    }\n    \n    func fetchUsers() -> Observable<[User]> {\n        return networkService.request(.users)\n            .map { (response: APIResponse<[User]>) in response.data }\n            .do(onNext: { [weak self] users in\n                self?.localService.save(users)\n            })\n            .catch { [weak self] _ in\n                self?.localService.fetchUsers() ?? Observable.just([])\n            }\n    }\n    \n    func fetchUser(id: Int) -> Observable<User> {\n        return networkService.request(.user(id: id))\n            .map { (response: APIResponse<User>) in response.data }\n    }\n    \n    func updateUser(_ user: User) -> Observable<User> {\n        return networkService.request(.updateUser(user))\n            .map { (response: APIResponse<User>) in response.data }\n    }\n}\n```\n\n## RxSwift Best Practices\n\n### 1. Memory Management\n```swift\n// Always dispose subscriptions\n.disposed(by: disposeBag)\n\n// Use weak self in closures\n.subscribe(onNext: { [weak self] value in\n    self?.handleValue(value)\n})\n\n// Use unowned when certain reference exists\n.flatMap { [unowned self] in\n    self.processData()\n}\n```\n\n### 2. UI Binding\n```swift\n// Use Driver for UI binding (main thread, no errors)\nviewModel.data.asDriver()\n    .drive(tableView.rx.items)\n    .disposed(by: disposeBag)\n\n// Use Signal for one-time events\nviewModel.showAlert.asSignal()\n    .emit(onNext: { message in\n        // Show alert\n    })\n    .disposed(by: disposeBag)\n```\n\n### 3. Error Handling\n```swift\n// Centralized error tracking\nclass ErrorTracker: SharedSequenceConvertibleType {\n    typealias SharingStrategy = DriverSharingStrategy\n    \n    private let _subject = PublishSubject<Error>()\n    \n    func trackError<O: ObservableConvertibleType>(from source: O) -> Observable<O.Element> {\n        return source.asObservable().do(onError: onError)\n    }\n    \n    func asSharedSequence() -> SharedSequence<SharingStrategy, Error> {\n        return _subject.asObservable().asDriver(onErrorRecover: { _ in .empty() })\n    }\n    \n    func asObservable() -> Observable<Error> {\n        return _subject.asObservable()\n    }\n    \n    private func onError(_ error: Error) {\n        _subject.onNext(error)\n    }\n}\n\n// Activity indicator for loading states\nclass ActivityIndicator: SharedSequenceConvertibleType {\n    typealias Element = Bool\n    typealias SharingStrategy = DriverSharingStrategy\n    \n    private let _lock = NSRecursiveLock()\n    private let _subject = BehaviorSubject(value: false)\n    private let _loading: SharedSequence<SharingStrategy, Bool>\n    \n    init() {\n        _loading = _subject.asObservable()\n            .distinctUntilChanged()\n            .asDriver(onErrorJustReturn: false)\n    }\n    \n    func trackActivityOfObservable<Source: ObservableConvertibleType>(_ source: Source) -> Observable<Source.Element> {\n        return source.asObservable()\n            .do(onNext: { _ in\n                self.sendStopLoading()\n            }, onError: { _ in\n                self.sendStopLoading()\n            }, onCompleted: {\n                self.sendStopLoading()\n            }, onSubscribe: subscribed)\n    }\n    \n    private func subscribed() {\n        _lock.lock()\n        _subject.onNext(true)\n        _lock.unlock()\n    }\n    \n    private func sendStopLoading() {\n        _lock.lock()\n        _subject.onNext(false)\n        _lock.unlock()\n    }\n    \n    func asSharedSequence() -> SharedSequence<SharingStrategy, Element> {\n        return _loading\n    }\n}\n```\n\n### 4. Network Service\n```swift\nprotocol NetworkServiceProtocol {\n    func request<T: Codable>(_ endpoint: APIEndpoint) -> Observable<T>\n}\n\nfinal class NetworkService: NetworkServiceProtocol {\n    private let session: URLSession\n    \n    init(session: URLSession = .shared) {\n        self.session = session\n    }\n    \n    func request<T: Codable>(_ endpoint: APIEndpoint) -> Observable<T> {\n        return Observable.create { observer in\n            let request = endpoint.asURLRequest()\n            \n            let task = self.session.dataTask(with: request) { data, response, error in\n                if let error = error {\n                    observer.onError(NetworkError.connectionError(error))\n                    return\n                }\n                \n                guard let data = data else {\n                    observer.onError(NetworkError.noData)\n                    return\n                }\n                \n                do {\n                    let decodedObject = try JSONDecoder().decode(T.self, from: data)\n                    observer.onNext(decodedObject)\n                    observer.onCompleted()\n                } catch {\n                    observer.onError(NetworkError.decodingError(error))\n                }\n            }\n            \n            task.resume()\n            \n            return Disposables.create {\n                task.cancel()\n            }\n        }\n    }\n}\n\nenum NetworkError: Error {\n    case connectionError(Error)\n    case noData\n    case decodingError(Error)\n}\n\nenum APIEndpoint {\n    case users\n    case user(id: Int)\n    case updateUser(User)\n    \n    func asURLRequest() -> URLRequest {\n        // Implementation details\n        var request = URLRequest(url: url)\n        request.httpMethod = method.rawValue\n        return request\n    }\n}\n```\n\n## Useful Extensions\n\n```swift\n// Observable extensions\nextension Observable {\n    func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable<Element> {\n        return activityIndicator.trackActivityOfObservable(self)\n    }\n    \n    func trackError(_ errorTracker: ErrorTracker) -> Observable<Element> {\n        return errorTracker.trackError(from: self)\n    }\n}\n\n// Array safe subscript\nextension Array {\n    subscript(safe index: Int) -> Element? {\n        return indices.contains(index) ? self[index] : nil\n    }\n}\n\n// UITableView reachBottom\nextension Reactive where Base: UIScrollView {\n    var reachedBottom: Observable<Void> {\n        return contentOffset\n            .flatMap { [weak base] contentOffset -> Observable<Void> in\n                guard let scrollView = base else { return Observable.empty() }\n                \n                let visibleHeight = scrollView.frame.height - scrollView.contentInset.top - scrollView.contentInset.bottom\n                let y = contentOffset.y + scrollView.contentInset.top\n                let threshold = max(0.0, scrollView.contentSize.height - visibleHeight)\n                \n                return y > threshold ? Observable.just(()) : Observable.empty()\n            }\n    }\n}\n```\n\n## Testing with RxTest\n\n```swift\nimport XCTest\nimport RxTest\nimport RxSwift\n@testable import YourApp\n\nclass UserListViewModelTests: XCTestCase {\n    var viewModel: UserListViewModel!\n    var mockRepository: MockUserRepository!\n    var scheduler: TestScheduler!\n    var disposeBag: DisposeBag!\n    \n    override func setUp() {\n        super.setUp()\n        scheduler = TestScheduler(initialClock: 0)\n        mockRepository = MockUserRepository()\n        viewModel = UserListViewModel(userRepository: mockRepository)\n        disposeBag = DisposeBag()\n    }\n    \n    func testViewDidLoadFetchesUsers() {\n        // Given\n        let users = [User(id: 1, name: \"John\", email: \"john@test.com\", avatar: nil)]\n        mockRepository.usersToReturn = users\n        \n        let viewDidLoad = scheduler.createHotObservable([.next(10, ())])\n        let refresh = scheduler.createHotObservable([Recorded<Event<Void>>]())\n        let selection = scheduler.createHotObservable([Recorded<Event<IndexPath>>]())\n        \n        let input = UserListViewModel.Input(\n            viewDidLoad: viewDidLoad.asObservable(),\n            refresh: refresh.asObservable(),\n            selection: selection.asObservable()\n        )\n        \n        let output = viewModel.transform(input: input)\n        let result = scheduler.start { output.users.asObservable() }\n        \n        // Then\n        XCTAssertEqual(result.events.count, 1)\n        XCTAssertEqual(result.events.first?.value.element, users)\n    }\n}\n\nclass MockUserRepository: UserRepositoryProtocol {\n    var usersToReturn: [User] = []\n    \n    func fetchUsers() -> Observable<[User]> {\n        return Observable.just(usersToReturn)\n    }\n    \n    func fetchUser(id: Int) -> Observable<User> {\n        return Observable.just(usersToReturn.first(where: { $0.id == id })!)\n    }\n    \n    func updateUser(_ user: User) -> Observable<User> {\n        return Observable.just(user)\n    }\n}\n```\n\n## Code Guidelines\n\n### Naming Conventions\n- ViewModels: `FeatureViewModel`\n- ViewControllers: `FeatureViewController`\n- Repositories: `FeatureRepository`\n- Services: `FeatureService`\n\n### File Organization\n- Group by feature, not by type\n- Use meaningful folder names\n- Keep related files together\n\n### Architecture Rules\n- ViewModels should not import UIKit\n- Views should not contain business logic\n- Use dependency injection for testability\n- Separate network and local data concerns\n\n### RxSwift Patterns\n- Use Input/Output pattern for ViewModels\n- Prefer Driver/Signal for UI binding\n- Always dispose subscriptions\n- Use ActivityIndicator for loading states\n- Implement proper error handling\n\nRemember: Keep it simple, testable, and maintainable. Focus on reactive streams and clear separation of concerns.\n"}