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.