facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.76k stars 24.29k forks source link

iOS Native Module not working #31787

Closed padapada09 closed 3 years ago

padapada09 commented 3 years ago

Description

So I'am trying to implement the Garmin iOS SDK in a native module to connect a Garmin watch to my react native app. I have done all what the docs say for creating a native module and I'am able to initialize and start scanning (this does not throw any errors and it prompts the user to enable bluetooth so it's definitely doing something). But the method of the class I have to implement that says didScanDevice is not getting call. Again, without throwing any error.

My question is, could there be some problem with Native Modules that is not allowing my class that implements the Garmin SDK to execute properly.

I know I didn't give much context, but is there anything you can thin off that could help me find the bug?

React Native version:

0.64.1

padapada09 commented 3 years ago

The Native module is implemented as follows:

//AppDelegate.m
#import "AppDelegate.h"
#import <companion/companion.h>
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"swc"
                                            initialProperties:nil];

  if (@available(iOS 13.0, *)) {
      rootView.backgroundColor = [UIColor systemBackgroundColor];
  } else {
      rootView.backgroundColor = [UIColor whiteColor];
  }

  GHInitializer *initializer = [GHInitializer sharedManager];
  [initializer initializeLicense:@"SDK_KEY"];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

@end
//RNGarmin.swift
import Foundation
import companion

@objc(RNGarmin)
class RNGarmin: NSObject, GHDeviceConnectionDelegate, GHScanDelegate, GHPairingDelegate {

  public var supportedDevices: [NSNumber] = [NSNumber(value: GHDeviceTypes.venu.rawValue), NSNumber(value: GHDeviceTypes.venu2.rawValue), NSNumber(value: GHDeviceTypes.venu2s.rawValue), NSNumber(value: GHDeviceTypes.venuSq.rawValue), NSNumber(value: GHDeviceTypes.vivoactive3.rawValue), NSNumber(value: GHDeviceTypes.vivoactive3m.rawValue), NSNumber(value: GHDeviceTypes.vivoactive4.rawValue), NSNumber(value: GHDeviceTypes.vivoactive4Legacy.rawValue), NSNumber(value: GHDeviceTypes.vivoactive4s.rawValue), NSNumber(value: GHDeviceTypes.vivoactiveHr.rawValue), NSNumber(value: GHDeviceTypes.vivosmart3.rawValue), NSNumber(value: GHDeviceTypes.vivosmart4.rawValue), NSNumber(value: GHDeviceTypes.vivosmartHr.rawValue), NSNumber(value: GHDeviceTypes.vivomove3.rawValue), NSNumber(value: GHDeviceTypes.vivomove3s.rawValue), NSNumber(value: GHDeviceTypes.vivomoveHr.rawValue), NSNumber(value: GHDeviceTypes.vivomoveLuxe.rawValue), NSNumber(value: GHDeviceTypes.vivomoveStyle.rawValue), NSNumber(value: GHDeviceTypes.vivosport.rawValue), NSNumber(value: GHDeviceTypes.fenix5.rawValue), NSNumber(value: GHDeviceTypes.fenix5s.rawValue), NSNumber(value: GHDeviceTypes.fenix5x.rawValue), NSNumber(value: GHDeviceTypes.fenix5Plus.rawValue), NSNumber(value: GHDeviceTypes.fenix5sPlus.rawValue), NSNumber(value: GHDeviceTypes.fenix5xPlus.rawValue), NSNumber(value: GHDeviceTypes.fenix6.rawValue), NSNumber(value: GHDeviceTypes.fenix6Pro.rawValue), NSNumber(value: GHDeviceTypes.fenix6ProSolar.rawValue), NSNumber(value: GHDeviceTypes.fenix6s.rawValue), NSNumber(value: GHDeviceTypes.fenix6sPro.rawValue), NSNumber(value: GHDeviceTypes.fenix6xPro.rawValue), NSNumber(value: GHDeviceTypes.forerunner245.rawValue), NSNumber(value: GHDeviceTypes.forerunner245m.rawValue), NSNumber(value: GHDeviceTypes.forerunner645.rawValue), NSNumber(value: GHDeviceTypes.forerunner645m.rawValue), NSNumber(value: GHDeviceTypes.forerunner945.rawValue), NSNumber(value: GHDeviceTypes.forerunner945lte.rawValue), NSNumber(value: GHDeviceTypes.instinct.rawValue), NSNumber(value: GHDeviceTypes.lily.rawValue), NSNumber(value: GHDeviceTypes.tactixCharlie.rawValue), NSNumber(value: GHDeviceTypes.tactixDelta.rawValue), NSNumber(value: GHDeviceTypes.marqDriver.rawValue), NSNumber(value: GHDeviceTypes.marqAviator.rawValue), NSNumber(value: GHDeviceTypes.marqCaptain.rawValue), NSNumber(value: GHDeviceTypes.marqExpedition.rawValue), NSNumber(value: GHDeviceTypes.marqAthlete.rawValue), NSNumber(value: GHDeviceTypes.marqCommander.rawValue)]

  @objc
  func startScanning() -> Void {
    GHDeviceManager.shared().add(self);
    GHDeviceManager.shared().setScannerDelegate(self);
    GHDeviceManager.shared().scan(forDevices: self.supportedDevices);
    print("Started scanning");
  }

  @objc
  func stopScanning() -> Void {
    GHDeviceManager.shared().stopScan();
    print("Scanning ended");
  }

  func didConnect(_ device: GHRealTimeDevice) {
    print("didConnect");
  }

  func didDisconnectDevice(_ device: GHRealTimeDevice) {
    print("didConnect");
  }

  func didScanDevice(_ device: GHScannedDevice) {
    print("didConnect");
  }

  func didPairDevice(_ device: GHRealTimeDevice) {
    print("didConnect");
  }

  func didFail(toPairDevice deviceId: UUID, error: Error) {
    print("didConnect");
  }

  func didPausePairing(_ completion: GHPairingCompletion) {
    print("didConnect");
  }

  func scanDidFailWithError(_ error: Error) {
      print("scanDidFailWithError")
      print(error)
  }
}
//RNGarmin.m
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_MODULE(RNGarmin, NSObject)

RCT_EXTERN_METHOD(startScanning)
RCT_EXTERN_METHOD(stopScanning)

@end
//swc-Bridging-Header.h
#import <React/RCTBridgeModule.h>
padapada09 commented 3 years ago

The output for running the app and calling

useEffect(() => {
    RNGarmin.startScanning();
}, []);

is:

2021-06-29 09:07:37.366641-0300 swc[2573:736288] Connection 1: received failure notification
2021-06-29 09:07:37.366723-0300 swc[2573:736288] Connection 1: failed to connect 1:50, reason -1
2021-06-29 09:07:37.366757-0300 swc[2573:736288] Connection 1: encountered error(1:50)
2021-06-29 09:07:37.368547-0300 swc[2573:736284] Task <C9118789-39B4-49D1-8BFA-783C2867AAD9>.<1> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2021-06-29 09:07:37.373417-0300 swc[2573:736288] Task <C9118789-39B4-49D1-8BFA-783C2867AAD9>.<1> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x280ad0540 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <C9118789-39B4-49D1-8BFA-783C2867AAD9>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <C9118789-39B4-49D1-8BFA-783C2867AAD9>.<1>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=http://192.168.2.119:8081/status, NSErrorFailingURLKey=http://192.168.2.119:8081/status, _kCFStreamErrorDomainKey=1}
2021-06-29 09:07:37.377921-0300 swc[2573:736116] [native] Module RNGarmin requires main queue setup since it overrides `init` but doesn't implement `requiresMainQueueSetup`. In a future release React Native will default to initializing all native modules on a background thread unless explicitly opted-out of.
2021-06-29 09:07:37.384174-0300 swc[2573:736116] [native] Running application swc ({
    initialProps =     {
    };
    rootTag = 1;
})
2021-06-29 09:07:37.412353-0300 swc[2573:736284] Connection 2: received failure notification
2021-06-29 09:07:37.412389-0300 swc[2573:736284] Connection 2: failed to connect 1:50, reason -1
2021-06-29 09:07:37.412410-0300 swc[2573:736284] Connection 2: encountered error(1:50)
2021-06-29 09:07:37.413011-0300 swc[2573:736286] Task <1FC30C05-7B91-4BC1-97DB-348216E88FE8>.<2> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2021-06-29 09:07:37.413253-0300 swc[2573:736284] Task <1FC30C05-7B91-4BC1-97DB-348216E88FE8>.<2> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x280ae6220 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <1FC30C05-7B91-4BC1-97DB-348216E88FE8>.<2>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <1FC30C05-7B91-4BC1-97DB-348216E88FE8>.<2>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=http://192.168.2.119:8081/status, NSErrorFailingURLKey=http://192.168.2.119:8081/status, _kCFStreamErrorDomainKey=1}
2021-06-29 09:07:37.524201-0300 swc[2573:736284] [connection] nw_socket_handle_socket_event [C3.1:1] Socket SO_ERROR [61: Connection refused]
2021-06-29 09:07:37.524375-0300 swc[2573:736284] [connection] nw_socket_handle_socket_event [C4.1:1] Socket SO_ERROR [61: Connection refused]
2021-06-29 09:07:37.525439-0300 swc[2573:736284] [connection] nw_socket_handle_socket_event [C3.2:1] Socket SO_ERROR [61: Connection refused]
2021-06-29 09:07:37.525566-0300 swc[2573:736284] [connection] nw_socket_handle_socket_event [C4.2:1] Socket SO_ERROR [61: Connection refused]
2021-06-29 09:07:37.525849-0300 swc[2573:736286] [connection] nw_connection_get_connected_socket [C3] Client called nw_connection_get_connected_socket on unconnected nw_connection
2021-06-29 09:07:37.525934-0300 swc[2573:736286] TCP Conn 0x283388580 Failed : error 0:61 [61]
2021-06-29 09:07:37.526277-0300 swc[2573:736286] [connection] nw_connection_get_connected_socket [C4] Client called nw_connection_get_connected_socket on unconnected nw_connection
2021-06-29 09:07:37.526464-0300 swc[2573:736286] TCP Conn 0x28338c9a0 Failed : error 0:61 [61]
2021-06-29 09:07:37.684259-0300 swc[2573:736290] [javascript] Running "swc" with {"rootTag":1,"initialProps":{}}
2021-06-29 09:07:37.687782-0300 swc[2573:736283] [connection] nw_socket_handle_socket_event [C5.1:1] Socket SO_ERROR [61: Connection refused]
2021-06-29 09:07:37.688130-0300 swc[2573:736283] [connection] nw_socket_handle_socket_event [C5.2:1] Socket SO_ERROR [61: Connection refused]
2021-06-29 09:07:37.688593-0300 swc[2573:736284] [connection] nw_connection_get_connected_socket [C5] Client called nw_connection_get_connected_socket on unconnected nw_connection
2021-06-29 09:07:37.688724-0300 swc[2573:736284] TCP Conn 0x283388580 Failed : error 0:61 [61]
2021-06-29 09:07:37.786838-0300 swc[2573:736116] [native] [GESTURE HANDLER] Initialize gesture handler for root view <RCTRootContentView: 0x15de0dba0; reactTag: 1; frame = (0 0; 390 844); gestureRecognizers = <NSArray: 0x280adf510>; layer = <CALayer: 0x2804b31c0>>
Started scanning
padapada09 commented 3 years ago

So I finally solved it, at the end I realized the Garmin module had to run on the main thread (not exactly sure why it is that way but since the module is privare and I can only see the interfaces of each module I'am not expecting to get more information).

Once I told react native to run my module on the main thread it started working fine.