Skip to content

Commit

Permalink
Merge pull request #3 from k-angama/feature/update-router
Browse files Browse the repository at this point in the history
feat: Add PrepareClosure and fix baseViewController
  • Loading branch information
k-angama authored Jun 6, 2023
2 parents df7ca23 + 8535ec6 commit f352f91
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 17 deletions.
2 changes: 1 addition & 1 deletion CleanArchKit.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Pod::Spec.new do |s|
s.name = 'CleanArchKit'
s.version = '0.1.2'
s.version = '0.1.3'
s.swift_version = "5.0"
s.summary = 'An iOS library to create a project with the Clean Architecture using Router and MVVM pattern.'

Expand Down
65 changes: 59 additions & 6 deletions CleanArchKit/CleanArchKit/Base/BaseRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,78 @@
import Foundation
import UIKit

/// Implement the required methods in your Router (MyRouter in this example) for managing navigation between screens
///
///
/// ```swift
/// class MyRouter: BaseRouter<MyRoute> {
///
/// // Transition between screens
/// override func transition(route: MyRoute) {
/// // Handle navigation between screens here
/// }
///
/// // ...
/// }
/// ```
open class BaseRouter<T: Route>: Router {

/**
* Current view controller
*/
// MARK: - Public Properties

public typealias PrepareClosure = (_ segue: UIStoryboardSegue, _ sender: Any?) -> Void

/// Current view controller
weak public var viewController: UIViewController?


// MARK: - Public Method

/**
* Initializer
*
* @param current view controller
Initializer

- Parameters
- viewController: Current view controller
*/
required public init(viewController: UIViewController? = nil) {
self.viewController = viewController
}

/**
Transition between screens

- Parameters
- route: Enum
*/
open func transition(route: T) {}

/**
Intercepts the prepare method of the viewController

- Parameters
- block: Prepare method closure. Returns segue storyboard and sender
*/
open func prepare(block: @escaping PrepareClosure) {
swizzleImplementation(blockPrepare: block)
}


// MARK: - Private Properties

private func swizzleImplementation(blockPrepare: PrepareClosure?) {
guard let vc = viewController,
let aClass: AnyClass = object_getClass(vc) else { return }
let selector = #selector(vc.prepare(for:sender:))
let originalMethod = class_getInstanceMethod(aClass, selector)

let block: @convention(block) (_ sender: Any?, _ segue: UIStoryboardSegue) -> Void = {sender, segue in
blockPrepare?(segue, sender)
}
let types = method_getTypeEncoding(originalMethod!);
class_replaceMethod(aClass, selector, imp_implementationWithBlock(unsafeBitCast(block, to: AnyObject.self)), types)
}

deinit {
print("deinit Router - \(Self.self)")
}

}
11 changes: 11 additions & 0 deletions CleanArchKit/CleanArchKit/Base/BaseUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@

import Foundation

/// Define your use cases by creating classes conforming to the UseCase protocol
///
/// ```swift
///
/// struct MyUseCase: UseCase {
/// func execute(params: String) -> Bool {
/// // Implement your logic here
/// }
/// }
///
/// ```
public protocol BaseUseCase {

associatedtype Input
Expand Down
13 changes: 7 additions & 6 deletions CleanArchKit/CleanArchKit/Base/BaseViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ open class BaseViewModel<T: Router, Input: InputProtocol, Output: OutputProtocol

private var _router: T
public var router: T {
if(Thread.callStackSymbols.contains(where: {
$0.contains("UIViewController")
})){
let message = "Not used to the UIViewController"
guard !Thread.callStackSymbols.isEmpty &&
Thread.callStackSymbols.count > 0
else { return _router }
let symbol = Thread.callStackSymbols[1]

if symbol.contains("ViewController") {
let message = "Router cannot be called in a viewController"
#if DEBUG
fatalError(message)
#else
Expand All @@ -29,12 +32,10 @@ open class BaseViewModel<T: Router, Input: InputProtocol, Output: OutputProtocol
return _router
}


required public init(viewController: UIViewController? = nil) {
self.input = Input()
self.output = Output()
self._router = T(viewController: viewController)

}

/**
Expand Down
12 changes: 12 additions & 0 deletions CleanArchKit/CleanArchKit/Protocol/DIProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@

import Foundation

/// Implement dependency injection using the DIProtocol:
///
/// ```swift
///
/// extension MainViewController {
/// @objc func di() {
/// self.viewModel.input.checkCredentialUseCase = CheckCredentialUseCase(loginAPI: RegistrationAPI())
/// self.viewModel.input.checkValidEmailUseCase = CheckValidEmailUseCase()
/// }
/// }
///
/// ```
@objc public protocol DIProtocol {
@objc optional func di()
}
5 changes: 2 additions & 3 deletions CleanArchKit/CleanArchKit/Protocol/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
import Foundation
import UIKit

/**
* Route for describes which routes can be triggered.
*/
/// Route for describes which routes can be triggered.
///
public protocol Route { }

public protocol Router: AnyObject {
Expand Down
2 changes: 1 addition & 1 deletion Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 0379424d1a27e9a926255062c7d08b9ce3a023f8

COCOAPODS: 1.11.3
COCOAPODS: 1.12.1

0 comments on commit f352f91

Please sign in to comment.