From 8535ec6314023ab14bbe23e2d60e2707028ae300 Mon Sep 17 00:00:00 2001 From: Karim Angama Date: Tue, 6 Jun 2023 13:22:10 +0200 Subject: [PATCH] feat: Add PrepareClosure and fix baseViewController --- CleanArchKit.podspec | 2 +- .../CleanArchKit/Base/BaseRouter.swift | 65 +++++++++++++++++-- .../CleanArchKit/Base/BaseUseCase.swift | 11 ++++ .../CleanArchKit/Base/BaseViewModel.swift | 13 ++-- .../CleanArchKit/Protocol/DIProtocol.swift | 12 ++++ .../CleanArchKit/Protocol/Router.swift | 5 +- Podfile.lock | 2 +- 7 files changed, 93 insertions(+), 17 deletions(-) diff --git a/CleanArchKit.podspec b/CleanArchKit.podspec index a765821..0b77ae2 100644 --- a/CleanArchKit.podspec +++ b/CleanArchKit.podspec @@ -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.' diff --git a/CleanArchKit/CleanArchKit/Base/BaseRouter.swift b/CleanArchKit/CleanArchKit/Base/BaseRouter.swift index a2a4a72..7090016 100644 --- a/CleanArchKit/CleanArchKit/Base/BaseRouter.swift +++ b/CleanArchKit/CleanArchKit/Base/BaseRouter.swift @@ -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 { +/// +/// // Transition between screens +/// override func transition(route: MyRoute) { +/// // Handle navigation between screens here +/// } +/// +/// // ... +/// } +/// ``` open class BaseRouter: 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)") } + } diff --git a/CleanArchKit/CleanArchKit/Base/BaseUseCase.swift b/CleanArchKit/CleanArchKit/Base/BaseUseCase.swift index 3f62816..1ea34c4 100644 --- a/CleanArchKit/CleanArchKit/Base/BaseUseCase.swift +++ b/CleanArchKit/CleanArchKit/Base/BaseUseCase.swift @@ -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 diff --git a/CleanArchKit/CleanArchKit/Base/BaseViewModel.swift b/CleanArchKit/CleanArchKit/Base/BaseViewModel.swift index 8c85beb..cfa2992 100644 --- a/CleanArchKit/CleanArchKit/Base/BaseViewModel.swift +++ b/CleanArchKit/CleanArchKit/Base/BaseViewModel.swift @@ -16,10 +16,13 @@ open class BaseViewModel 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 @@ -29,12 +32,10 @@ open class BaseViewModel