Skip to content

Instantly share code, notes, and snippets.

@minsOne
Created July 19, 2023 16:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save minsOne/256bbe28c54ff3a67fbeb14953b71711 to your computer and use it in GitHub Desktop.
Save minsOne/256bbe28c54ff3a67fbeb14953b71711 to your computer and use it in GitHub Desktop.
RIBs+SwiftUI
import RIBs
import RxSwift
protocol HomeRouting: ViewableRouting {}
protocol HomePresentable: Presentable {
var listener: HomePresentableListener? { get set }
func update(state: HomeViewState)
}
protocol HomeListener: AnyObject {}
final class HomeInteractor: PresentableInteractor<HomePresentable>, HomeInteractable, HomePresentableListener {
weak var router: HomeRouting?
weak var listener: HomeListener?
override init(presenter: HomePresentable) {
super.init(presenter: presenter)
presenter.listener = self
}
func request(action: HomeViewAction) {
let state: HomeViewState
switch action {
case .viewDidLoad:
state = .init(title: "ViewDidLoad Action",
desc: "Number \(Int.random(in: 0 ... 5))")
case .tap1:
state = .init(title: "Tap1 Action",
desc: "Tap1 Number \(Int.random(in: 0 ... 5))")
case .tap2:
state = .init(title: "Tap2 Action",
desc: "Tap2 Number \(Int.random(in: 0 ... 5))")
}
presenter.update(state: state)
}
}
import Foundation
import SwiftUI
class HomeViewModel: ObservableObject {
typealias State = HomeViewState
typealias Action = HomeViewAction
weak var listener: HomePresentableListener?
@Published var state: State
init(listener: HomePresentableListener? = nil,
state: State)
{
self.listener = listener
self.state = state
}
func update(state: State) {
self.state = state
}
func request(action: Action) {
listener?.request(action: action)
}
}
struct HomeView: View {
@ObservedObject var viewModel: HomeViewModel
var body: some View {
HStack {
Spacer()
VStack(alignment: .center) {
Spacer()
Button("Tap1 Action Button") {
viewModel.request(action: .tap1)
}
Button("Tap2 Action Button") {
viewModel.request(action: .tap2)
}
Spacer()
.frame(height: 10)
Text(viewModel.state.title)
.onChange(of: viewModel.state.title) { _ in
print("title changed to \(viewModel.state.desc)!")
}
.font(.title)
.border(.gray)
Spacer()
.frame(height: 10)
Text(viewModel.state.desc)
.onChange(of: viewModel.state.desc) { _ in
print("desc changed to \(viewModel.state.desc)!")
}
.font(.title)
.border(.gray)
Spacer()
}
Spacer()
}
.border(Color.blue)
.padding()
}
}
#if DEBUG
struct HomeView_Previews: PreviewProvider {
typealias State = HomeViewState
typealias Action = HomeViewAction
typealias ViewModel = HomeViewModel
class Listener: HomePresentableListener {
var viewModel: ViewModel? {
didSet { viewModel?.listener = self }
}
func request(action: Action) {
let state: State
switch action {
case .viewDidLoad:
state = .init(title: "Preview ViewDidLoad Action",
desc: "Number \(Int.random(in: 0 ... 5))")
case .tap1:
state = .init(title: "Preview Tap1 Action",
desc: "Tap1 Number \(Int.random(in: 0 ... 5))")
case .tap2:
state = .init(title: "Preview Tap2 Action",
desc: "Tap2 Number \(Int.random(in: 0 ... 5))")
}
viewModel?.update(state: state)
}
}
static let listener = Listener()
static var previews: some View {
let state = State(title: "Hello", desc: "World")
let view = HomeView(viewModel: .init(state: state))
listener.viewModel = view.viewModel
return view
}
}
#endif
import UIKit
import SwiftUI
protocol HomePresentableListener: AnyObject {
func request(action: HomeViewAction)
}
final class HomeViewController: UIViewController, HomePresentable, HomeViewControllable {
weak var listener: HomePresentableListener? {
didSet {
viewModel.listener = listener
}
}
let viewModel = HomeViewModel(state: .init(title: "Hello", desc: "World"))
override func viewDidLoad() {
super.viewDidLoad()
title = "Hello World"
HomeView(viewModel: viewModel)
.attachTo(ViewController: self)
listener?.request(action: .viewDidLoad)
}
public func update(state: HomeViewState) {
viewModel.update(state: state)
}
}
extension View {
func attachTo(ViewController parentViewController: UIViewController) {
let contentVC = UIHostingController(rootView: self)
let parentVC = parentViewController
parentVC.addChild(contentVC)
contentVC.view.translatesAutoresizingMaskIntoConstraints = false
parentVC.view.addSubview(contentVC.view)
contentVC.didMove(toParent: parentVC)
NSLayoutConstraint.activate([
contentVC.view.topAnchor.constraint(equalTo: parentVC.view.topAnchor),
contentVC.view.bottomAnchor.constraint(equalTo: parentVC.view.bottomAnchor),
contentVC.view.leadingAnchor.constraint(equalTo: parentVC.view.leadingAnchor),
contentVC.view.trailingAnchor.constraint(equalTo: parentVC.view.trailingAnchor),
])
}
}
import Foundation
struct HomeViewState {
var title: String
var desc: String
}
enum HomeViewAction {
case viewDidLoad
case tap1
case tap2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment