Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
RxLifecycle

Adds 6 Observable fields on UIViewController:

Two main Observable<Bool>

  • rx_visible: did the controller appear/disappear?
  • rx_willBeVisible: will the controller appear/disappear?

And conveniently splitting those two out into Observable<Void>:

  • rx_willAppear
  • rx_willDisappear
  • rx_appear
  • rx_disappear

Example usage:

Observable<Int>.interval(10, scheduler: MainScheduler.instance)
  .takeUntil(self.rx_disappear)
  .subscribeNext { print("\($0)") }
//
// UIViewController+RxLifecycleSwizzle
//
// Created by Herman Banken on 25/08/16.
// Copyright © 2016 Q42. All rights reserved.
//
import Foundation
import UIKit
import RxSwift
var lifecycleSubjectContext: UInt8 = 0
// MARK: - Subject Containers
class ViewControllerLifeCycleObservable {
let visibleSubject = ReplaySubject<Bool>.create(bufferSize: 1)
let willBeVisibleSubject = ReplaySubject<Bool>.create(bufferSize: 1)
func viewWillAppear() {
willBeVisibleSubject.on(.Next(true))
}
func viewDidAppear() {
visibleSubject.on(.Next(true))
}
func viewWillDisappear() {
willBeVisibleSubject.on(.Next(false))
}
func viewDidDisappear() {
visibleSubject.on(.Next(false))
}
deinit {
visibleSubject.on(.Completed)
willBeVisibleSubject.on(.Completed)
}
}
// MARK: - Method Swizzling
extension UIViewController {
public override class func initialize() {
struct Static {
static var token: dispatch_once_t = 0
}
// make sure this isn't a subclass
if self !== UIViewController.self {
return
}
func swizzle(original: Selector, _ replacement: Selector) {
let originalMethod = class_getInstanceMethod(self, original)
let swizzledMethod = class_getInstanceMethod(self, replacement)
let didAddMethod = class_addMethod(self, original, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, replacement, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
dispatch_once(&Static.token) {
swizzle(#selector(UIViewController.viewWillAppear(_:)), #selector(UIViewController.rxlifecycle_viewWillAppear(_:)))
swizzle(#selector(UIViewController.viewWillDisappear(_:)), #selector(UIViewController.rxlifecycle_viewWillDisappear(_:)))
swizzle(#selector(UIViewController.viewDidAppear(_:)), #selector(UIViewController.rxlifecycle_viewDidAppear(_:)))
swizzle(#selector(UIViewController.viewDidDisappear(_:)), #selector(UIViewController.rxlifecycle_viewDidDisappear(_:)))
}
}
// MARK: - Shiny new Observable properties
public var rx_visible: Observable<Bool> {
return rx_synchronized {
if let lifecycleObservable = objc_getAssociatedObject(self, &lifecycleSubjectContext) as? ViewControllerLifeCycleObservable {
return lifecycleObservable.visibleSubject
}
let lifecycleObservable = ViewControllerLifeCycleObservable()
objc_setAssociatedObject(self, &lifecycleSubjectContext, lifecycleObservable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return lifecycleObservable.visibleSubject
}
}
public var rx_willBeVisible: Observable<Bool> {
return rx_synchronized {
if let lifecycleObservable = objc_getAssociatedObject(self, &lifecycleSubjectContext) as? ViewControllerLifeCycleObservable {
return lifecycleObservable.willBeVisibleSubject
}
let lifecycleObservable = ViewControllerLifeCycleObservable()
objc_setAssociatedObject(self, &lifecycleSubjectContext, lifecycleObservable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return lifecycleObservable.willBeVisibleSubject
}
}
public var rx_willAppear: Observable<Void> { return rx_willBeVisible.filter { $0 }.map { _ in Void() } }
public var rx_willDisappear: Observable<Void> { return rx_willBeVisible.filter { !$0 }.map { _ in Void() } }
public var rx_appear: Observable<Void> { return rx_visible.filter { $0 }.map { _ in Void() } }
public var rx_disappear: Observable<Void> { return rx_visible.filter { !$0 }.map { _ in Void() } }
// MARK: - Swizzled Methods
func rxlifecycle_viewWillAppear(animated: Bool) {
self.rxlifecycle_viewWillAppear(animated)
if let lifecycleObservable = objc_getAssociatedObject(self, &lifecycleSubjectContext) as? ViewControllerLifeCycleObservable {
lifecycleObservable.viewWillAppear()
}
}
func rxlifecycle_viewWillDisappear(animated: Bool) {
self.rxlifecycle_viewWillDisappear(animated)
if let lifecycleObservable = objc_getAssociatedObject(self, &lifecycleSubjectContext) as? ViewControllerLifeCycleObservable {
lifecycleObservable.viewWillDisappear()
}
}
func rxlifecycle_viewDidAppear(animated: Bool) {
self.rxlifecycle_viewDidAppear(animated)
if let lifecycleObservable = objc_getAssociatedObject(self, &lifecycleSubjectContext) as? ViewControllerLifeCycleObservable {
lifecycleObservable.viewDidAppear()
}
}
func rxlifecycle_viewDidDisappear(animated: Bool) {
self.rxlifecycle_viewDidDisappear(animated)
if let lifecycleObservable = objc_getAssociatedObject(self, &lifecycleSubjectContext) as? ViewControllerLifeCycleObservable {
lifecycleObservable.viewDidDisappear()
}
}
}
extension NSObject {
func rx_synchronized<T>(@noescape action: () -> T) -> T {
objc_sync_enter(self)
let result = action()
objc_sync_exit(self)
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment