Skip to content

Commit

Permalink
⭐️ Impl: Add - Import RNIUtilities
Browse files Browse the repository at this point in the history
TODO:2023-03-04-05-20-26 - Library Native Cleanup
  • Loading branch information
dominicstop committed Mar 3, 2023
1 parent 356890d commit adddb4a
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 4 deletions.
11 changes: 7 additions & 4 deletions ios/src_library/IosModal-Bridging-Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

#import "React Native/RCTSwiftLog/RCTSwiftLog.h"

#import <React/RCTUIManager.h>
#import <React/RCTTouchHandler.h>
#import <React/RCTLog.h>
#import "React/RCTBridgeModule.h"
#import <React/RCTBridge.h>
#import "React/RCTEventEmitter.h"

#import <React/RCTView.h>
#import <React/RCTTouchHandler.h>

#import <React/RCTUIManager.h>
#import <React/RCTUIManagerUtils.h>

164 changes: 164 additions & 0 deletions ios/src_library/RNIUtilities/RNIUtilities.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
//
// RNIUtilities.swift
// IosNavigatorExample
//
// Created by Dominic Go on 1/9/21.
//

import Foundation;


internal class RNIUtilities {

static weak var sharedBridge: RCTBridge?;

static let osVersion = ProcessInfo().operatingSystemVersion;

/// If you remove a "react view" from the view hierarchy (e.g. via
/// `removeFromSuperview`), it won't be released, because it's being retained
/// by the `_viewRegistry` ivar in the shared `UIManager` (singleton) instance.
///
/// The `_viewRegistry` keeps a ref. to all of the "react views" in the app.
/// This explains how you can get a ref. to a view via `viewForReactTag` and
/// `viewForNativeID` (which also means that `reactTag`/`node` is just an index,
/// i.e. it's just a number that gets inc. every-time a "react view" is added).
///
/// If you are **absolutely sure** that a particular `reactView` is no longer
/// being used, this helper func. will remove `reactView` (and all of it's
/// subviews) in the `_viewRegistry`.
static func recursivelyRemoveFromViewRegistry(bridge: RCTBridge, reactView: UIView) {

func getRegistry(forKey key: String) -> NSMutableDictionary? {
return bridge.uiManager?.value(forKey: key) as? NSMutableDictionary;
};

/// Get a ref to the `_viewRegistry` ivar in the `RCTUIManager` instance.
/// * Note: Unlike objc properties, ivars are "private" so they aren't
/// automagically exposed/bridged to swift.
/// * Note: key: `NSNumber` (the `reactTag`), and value: `UIView`
guard let viewRegistry = getRegistry(forKey: "_viewRegistry")
else { return };

/// The "react tags" of the views that were removed
var removedViewTags: [NSNumber] = [];

func removeView(_ v: UIView){
/// if this really is a "react view" then it should have a `reactTag`
if let reactTag = v.reactTag,
viewRegistry[reactTag] != nil {

removedViewTags.append(reactTag);

/// remove from view hierarchy
v.removeFromSuperview();

/// remove this "react view" from the registry
viewRegistry.removeObject(forKey: reactTag);
};

/// remove other subviews...
v.subviews.forEach {
removeView($0);
};

/// remove other react subviews...
v.reactSubviews()?.forEach {
removeView($0);
};
};

func removeShadowViews(){
/// Get a ref to the `_shadowViewRegistry` ivar in the `RCTUIManager` instance.
/// Note: Execute on "RCT thread" (i.e. "com.facebook.react.ShadowQueue")
guard let shadowViewRegistry = getRegistry(forKey: "_shadowViewRegistry")
else { return };

for reactTag in removedViewTags {
shadowViewRegistry.removeObject(forKey: reactTag);
};
};

DispatchQueue.main.async {
// start recursively removing views...
removeView(reactView);

// remove shadow views...
RCTExecuteOnUIManagerQueue {
removeShadowViews();
};
};
};

/// Recursive climb the responder chain until `T` is found.
/// Useful for finding the corresponding view controller of a view.
static func getParent<T>(responder: UIResponder, type: T.Type) -> T? {
var parentResponder: UIResponder? = responder;

while parentResponder != nil {
parentResponder = parentResponder?.next;

if let parent = parentResponder as? T {
return parent;
};
};

return nil;
};

static func getView<T>(
forNode node: NSNumber,
type: T.Type,
bridge: RCTBridge?
) -> T? {
guard let bridge = bridge,
let view = bridge.uiManager?.view(forReactTag: node)
else { return nil };

return view as? T;
};

static func recursivelyGetAllSubviews(for view: UIView) -> [UIView] {
var views: [UIView] = [];

for subview in view.subviews {
views += Self.recursivelyGetAllSubviews(for: subview);
views.append(subview);
};

return views;
};

static func recursivelyGetAllSuperViews(for view: UIView) -> [UIView] {
var views: [UIView] = [];

if let parentView = view.superview {
views.append(parentView);
views += Self.recursivelyGetAllSuperViews(for: parentView);
};

return views;
};

static func compareImages(_ a: UIImage?, _ b: UIImage?) -> Bool {
if (a == nil && b == nil){
// both are nil, equal
return true;

} else if a == nil || b == nil {
// one is nil, not equal
return false;

} else if a == b {
// same ref to the object, true
return true;

} else if a!.size != b!.size {
// size diff, not equal
return false;
};

// compare raw data
return a!.isEqual(b!);
};
};

14 changes: 14 additions & 0 deletions ios/src_library/RNIUtilities/RNIUtilitiesModule.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// RNIUtilitiesModule.m
// react-native-ios-context-menu
//
// Created by Dominic Go on 9/27/22.
//

#import "React/RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(RNIUtilitiesModule, NSObject)

RCT_EXTERN_METHOD(initialize:(NSDictionary *)params);

@end
28 changes: 28 additions & 0 deletions ios/src_library/RNIUtilities/RNIUtilitiesModule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// RNIUtilitiesModule.swift
// react-native-ios-context-menu
//
// Created by Dominic Go on 9/27/22.
//

import Foundation


@objc(RNIUtilitiesModule)
internal class RNIUtilitiesModule: NSObject {

@objc var bridge: RCTBridge! {
willSet {
RNIUtilities.sharedBridge = newValue;
}
};

@objc static func requiresMainQueueSetup() -> Bool {
// run init in bg thread
return false;
};

@objc func initialize(_ params: NSDictionary){
// no-op
};
};

0 comments on commit adddb4a

Please sign in to comment.