Understanding RIB Architecture in iOS: A Complete Guide to Router‑Interactor‑Builder
This article explains the RIB (Router‑Interactor‑Builder) architecture for iOS, detailing its core components, how to generate and connect them with Xcode templates, manage navigation, inject dependencies, write unit tests with mock generation, and integrate the whole structure into a root RIB for a production‑ready app.
What Is a RIB?
RIB stands for Router + Interactor + Builder. It is an iOS architecture that drives an application from business logic rather than view hierarchy. A RIB is visualized as a tree where each node can have zero or many child nodes, which can be attached or detached during the app lifecycle.
Diving Into RIBs
When creating a new RIB you can use the Xcode RIB template. Selecting RIB from the file creation menu generates four files: Builder, Interactor, Router, and View/Presenter.
LoginBuilder
import RIBs
protocol LoginDependency: Dependency { }
final class LoginComponent: Component<LoginDependency> { }
// MARK: - Builder
protocol LoginBuildable: Buildable {
func build(withListener listener: LoginListener) -> LoginRouting
}
final class LoginBuilder: Builder<LoginDependency>, LoginBuildable {
override init(dependency: LoginDependency) {
super.init(dependency: dependency)
}
func build(withListener listener: LoginListener) -> LoginRouting {
let component = LoginComponent(dependency: dependency)
let viewController = LoginViewController()
let interactor = LoginInteractor(presenter: viewController)
interactor.listener = listener
return LoginRouter(interactor: interactor, viewController: viewController)
}
}LoginInteractor
import RIBs
import RxSwift
protocol LoginRouting: ViewableRouting { }
protocol LoginPresentable: Presentable {
var listener: LoginPresentableListener? { get set }
func showActivityIndicator(_ isLoading: Bool)
}
protocol LoginListener: class { }
final class LoginInteractor: PresentableInteractor<LoginPresentable>, LoginInteractable, LoginPresentableListener {
weak var router: LoginRouting?
weak var listener: LoginListener?
override init(presenter: LoginPresentable) {
super.init(presenter: presenter)
presenter.listener = self
}
override func didBecomeActive() { super.didBecomeActive() }
override func willResignActive() { super.willResignActive() }
// Business‑logic methods will be added here
}LoginRouter
import RIBs
protocol LoginInteractable: Interactable {
var router: LoginRouting? { get set }
var listener: LoginListener? { get set }
}
protocol LoginViewControllable: ViewControllable { }
final class LoginRouter: ViewableRouter<LoginInteractable, LoginViewControllable>, LoginRouting {
override init(interactor: LoginInteractable, viewController: LoginViewControllable) {
super.init(interactor: interactor, viewController: viewController)
interactor.router = self
}
// Navigation methods will be added here
}LoginPresenter / LoginViewController
import RIBs
import RxSwift
import UIKit
protocol LoginPresentableListener: class { }
final class LoginViewController: UIViewController, LoginPresentable, LoginViewControllable {
weak var listener: LoginPresentableListener?
func showActivityIndicator(_ isLoading: Bool) { /* update UI */ }
func present(_ viewController: ViewControllable) {
present(viewController.uiviewController, completion: nil)
}
}The Interactor communicates with the Presenter (ViewController) to update UI, with the Router to navigate, and with the parent RIB via the Listener.
Advanced RIB Topics
Mock Generation
Using tools like Mockolo, you can annotate protocols with /// @mockable and generate mock implementations automatically. Example mock for WebServicing:
class WebServicingMock: WebServicing {
init() { }
var loginCallCount = 0
var loginHandler: ((String, String, (Result<String, Error>) -> Void) -> ())?
func login(username: String, password: String, handler: (Result<String, Error>) -> Void) {
loginCallCount += 1
loginHandler?(username, password, handler)
}
}Unit Testing
With generated mocks you can fully test Interactor and Router logic. Below is a simplified XCTest case for LoginInteractor covering activity‑indicator handling, web‑service calls, success and failure flows.
final class LoginInteractorTests: XCTestCase {
private var interactor: LoginInteractor!
private var presenter = LoginPresentableMock()
private var listener = LoginListenerMock()
private var router = LoginRoutingMock()
private let webService = WebServicingMock()
override func setUp() {
super.setUp()
interactor = LoginInteractor(presenter: presenter, webService: webService)
interactor.router = router
interactor.listener = listener
}
func test_didTapLogin_triggersWebTask_andShowsActivity() {
presenter.showActivityIndicatorHandler = { isLoading in
XCTAssertTrue(isLoading)
}
interactor.didTapLogin(username: "user", password: "pass")
XCTAssertEqual(webService.loginCallCount, 1)
XCTAssertEqual(presenter.showActivityIndicatorCallCount, 1)
}
func test_loginSuccess_callsDismissListener() {
webService.loginHandler = { _, _, handler in handler(.success("uid")) }
interactor.didTapLogin(username: "user", password: "pass")
XCTAssertEqual(listener.dismissLoginFlowCallCount, 1)
XCTAssertEqual(presenter.showActivityIndicatorCallCount, 2) // start & stop
}
func test_loginFailure_showsErrorAlert() {
webService.loginHandler = { _, _, handler in handler(.failure(WebServiceError.generic)) }
interactor.didTapLogin(username: "user", password: "pass")
XCTAssertEqual(presenter.showErrorAlertCallCount, 1)
XCTAssertEqual(presenter.showActivityIndicatorCallCount, 2)
}
}Dependency Injection
The sample uses Uber’s Needle framework to wire dependencies from a top‑level AppComponent down to each RIB, reducing boilerplate while keeping the protocol‑based design.
Conclusion
The RIB architecture separates concerns into Router, Interactor, Builder, View, and Presenter, enabling clear navigation, testability, and scalability for iOS apps. Combined with mock generation and a DI framework like Needle, it provides a robust foundation for large‑scale mobile projects.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
