Skip to content

Commit

Permalink
💫 Update: UIViewController+Swizzling
Browse files Browse the repository at this point in the history
  • Loading branch information
dominicstop committed Apr 27, 2023
1 parent f1f2358 commit a2bc44f
Showing 1 changed file with 134 additions and 110 deletions.
244 changes: 134 additions & 110 deletions ios/src_library/Extensions/UIViewController+Swizzling.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,101 +7,143 @@

import Foundation




extension UIViewController {

// MARK: - Static - Swizzling-Related
// ----------------------------------

static var isSwizzled = false;

fileprivate var modalWrapper: RNIModalViewControllerWrapper? {
RNIModalViewControllerWrapperRegistry.get(forViewController: self);
internal static func swizzleMethods() {
guard RNIModalFlagsShared.shouldSwizzleViewControllers else { return };

#if DEBUG
print(
"Log - UIViewController+Swizzling"
+ " - UIViewController.swizzleMethods invoked"
);
#endif

RNIUtilities.swizzleExchangeMethods(
defaultSelector: #selector(Self.present(_:animated:completion:)),
newSelector: #selector(Self._swizzled_present(_:animated:completion:)),
forClass: UIViewController.self
);

RNIUtilities.swizzleExchangeMethods(
defaultSelector: #selector(Self.dismiss(animated:completion:)),
newSelector: #selector(Self._swizzled_dismiss(animated:completion:)),
forClass: UIViewController.self
);

self.isSwizzled.toggle();
};

// MARK: - Helpers - Static
// ------------------------

@discardableResult
fileprivate func registerIfNeeded(
viewControllerToPresent: UIViewController
static private func registerIfNeeded(
forViewController vc: UIViewController
) -> RNIModalViewControllerWrapper? {

/// If `viewControllerToPresent` is being presented by a
/// `RNIModalViewController` instance, then we don't need to wrap the
/// current instance inside a `RNIModalViewControllerWrapper` since it
/// will already notify `RNIModalManager` of modal-related events...
let shouldWrapVC = vc is RNIModalViewController
? RNIModalFlagsShared.shouldWrapAllViewControllers
: true;

/// If the arg `vc` is a `RNIModalViewController` instance, then we don't
/// need to wrap the current instance inside a
/// `RNIModalViewControllerWrapper` since it will already notify
/// `RNIModalManager` of modal-related events...
///
guard !(self is RNIModalViewController) else { return nil };
guard shouldWrapVC else { return nil };

let modalWrapper: RNIModalViewControllerWrapper = {

/// A - Wrapper already exists for `viewControllerToPresent`,
/// return matching instance.
/// A - Wrapper already exists for arg `vc`, so return the matching
/// wrapper instance.
///
if let modalWrapper = RNIModalViewControllerWrapperRegistry.get(
forViewController: viewControllerToPresent
forViewController: vc
) {

return modalWrapper;
};

// B - Wrapper does not exists for `viewControllerToPresent`,
// so create new instance.
//
let newModalWrapper = RNIModalViewControllerWrapper();
/// B - Wrapper does not exists for arg `vc`, so create a new wrapper
/// instance.
///
let newModalWrapper = RNIModalViewControllerWrapper(viewController: vc);

RNIModalViewControllerWrapperRegistry.set(
forViewController: self,
forViewController: vc,
newModalWrapper
);

return newModalWrapper;
}();

modalWrapper.presentingViewController = self;
modalWrapper.modalViewController = viewControllerToPresent;


return modalWrapper;
};

fileprivate func getPresentedModal(
viewControllerToPresent: UIViewController? = nil
// MARK: - Helpers - Computed Properties
// -------------------------------------

/// Get the associated `RNIModalViewControllerWrapper` instance for the
/// current view controller
///
var modalWrapper: RNIModalViewControllerWrapper? {
RNIModalViewControllerWrapperRegistry.get(forViewController: self);
};

// MARK: - Helpers - Functions
// ---------------------------

private func getPresentedModalToNotify(
_ presentedVC: UIViewController? = nil
) -> (any RNIModal)? {
if let presentedModalVC = viewControllerToPresent as? RNIModalViewController {
return presentedModalVC.modalViewRef;

} else if let presentedVC = self.presentedViewController,
let presentedModalVC = presentedVC as? RNIModalViewController {

/// A - Arg `viewControllerToPresent` is a `RNIModalViewController`
return presentedModalVC.modalViewRef;

} else if let presentingModalVC = self as? RNIModalViewController,
let presentingModal = presentingModalVC.modalViewRef,
let presentedModalVC = presentingModal.modalVC {

/// B - Current vc instance is a `RNIModalViewController` (and was
/// presented by a `RNIModalView`).
return presentedModalVC.modalViewRef;

} else if let presentedVC = viewControllerToPresent,
let presentedModalWrapper =
RNIModalViewControllerWrapperRegistry.get(forViewController: presentedVC) {

/// C - `viewControllerToPresent` has a corresponding
/// `RNIModalViewControllerWrapper` instance associated to it.
return presentedModalWrapper;

} else if let presentingModalWrapper = self.modalWrapper,
let presentedVC = presentingModalWrapper.modalViewController,
let presentedModalWrapper =
RNIModalViewControllerWrapperRegistry.get(forViewController: presentedVC) {

let presentedModal = RNIModalManager.getPresentedModal(
forPresentingViewController: self,
presentedViewController: presentedVC
);

return RNIModalFlagsShared.shouldSwizzledViewControllerNotifyAll
? presentedModal
: presentedModal as? RNIModalViewControllerWrapper;
};

private func registerOrInitialize(
_ viewControllerToPresent: UIViewController
){
let presentingWrapper = Self.registerIfNeeded(forViewController: self);

presentingWrapper?.modalViewController = viewControllerToPresent;
presentingWrapper?.presentingViewController = self;

let presentedWrapper =
Self.registerIfNeeded(forViewController: viewControllerToPresent);

/// D - Current vc instance has a `RNIModalViewControllerWrapper`
/// instance associated to it.
return presentedModalWrapper;
};
presentedWrapper?.presentingViewController = self;
};


private func notifyOnModalWillDismiss() -> (() -> Void)? {
guard let presentedModal = self.getPresentedModalToNotify()
else { return nil };

return nil;
presentedModal.notifyWillDismiss();

return {
if presentedModal.computedIsModalInFocus {
presentedModal.notifyDidPresent();

} else {
presentedModal.notifyDidDismiss();
};
};
};

// MARK: - Swizzled Functions
// --------------------------

@objc fileprivate func _swizzled_present(
_ viewControllerToPresent: UIViewController,
animated flag: Bool,
Expand All @@ -116,15 +158,10 @@ extension UIViewController {
);
#endif

self.registerIfNeeded(viewControllerToPresent: viewControllerToPresent);

let presentedModal =
self.getPresentedModal(viewControllerToPresent: viewControllerToPresent);
self.registerOrInitialize(viewControllerToPresent);

if let presentedModal = presentedModal {
presentedModal.modalPresentationNotificationDelegate
.notifyOnModalWillShow(sender: presentedModal);
};
let presentedModal = self.getPresentedModalToNotify(viewControllerToPresent);
presentedModal?.notifyWillPresent();

// call original impl.
self._swizzled_present(viewControllerToPresent, animated: flag) {
Expand All @@ -136,11 +173,7 @@ extension UIViewController {
);
#endif

if let presentedModal = presentedModal {
presentedModal.modalPresentationNotificationDelegate
.notifyOnModalDidShow(sender: presentedModal);
};

presentedModal?.notifyDidPresent();
completion?();
};
};
Expand All @@ -158,12 +191,7 @@ extension UIViewController {
);
#endif

let presentedModal = self.getPresentedModal();

if let presentedModal = presentedModal {
presentedModal.modalPresentationNotificationDelegate
.notifyOnModalWillHide(sender: presentedModal);
};
let notifyOnModalDidDismiss = self.notifyOnModalWillDismiss();

// call original impl.
self._swizzled_dismiss(animated: flag) {
Expand All @@ -175,37 +203,33 @@ extension UIViewController {
);
#endif

if let presentedModal = presentedModal {
presentedModal.modalPresentationNotificationDelegate
.notifyOnModalDidHide(sender: presentedModal);
};

notifyOnModalDidDismiss?();
completion?();
};
};
};

// MARK: - Extensions - Helpers
// ----------------------------

fileprivate extension RNIModalPresentationNotifying where Self: RNIModal {
func notifyWillPresent() {
guard let delegate = modalPresentationNotificationDelegate else { return };
delegate.notifyOnModalWillShow(sender: self);
};

internal static func swizzleMethods() {
guard RNIModalFlagsShared.shouldSwizzleViewControllers else { return };

#if DEBUG
print(
"Log - UIViewController+Swizzling"
+ " - UIViewController.swizzleMethods invoked"
);
#endif

RNIUtilities.swizzleExchangeMethods(
defaultSelector: #selector(Self.present(_:animated:completion:)),
newSelector: #selector(Self._swizzled_present(_:animated:completion:)),
forClass: UIViewController.self
);

RNIUtilities.swizzleExchangeMethods(
defaultSelector: #selector(Self.dismiss(animated:completion:)),
newSelector: #selector(Self._swizzled_dismiss(animated:completion:)),
forClass: UIViewController.self
);

self.isSwizzled.toggle();
func notifyDidPresent(){
guard let delegate = modalPresentationNotificationDelegate else { return };
delegate.notifyOnModalDidShow(sender: self);
};

func notifyWillDismiss(){
guard let delegate = modalPresentationNotificationDelegate else { return };
delegate.notifyOnModalWillHide(sender: self);
};

func notifyDidDismiss(){
guard let delegate = modalPresentationNotificationDelegate else { return };
delegate.notifyOnModalDidHide(sender: self);
};
};

0 comments on commit a2bc44f

Please sign in to comment.