How to use view created in swift inside an objective-c++ file?

Viewed 307

I am trying to use react-native's new architecture and I have enabled fabric for the same. I can use a view created in objective-c++ inside my typescript code. But I want to use a view created in swift with UIKit in my objective-c++ file

The documentation on react-native's website is still for the old architecture of using bridge so it does not directly apply in my case. There are pretty significant changes in the new architecture.

Below is the working code for the objective-c++ file

    #import <Foundation/Foundation.h>
    #import "RNThubView.h"
    
    #import <react/renderer/components/RNThubViewSpec/ComponentDescriptors.h>
    #import <react/renderer/components/RNThubViewSpec/EventEmitters.h>
    #import <react/renderer/components/RNThubViewSpec/Props.h>
    #import <react/renderer/components/RNThubViewSpec/RCTComponentViewHelpers.h>
    
    #import "RCTFabric/React-RCTFabric-umbrella.h"
    
    
    
    using namespace facebook::react;
    
    @interface RNThubView () <RCTThubViewViewProtocol>
    
    @end
    
    @implementation RNThubView {
      UIView *_view;
      UILabel *_label;
    }
    
    + (ComponentDescriptorProvider)componentDescriptorProvider
    {
      return concreteComponentDescriptorProvider<ThubViewComponentDescriptor>();
    }
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
      if (self = [super initWithFrame:frame]) {
        static const auto defaultProps = std::make_shared<const ThubViewProps>();
        
        _props = defaultProps;
        
        
        _rnThubView = [[RNThubView alloc] init];
        _view = [[UIView alloc] init];
        
        _label = [[UILabel alloc] init];
        _label.text = @"Initial value";
        [_view addSubview:_label];
        
        _label.translatesAutoresizingMaskIntoConstraints = false;
        [NSLayoutConstraint activateConstraints:@[
          [_label.leadingAnchor constraintEqualToAnchor:_view.leadingAnchor],
          [_label.topAnchor constraintEqualToAnchor:_view.topAnchor],
          [_label.trailingAnchor constraintEqualToAnchor:_view.trailingAnchor],
          [_label.bottomAnchor constraintEqualToAnchor:_view.bottomAnchor],
        ]];
        _label.textAlignment = NSTextAlignmentCenter;
        
        self.contentView = _view;
    
        
      }
      
      return self;
    }
    
    - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
    {
      const auto &oldViewProps = *std::static_pointer_cast<ThubViewProps const>(_props);
      const auto &newViewProps = *std::static_pointer_cast<ThubViewProps const>(props);
      
      if (oldViewProps.color != newViewProps.color) {
        //    NSString * colorToConvert = [[NSString alloc] initWithUTF8String: newViewProps.color.c_str()];
        //    [_view setBackgroundColor:[self hexStringToColor:colorToConvert]];
        //    _view.backgroundColor = [UIColor redColor];
      }
      
      [super updateProps:props oldProps:oldProps];
    }
    
    Class<RCTComponentViewProtocol> ThubViewCls(void)
    {
      return RNThubView.class;
    }
    
    - hexStringToColor:(NSString *)stringToConvert
    {
      NSString *noHashString = [stringToConvert stringByReplacingOccurrencesOfString:@"#" withString:@""];
      NSScanner *stringScanner = [NSScanner scannerWithString:noHashString];
      
      unsigned hex;
      if (![stringScanner scanHexInt:&hex]) return nil;
      int r = (hex >> 16) & 0xFF;
      int g = (hex >> 8) & 0xFF;
      int b = (hex) & 0xFF;
      
      return [UIColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:1.0f];
    }
    
    @end

Now to use a view which is created in swift inside my objective-c++ file, I use the following approach

Created a swift file called RNThubView. Following is the code for the same

@objcMembers class RNThubView: UIView {
  let btnLogin:UIButton = {
      let btn = UIButton(type:.system)
      btn.backgroundColor = .blue
      btn.setTitle("Login", for: .normal)
      btn.tintColor = .white
      btn.layer.cornerRadius = 5
      btn.clipsToBounds = true
      btn.translatesAutoresizingMaskIntoConstraints = false
      return btn
  }()
  
  
  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }
  
  override init(frame: CGRect) {
    super.init(frame: frame)
    self.addSubview(btnLogin)
    addConstraintsToButtonLogin()
  }
  
  func addConstraintsToButtonLogin(){
    btnLogin.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
      btnLogin.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
      btnLogin.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
      btnLogin.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
  }
  
  
}

And I updated my objective-c++ file code to the following

- (instancetype)initWithFrame:(CGRect)frame
{
  if (self = [super initWithFrame:frame]) {
    static const auto defaultProps = std::make_shared<const ThubViewProps>();
    
    _props = defaultProps;
     
    
    _rnThubView = [[RNThubView alloc] init];
    self.contentView = _rnThubView
   .....
......

The rest of the code remains mostly the same in the objective-c++ file.

Now when I run via cli, the app crashes with no logs on cli. When I run via Xcode, I get the following logs with a white screen on the simulator

flipper: FlipperClient::addPlugin Inspector
flipper: FlipperClient::addPlugin Preferences
flipper: FlipperClient::addPlugin React
flipper: FlipperClient::addPlugin Network
2022-07-04 19:16:34.262238+0530 FabricSwift[51914:596543] [connection] nw_socket_handle_socket_event [C1.1:1] Socket SO_ERROR [61: Connection refused]
2022-07-04 19:16:34.290263+0530 FabricSwift[51914:596543] [connection] nw_socket_handle_socket_event [C1.2:1] Socket SO_ERROR [61: Connection refused]
2022-07-04 19:16:34.293022+0530 FabricSwift[51914:596537] [connection] nw_connection_get_connected_socket [C1] Client called nw_connection_get_connected_socket on unconnected nw_connection
2022-07-04 19:16:34.296327+0530 FabricSwift[51914:596537] TCP Conn 0x600001130370 Failed : error 0:61 [61]
2022-07-04 19:16:36.557475+0530 FabricSwift[51914:596543] [connection] nw_socket_handle_socket_event [C5.1:1] Socket SO_ERROR [61: Connection refused]
2022-07-04 19:16:36.558768+0530 FabricSwift[51914:596543] [connection] nw_socket_handle_socket_event [C5.2:1] Socket SO_ERROR [61: Connection refused]
2022-07-04 19:16:36.559075+0530 FabricSwift[51914:596538] [connection] nw_connection_get_connected_socket [C5] Client called nw_connection_get_connected_socket on unconnected nw_connection
2022-07-04 19:16:36.559146+0530 FabricSwift[51914:596538] TCP Conn 0x600001104840 Failed : error 0:61 [61]
2022-07-04 19:16:36.728528+0530 FabricSwift[51914:596578] [javascript] Running "FabricSwift" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1}
2022-07-04 19:16:37.065773+0530 FabricSwift[51914:596539] [connection] nw_socket_handle_socket_event [C9.1:1] Socket SO_ERROR [61: Connection refused]
2022-07-04 19:16:37.066881+0530 FabricSwift[51914:596539] [connection] nw_socket_handle_socket_event [C9.2:1] Socket SO_ERROR [61: Connection refused]
2022-07-04 19:16:37.067294+0530 FabricSwift[51914:596539] [connection] nw_connection_get_connected_socket [C9] Client called nw_connection_get_connected_socket on unconnected nw_connection
2022-07-04 19:16:37.067425+0530 FabricSwift[51914:596539] TCP Conn 0x6000011370d0 Failed : error 0:61 [61]
assertion failed [thread_context != nullptr]: got an exception for an unknown thread
(ExceptionServer.cpp:809 handle_exception)
Message from debugger: Xcode has killed the LLDB RPC server to allow the debugger to detach from your process. You may need to manually terminate your process.

The full source is here https://github.com/PritishSawant/ReactNativeFabricSwiftTry => This works but everything is in objective-c++ and the usage of swift is commented out.

https://github.com/PritishSawant/ReactNativeFabricSwiftTry/tree/swift-try => This contains what I have tried doing with swift and objective-c++ code resulting in the above crash which I mentioned

Also how to pass props from objective-c++ to swift. In my example it is a color prop which once set from the react-native's side should use the value in my swift file.

1 Answers

I do this all the time. This is completely possible. You need to include the class type in the header file you are trying to use the code in. Something like this:

//
//  SomeVC.h
//  MyProjectApp
//
//  Copyright Something. All rights reserved.
//
    
#import "SomethingNotRelated1.h"
#import "SomethingNotRelated2.h"
#import "SomethingNotRelated3.h"

#define DEFAULT_SOMETHING_NOT_RELATED_TEXT NSLocalizedString(@"Insert SOMETHING RANDOM...", nil)

@class SomeRandomKeyboardView; //THIS IS THE IMPORTANT LINE. DO IT LIKE THIS.

and the class

@objcMembers class SomeRandomKeyboardView: UIToolbar { //FOR THIS CLASS
...
}
Related