♻️ :: Moved logic for removing the gradientView from its superview on deinit to view-model and provide tests

This commit is contained in:
Felix Mau
2019-08-30 15:10:11 +02:00
parent 8ecf4c239d
commit 7621da5884
3 changed files with 28 additions and 38 deletions
@@ -17,8 +17,6 @@ class GradientLoadingBarViewModelTestCase: XCTestCase {
private var sharedApplicationMock: SharedApplicationMock!
private var notificationCenter: NotificationCenter!
private var viewModel: GradientLoadingBarViewModel!
// MARK: - Public methods
override func setUp() {
@@ -26,14 +24,9 @@ class GradientLoadingBarViewModelTestCase: XCTestCase {
sharedApplicationMock = SharedApplicationMock()
notificationCenter = NotificationCenter()
viewModel = GradientLoadingBarViewModel(sharedApplication: sharedApplicationMock,
notificationCenter: notificationCenter)
}
override func tearDown() {
viewModel = nil
notificationCenter = nil
sharedApplicationMock = nil
@@ -73,16 +66,22 @@ class GradientLoadingBarViewModelTestCase: XCTestCase {
keyWindow)
}
func testInitializerShouldSetupSuperviewObservableAfterUIWindowDidBecomeKeyNotificationJustOnce() {
func testDeinitShouldResetSuperviewObservableToNil() {
// Given
let viewModel = GradientLoadingBarViewModel(sharedApplication: sharedApplicationMock,
notificationCenter: notificationCenter)
let keyWindow = UIWindow()
sharedApplicationMock.keyWindow = keyWindow
let expectation = self.expectation(description: "Expected observer for superview to be informed just once.")
// swiftlint:disable:next unnecessary_type
var viewModel: GradientLoadingBarViewModel? = GradientLoadingBarViewModel(sharedApplication: sharedApplicationMock,
notificationCenter: notificationCenter)
let expectation = self.expectation(description: "Expected observer to be informed to reset superview to nil.")
var disposeBag = DisposeBag()
viewModel.superview.subscribe { newSuperview, _ in
guard newSuperview != nil else {
// swiftlint:disable:next force_unwrapping
viewModel!.superview.subscribe { newSuperview, _ in
guard newSuperview == nil else {
// Skip initial call to observer.
return
}
@@ -91,11 +90,7 @@ class GradientLoadingBarViewModelTestCase: XCTestCase {
}.disposed(by: &disposeBag)
// When
for _ in 1 ... 3 {
sharedApplicationMock.keyWindow = UIWindow()
notificationCenter.post(name: UIWindow.didBecomeKeyNotification,
object: nil)
}
viewModel = nil
// Then
wait(for: [expectation], timeout: 0.1)
@@ -78,31 +78,24 @@ open class GradientLoadingBarController {
bindViewModelToView()
}
/// By providing a custom deinitializer we make sure to remove the corresponding `gradientView` from its superview.
deinit {
if gradientView.superview != nil {
gradientView.removeFromSuperview()
}
}
// MARK: - Private methods
private func bindViewModelToView() {
viewModel.superview.subscribeDistinct { [weak self] newSuperview, _ in
self?.addGradientView(to: newSuperview)
self?.updateSuperview(newSuperview)
}.disposed(by: &disposeBag)
}
private func addGradientView(to superview: UIView?) {
guard let superview = superview else {
// We've received an invalid superview.
return
private func updateSuperview(_ superview: UIView?) {
// If the view’s superview is not nil, the superview releases the view.
gradientView.removeFromSuperview()
if let superview = superview {
gradientView.translatesAutoresizingMaskIntoConstraints = false
superview.addSubview(gradientView)
setupConstraints(superview: superview)
}
gradientView.translatesAutoresizingMaskIntoConstraints = false
superview.addSubview(gradientView)
setupConstraints(superview: superview)
}
// MARK: - Public methods
@@ -49,14 +49,16 @@ final class GradientLoadingBarViewModel {
}
}
deinit {
/// By providing a custom deinitializer we make sure to remove the corresponding `gradientView` from its superview.
superviewSubject.value = nil
}
// MARK: - Private methods
@objc private func didReceiveUIWindowDidBecomeKeyNotification(_: Notification) {
guard let keyWindow = sharedApplication.keyWindow else { return }
// Prevent informing the listener multiple times.
notificationCenter.removeObserver(self)
// Now that we have a valid key window, we can use it as superview.
superviewSubject.value = keyWindow
}