Skip to content

Instantly share code, notes, and snippets.

@sgr-ksmt
Last active September 9, 2022 10:07
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sgr-ksmt/2cc92d8c7d517e08767fbe296b6da720 to your computer and use it in GitHub Desktop.
Save sgr-ksmt/2cc92d8c7d517e08767fbe296b6da720 to your computer and use it in GitHub Desktop.
MVVM + RxSwift + Property Wrapper
extension AnyObserver {
static func create<E>(_ relay: PublishRelay<E>) -> AnyObserver<E> {
return .init { event in
guard case let .next(value) = event else { return }
relay.accept(value)
}
}
static func create<E>(_ relay: BehaviorRelay<E>) -> AnyObserver<E> {
return .init { event in
guard case let .next(value) = event else { return }
relay.accept(value)
}
}
}
// See: https://github.com/inamiy/RxProperty/blob/master/Sources/RxProperty/RxProperty.swift
import Foundation
import RxRelay
import RxSwift
@propertyWrapper
struct RxTrigger<Value> {
private let relay = PublishRelay<Value>()
private let trigger: AnyObserver<Value>
private let observable: Observable<Value>
init() {
trigger = .create(relay)
observable = relay.asObservable()
}
var wrappedValue: AnyObserver<Value> {
trigger
}
var projectedValue: Observable<Value> {
observable
}
}
@propertyWrapper
struct RxProperty<Value> {
private let relay: BehaviorRelay<Value>
private let property: Property<Value>
init(value: Value) {
relay = .init(value: value)
property = .init(relay)
}
var wrappedValue: Property<Value> {
property
}
var projectedValue: BehaviorRelay<Value> {
relay
}
}
final class SampleViewController: UIViewController {
private let viewModel: SampleViewModelType
private let disposeBag = DisposeBag()
init(viewModel: SampleViewModelType = SampleViewModel()) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
viewModel.outputs.messages
.changed
.bind(onNext: { print($0) })
.disposed(by: disposeBag)
rx.sentMessage(#selector(SampleViewController.viewWillAppear(_:)))
.map { _ in }
.take(1)
.bind(to: viewModel.inputs.refresh)
.disposed(by: disposeBag)
}
}
protocol SampleViewModelInputs {
var refresh: AnyObserver<Void> { get }
}
protocol SampleViewModelOutputs {
var messages: Property<[String]> { get }
}
protocol SampleViewModelType {
var inputs: SampleViewModelInputs { get }
var outputs: SampleViewModelOutputs { get }
}
final class SampleViewModel: SampleViewModelType, SampleViewModelInputs, SampleViewModelOutputs {
var inputs: SampleViewModelInputs { return self }
var outputs: SampleViewModelOutputs { return self }
@RxTrigger var refresh: AnyObserver<Void>
@RxProperty(value: []) var messages: Property<[String]>
private let disposeBag = DisposeBag()
init() {
$refresh
.map { ["Hello", "How about you?"] }
.bind(to: $messages)
.disposed(by: disposeBag)
}
}
protocol SampleViewModelInputs {
var refresh: AnyObserver<Void> { get }
}
protocol SampleViewModelOutputs {
var messages: Property<[String]> { get }
}
protocol SampleViewModelType {
var inputs: SampleViewModelInputs { get }
var outputs: SampleViewModelOutputs { get }
}
final class SampleViewModel: SampleViewModelType, SampleViewModelInputs, SampleViewModelOutputs {
var inputs: SampleViewModelInputs { return self }
var outputs: SampleViewModelOutputs { return self }
let refresh: AnyObserver<Void>
let messages: Property<[String]>
private let disposeBag = DisposeBag()
init() {
let _refresh = PublishRelay<Void>()
self.refresh = .create(_refresh)
let _messages = BehaviorRelay<[String]>(value: [])
self.messages = .init(_messages)
_refresh
.map { ["Hello", "How about you?"] }
.bind(to: _messages)
.disposed(by: disposeBag)
}
}
@Changzw
Copy link

Changzw commented Sep 9, 2022

how about this?

@propertyWrapper
public struct RxTrigger<T> {
  private let subject = PublishSubject<T>()
  public var wrappedValue: AnyObserver<T> {
    subject.asObserver()
  }
  public var projectedValue: Observable<T> {
    subject.asObservable()
  }
  public init(){ }
}

//test demo 
public class KK {
  @RxProperty public var ss: Int = 0
  @RxTrigger public var bb: AnyObserver<Void>
  public init() {
    $ss.bind {
      print($0)
    }
    ss = 10
    $bb.bind{
      print("kkkkk")
    }
    bb.onNext(())
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment