You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RxSwift中.never()操作符非测试场景用法及替代方案问询

Understanding .never() in RxSwift (Non-Test Scenarios)

First, let's recap: .never() creates an Observable sequence that never emits any elements and never terminates. It's easy to dismiss it as only useful for tests, but it has practical applications in production code—especially in patterns like MVVM-C, which you're learning. Let's break down real-world use cases with examples, their necessity, and alternatives.


1. Default Placeholder for Non-Optional Observables

In MVVM-C ViewModels, you often have output Observables that don't have an initial value until some action (like a network call) is triggered. Using .never() lets you keep these Observables non-optional, avoiding extra optional handling in the View layer.

Example Code:

import RxSwift
import RxCocoa

struct User {
    let id: String
    let name: String
}

class UserProfileViewModel {
    // Non-optional Observable, initialized with .never() as a placeholder
    var userInfo: Observable<User> = .never()
    private let disposeBag = DisposeBag()
    
    func fetchUser(for userId: String) {
        // Simulate a network request with delay
        let networkRequest = Observable.just(User(id: userId, name: "John Doe"))
            .delay(.seconds(1), scheduler: MainScheduler.instance)
        
        // Replace the placeholder with the actual data sequence
        userInfo = networkRequest
    }
}

// In the ViewController:
class UserProfileViewController: UIViewController {
    private let viewModel = UserProfileViewModel()
    private let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Subscribe to userInfo—no need to handle nil!
        viewModel.userInfo
            .subscribe(onNext: { [weak self] user in
                self?.updateUI(with: user)
            })
            .disposed(by: disposeBag)
        
        viewModel.fetchUser(for: "123")
    }
    
    private func updateUI(with user: User) {
        // Update labels, avatar, etc.
    }
}

Why .never() is Necessary Here:

  • It keeps userInfo non-optional, so the View doesn't have to deal with nil checks or optional binding that adds unnecessary complexity.
  • Unlike .empty() (which emits a completed event immediately), .never() stays alive until we replace it with the actual sequence. This prevents the View from receiving an unexpected termination signal before data loads.

Alternative:

You could use an optional Observable (var userInfo: Observable<User>?), but that forces the View layer to handle optional subscriptions. .empty() is another option, but you'd need to handle the completed event to avoid the subscription ending prematurely.


2. Manual Lifecycle Control in Coordinators

In MVVM-C, Coordinators manage navigation flow. Sometimes you need a sequence that stays active until a user action (like tapping "Back") triggers termination. .never() acts as a "placeholder" finish signal that only gets replaced when the user initiates a navigation action.

Example Code:

protocol Coordinator {
    func start() -> Observable<Void>
}

class ProfileCoordinator: Coordinator {
    private let navigationController: UINavigationController
    private let disposeBag = DisposeBag()
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func start() -> Observable<Void> {
        let viewModel = UserProfileViewModel()
        let viewController = UserProfileViewController(viewModel: viewModel)
        
        // Initialize finish signal with .never()—it won't terminate until we say so
        var finishSignal: Observable<Void> = .never()
        
        // Bind back button tap to terminate the coordinator's sequence
        finishSignal = viewController.backButton.rx.tap
            .do(onNext: { [weak self] in
                self?.navigationController.popViewController(animated: true)
            })
        
        navigationController.pushViewController(viewController, animated: true)
        
        return finishSignal
    }
}

Why .never() is Necessary Here:

  • It ensures the Coordinator's start() method returns a sequence that doesn't terminate automatically, aligning with the Coordinator pattern's goal of letting the user control navigation flow.
  • Using .never() avoids having to manage a PublishSubject or BehaviorSubject (which require manual disposal and event emission), keeping the code cleaner and less error-prone.

Alternative:

You could use a PublishSubject<Void>(), but you'd need to remember to send a next event when the coordinator finishes, and dispose of the subject properly. .never() eliminates this boilerplate entirely.


3. Temporary Placeholder for Unimplemented Features

During development, you might have Observables tied to features that aren't ready yet. .never() lets you keep your code compiling without triggering unintended events (like empty states or errors).

Example Code:

enum Theme {
    case light, dark
}

class SettingsViewModel {
    let themeChanged: Observable<Theme>
    
    init() {
        #if DEBUG
        // Theme switching isn't implemented yet—use .never() to avoid breaking the UI
        themeChanged = .never()
        #else
        // Production: Use the actual theme change sequence
        themeChanged = ThemeManager.shared.themeChanged
        #endif
    }
}

Why .never() is Necessary Here:

  • It prevents the View from receiving unexpected events (like a completed signal from .empty()) that could break UI logic before the feature is ready.
  • It keeps your codebase consistent—you don't have to comment out or modify Observable declarations as you iterate on features.

Alternative:

You could use .empty(), but that would trigger a completed event, which might cause the View to stop listening for future theme changes once the feature is implemented. .never() avoids this issue entirely.


Key Takeaways

.never() shines in scenarios where you need:

  • A non-optional, inert Observable placeholder
  • An infinitely alive sequence that only terminates on manual trigger
  • A safe stand-in for unimplemented features without disrupting existing logic

内容的提问来源于stack exchange,提问作者Nikita Vlasenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:19:33