Skip to content
This repository has been archived by the owner on Jun 1, 2024. It is now read-only.

Commit

Permalink
feat: enabled subclassing on BeagleScreenViewControllers to observe l…
Browse files Browse the repository at this point in the history
…ifecycle events as well as server driven state events without having to use a navigation controller. (#52)
  • Loading branch information
bradleysmaskell authored Mar 29, 2023
1 parent e3a36ca commit 670c1cf
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 20 deletions.
8 changes: 6 additions & 2 deletions Sources/Beagle/Beagle.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,10 @@
9398CB10255591310003A010 /* RepresentableByParsableString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9398CB0F255591310003A010 /* RepresentableByParsableString.swift */; };
93A2EE1C24EC4BB00085B3CF /* AddChildren+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93A2EE1B24EC4BB00085B3CF /* AddChildren+Extensions.swift */; };
93B9744F23A2833600B0D1CF /* NetworkClientProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93B9743F23A2833600B0D1CF /* NetworkClientProtocol.swift */; };
93C12E2228E75E3F008C051C /* OperationConversionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C12E2128E75E3F008C051C /* OperationConversionTests.swift */; };
93C12E1C28E4BE92008C051C /* listUsingIndex.json in Resources */ = {isa = PBXBuildFile; fileRef = 93C12E1B28E4BE92008C051C /* listUsingIndex.json */; };
93C12E1E28E4BF1B008C051C /* listUsingCustomIndexName.json in Resources */ = {isa = PBXBuildFile; fileRef = 93C12E1D28E4BF1B008C051C /* listUsingCustomIndexName.json */; };
93C12E2028E4C26C008C051C /* listViewUpdatingDataSource.json in Resources */ = {isa = PBXBuildFile; fileRef = 93C12E1F28E4C26C008C051C /* listViewUpdatingDataSource.json */; };
93C12E2228E75E3F008C051C /* OperationConversionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C12E2128E75E3F008C051C /* OperationConversionTests.swift */; };
93D24A512513DF47005A5CBD /* AutoLayoutWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93D24A502513DF47005A5CBD /* AutoLayoutWrapper.swift */; };
93EE0CC3252E56BC0032BE77 /* BindingConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93EE0CC2252E56BC0032BE77 /* BindingConfigurator.swift */; };
93F7072F271A021000202E36 /* AutoCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93F7072E271A021000202E36 /* AutoCodable.swift */; };
Expand Down Expand Up @@ -259,6 +259,7 @@
A9C6F54E273D858B00576313 /* DependenciesContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C6F54D273D858900576313 /* DependenciesContainerTests.swift */; };
A9C6F550273D90FC00576313 /* InjectedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C6F54F273D90FC00576313 /* InjectedTests.swift */; };
A9C6F555273DB76200576313 /* Singletons.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C6F553273DB73C00576313 /* Singletons.swift */; };
B253523D29D1EB2000E492E8 /* CustomBeagleScreenViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B253523C29D1EB2000E492E8 /* CustomBeagleScreenViewControllerTests.swift */; };
B755B58A2686602700A7EDC5 /* GridViewDecodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B755B5892686602700A7EDC5 /* GridViewDecodeTests.swift */; };
B755B591268667D400A7EDC5 /* gridViewSpanCount.json in Resources */ = {isa = PBXBuildFile; fileRef = B755B590268667D400A7EDC5 /* gridViewSpanCount.json */; };
C0291889247D5BFA00C3AA13 /* ComponentFromJsonFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0291888247D5BFA00C3AA13 /* ComponentFromJsonFile.swift */; };
Expand Down Expand Up @@ -576,10 +577,10 @@
9398CB0F255591310003A010 /* RepresentableByParsableString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepresentableByParsableString.swift; sourceTree = "<group>"; };
93A2EE1B24EC4BB00085B3CF /* AddChildren+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AddChildren+Extensions.swift"; sourceTree = "<group>"; };
93B9743F23A2833600B0D1CF /* NetworkClientProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkClientProtocol.swift; sourceTree = "<group>"; };
93C12E2128E75E3F008C051C /* OperationConversionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationConversionTests.swift; sourceTree = "<group>"; };
93C12E1B28E4BE92008C051C /* listUsingIndex.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = listUsingIndex.json; sourceTree = "<group>"; };
93C12E1D28E4BF1B008C051C /* listUsingCustomIndexName.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = listUsingCustomIndexName.json; sourceTree = "<group>"; };
93C12E1F28E4C26C008C051C /* listViewUpdatingDataSource.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = listViewUpdatingDataSource.json; sourceTree = "<group>"; };
93C12E2128E75E3F008C051C /* OperationConversionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationConversionTests.swift; sourceTree = "<group>"; };
93D24A502513DF47005A5CBD /* AutoLayoutWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoLayoutWrapper.swift; sourceTree = "<group>"; };
93EE0CC2252E56BC0032BE77 /* BindingConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingConfigurator.swift; sourceTree = "<group>"; };
93F7072E271A021000202E36 /* AutoCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCodable.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -613,6 +614,7 @@
A9C6F54D273D858900576313 /* DependenciesContainerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependenciesContainerTests.swift; sourceTree = "<group>"; };
A9C6F54F273D90FC00576313 /* InjectedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedTests.swift; sourceTree = "<group>"; };
A9C6F553273DB73C00576313 /* Singletons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Singletons.swift; sourceTree = "<group>"; };
B253523C29D1EB2000E492E8 /* CustomBeagleScreenViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBeagleScreenViewControllerTests.swift; sourceTree = "<group>"; };
B755B5892686602700A7EDC5 /* GridViewDecodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridViewDecodeTests.swift; sourceTree = "<group>"; };
B755B590268667D400A7EDC5 /* gridViewSpanCount.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = gridViewSpanCount.json; sourceTree = "<group>"; };
C0291888247D5BFA00C3AA13 /* ComponentFromJsonFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComponentFromJsonFile.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1443,6 +1445,7 @@
children = (
9356CEE726248D440021EFC6 /* __Snapshots__ */,
9356CD45262482810021EFC6 /* BeagleScreenViewControllerTests.swift */,
B253523C29D1EB2000E492E8 /* CustomBeagleScreenViewControllerTests.swift */,
9356CD46262482810021EFC6 /* BeagleViewBuilderTests.swift */,
9356CD47262482810021EFC6 /* AutoLayoutWrapperTests.swift */,
9356CD48262482810021EFC6 /* Json */,
Expand Down Expand Up @@ -2411,6 +2414,7 @@
9356CE43262482820021EFC6 /* UnitValueExtensionTests.swift in Sources */,
9356CD8F262482820021EFC6 /* BeagleLoggerTests.swift in Sources */,
A9C6F550273D90FC00576313 /* InjectedTests.swift in Sources */,
B253523D29D1EB2000E492E8 /* CustomBeagleScreenViewControllerTests.swift in Sources */,
9356CDA0262482820021EFC6 /* ActionRecordFactoryTests.swift in Sources */,
9356CE47262482820021EFC6 /* MirrorExtension.swift in Sources */,
C0E569152476F026003D413F /* BeaglePrefetchHelpingSpy.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
/*
* Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import XCTest
import SnapshotTesting
@testable import Beagle

final class CustomBeagleScreenViewControllerTests: EnvironmentTestCase {

func testLifecycleMethods() {
let viewController = CustomBeagleScreenViewControllerStub(ComponentStub(), controllerId: #function)
viewController.viewDidAppearExpectation.expectedFulfillmentCount = 1
viewController.viewWillAppearExpectation.expectedFulfillmentCount = 1
viewController.viewDidDisappearExpectation.expectedFulfillmentCount = 1

viewController.viewWillAppear(true)
viewController.viewDidAppear(true)
viewController.viewDidDisappear(true)

wait(
for: [
viewController.viewDidAppearExpectation,
viewController.viewWillAppearExpectation,
viewController.viewDidDisappearExpectation
],
timeout: 0.1
)
}

func testScreenDidChangeState() {
let viewController = CustomBeagleScreenViewControllerStub(ComponentStub(), controllerId: #function)
viewController.didChangeStateExpectation.expectedFulfillmentCount = 1

viewController.didChangeState(.started)

wait(for: [viewController.didChangeStateExpectation], timeout: 0.1)
}

}


class CustomBeagleScreenViewControllerStub: BeagleScreenViewController {

private(set) var viewWillAppearExpectation: XCTestExpectation = .init(description: "viewWillAppearExpectation")
private(set) var viewDidAppearExpectation: XCTestExpectation = .init(description: "viewDidAppearExpectation")
private(set) var viewDidDisappearExpectation: XCTestExpectation = .init(description: "viewDidDisappearExpectation")
private(set) var didChangeStateExpectation: XCTestExpectation = .init(description: "didChangeStateExpectation")

public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
viewWillAppearExpectation.fulfill()
}

public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewDidAppearExpectation.fulfill()
}

public override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
viewDidDisappearExpectation.fulfill()
}

override func didChangeState(_ state: ServerDrivenState) {
super.didChangeState(state)
didChangeStateExpectation.fulfill()
}
}
26 changes: 13 additions & 13 deletions Sources/Beagle/Sources/Renderer/BeagleScreenViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public protocol BeagleControllerProtocol: NSObjectProtocol {
func setNeedsLayout(component: UIView)
}

public class BeagleScreenViewController: BeagleController {
open class BeagleScreenViewController: BeagleController {

private let viewModel: BeagleScreenViewModel

Expand Down Expand Up @@ -96,7 +96,7 @@ public class BeagleScreenViewController: BeagleController {
self.navigationControllerId = controllerId
}

required init(viewModel: BeagleScreenViewModel, controllerId: String? = nil, config: BeagleConfiguration = GlobalConfiguration) {
required public init(viewModel: BeagleScreenViewModel, controllerId: String? = nil, config: BeagleConfiguration = GlobalConfiguration) {
self.viewModel = viewModel
self.navigationControllerId = controllerId
self.config = config
Expand All @@ -107,7 +107,7 @@ public class BeagleScreenViewController: BeagleController {
}

@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

Expand Down Expand Up @@ -152,38 +152,42 @@ public class BeagleScreenViewController: BeagleController {

// MARK: - Lifecycle

public override func viewDidLoad() {
open override func viewDidLoad() {
super.viewDidLoad()
initView()
createContent()
}

public override func viewWillAppear(_ animated: Bool) {
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateNavigationBar(animated: animated)
}

public override func viewDidAppear(_ animated: Bool) {
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if case .view = content {
viewModel.trackEventOnScreenAppeared()
}
}

public override func viewDidDisappear(_ animated: Bool) {
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if case .view = content {
viewModel.trackEventOnScreenDisappeared()
}
}

public override func viewDidLayoutSubviews() {
open override func viewDidLayoutSubviews() {
executeOnInit()
bindings.config()
layoutManager.applyLayout()
super.viewDidLayoutSubviews()
}

open func didChangeState(_ state: ServerDrivenState) {
updateView(state: state)
}

private func executeOnInit() {
for (view, actions) in onInit {
execute(actions: actions, event: "onInit", origin: view)
Expand Down Expand Up @@ -316,11 +320,7 @@ extension BeagleControllerProtocol where Self: UIViewController {

// MARK: - Observer

extension BeagleScreenViewController: BeagleScreenStateObserver {
func didChangeState(_ state: ServerDrivenState) {
updateView(state: state)
}
}
extension BeagleScreenViewController: BeagleScreenStateObserver {}

extension BeagleScreenViewController {
enum Content {
Expand Down
10 changes: 5 additions & 5 deletions Sources/Beagle/Sources/Renderer/BeagleScreenViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

class BeagleScreenViewModel {
public class BeagleScreenViewModel {

// MARK: Properties

Expand All @@ -40,13 +40,13 @@ class BeagleScreenViewModel {

// MARK: Observer

public weak var stateObserver: BeagleScreenStateObserver? {
weak var stateObserver: BeagleScreenStateObserver? {
didSet { stateObserver?.didChangeState(state) }
}

// MARK: Init

static func remote(
internal static func remote(
_ remote: ScreenType.Remote,
viewClient: ViewClientProtocol,
resolver: DependenciesContainerResolving,
Expand All @@ -70,7 +70,7 @@ class BeagleScreenViewModel {
}
}

required init(
internal required init(
screenType: ScreenType,
resolver: DependenciesContainerResolving = GlobalConfiguration.resolver
) {
Expand All @@ -81,7 +81,7 @@ class BeagleScreenViewModel {
_analyticsService = OptionalInjected(resolver)
}

convenience init(
internal convenience init(
screenType: ScreenType,
resolver: DependenciesContainerResolving = GlobalConfiguration.resolver,
beagleViewStateObserver: @escaping BeagleViewStateObserver
Expand Down

0 comments on commit 670c1cf

Please sign in to comment.