iOS和React互相通信&&iOS获取网络信号质量值

Wifi信号值可以正常获取,SIM卡的信号值是通过私有API获取的,如果正常使用,App Store恐怕会拒绝上架。

一,React To iOS
1,新建一个继承于NSObject的类,.m文件需要导入两个RN框架

#import 
#import 
@interface RNCalliOSAction ()
@end

2,在.m文件实现

@implementation RNCalliOSAction
/*
 一旦RCT_EXPORT_MODULE();声明该类是EXPORT_MODULE,那么该类的实例已经创建好了.如果你在其他地方创建这个类的实例(alloc 或 new),会导致,RN不能正确识别该类的实例.
 */
RCT_EXPORT_MODULE();

注意RCT_EXPORT_MODULE()括号内如果为空,默认就是当前类名。可以根据自己需求,填写在RN代码中接收的对象名称

3,接收方法
第一种没有回调的

RCT_EXPORT_METHOD(calliOSActionWithObject:(id)object) {
  NSLog(@"%@", object);
}

第二种有回调的

RCT_EXPORT_METHOD(calliOSActionWithCallBack:(RCTResponseSenderBlock)callBack) {
  NSString *string=@"hello";
  NSArray *array=@[@"RN",@"and",@"iOS"];
  NSString *end=@"goodbay";
  //更多参数放到数组中进行回调
  callBack(@[string,array,end]);
}

4,RN中的实现

let RNCalliOSAction = NativeModules.RNCalliOSAction
RNCalliOSAction.calliOSActionWithParams('Hello iOS')

复制过去,即可默认导入“NativeModules”
注意NativeModules.RNCalliOSAction中的“RNCalliOSAction”就是接收RN消息的iOS文件类名,如果在2中括号内有填写,那么用括号内填写的名称。

二,iOS To React
过时的方法我们不讲,我介绍Xcode中不报警告的写法。
第一步获取网络信号质量值并且主动发出通知传递出去。
.h文件。

#import 
#define TYZ_NETWORK_ADDRESS                       @"www.apple.com"
/**
 *  控制信号值类型的数值
 */
#define WIFIValueLow           -80
#define WIFIValueHigh          -60
#define SIMValueLow            20
#define SIMValueHigh           60
#define NetTool [TYZNetworkingTool initial]
/**
 *  网络连接类型
 */
typedef NS_ENUM(NSInteger, NetworkType) {
    NetworkType_None = 0,
    NetworkType_Wifi = 1,
    NetworkType_2G   = 2,
    NetworkType_3G   = 3,
    NetworkType_4G   = 4,
    NetworkType_5G   = 5,  // 5G网络还未定义
};
/**
 *  信号强度类型
 */
typedef NS_ENUM(NSInteger, NetworkIntensity) {
    NetworkIntensity_Default = 0, // 没有信号
    NetworkIntensity_Low     = 1, // wifi:<-80 SIM:<20
    NetworkIntensity_Mid     = 2, // wifi:-80~-70 SIM:20~60
    NetworkIntensity_High    = 3, // wifi:>-70 SIM:>60
};
@interface TYZNetworkingTool : NSObject
/**
 *  网络连接类型
 */
@property(nonatomic, assign) NetworkType networkType;
/**
 *  信号强度类型
 */
@property(nonatomic, assign) NetworkIntensity networkIntensity;
/**
 *  初始化
 */
+ (instancetype)initial;
@end

.m文件

#import "TYZNetworkingTool.h"
#import 
#import 
#import "Reachability.h"
#import "RNCalliOSAction.h"
#import "iOSCallRNAction.h"
@interface TYZNetworkingTool ()
@property (nonatomic, strong) Reachability *reachability;
#warning 测试用
@property(nonatomic, assign) NSInteger count;
@end

@implementation TYZNetworkingTool
+ (instancetype)initial {
  static dispatch_once_t onceToken;
  static TYZNetworkingTool *instance = nil;
  dispatch_once(&onceToken,^{
    instance = [[TYZNetworkingTool alloc] init];
    [instance monitorNetworkState];
  });
  return instance;
}
- (void)monitorNetworkState {
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:)name:kReachabilityChangedNotification object: nil];
  NSString *remoteHostName = TYZ_NETWORK_ADDRESS;
  self.reachability = [Reachability reachabilityWithHostName:remoteHostName];
  [self.reachability startNotifier];
}
- (void)reachabilityChanged:(NSNotification *)notification{
  Reachability *curReach = [notification object];
  NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
  NetworkStatus status = [curReach currentReachabilityStatus];
  switch (status) {
    case NotReachable:
      NSLog(@"====当前网络不可达=======");
      self.networkType = NetworkType_None;
      break;
    default:
      NSLog(@"====当前网络连接中=======");
      self.networkType = [self networkStateType];
      break;
  }
}
- (NetworkType)networkStateType {
  UIApplication *app = [UIApplication sharedApplication];
  NSArray *subviews = [[[app valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];
  for (id subview in subviews) {
    if ([subview isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {
      int networkType = [[subview valueForKeyPath:@"dataNetworkType"] intValue];
      self.networkIntensity = [self getSignalLevelWithType:networkType subview:subview];
      switch (networkType) {
        case 1:
          return NetworkType_2G;
        case 2:
          return NetworkType_3G;
        case 3:
          return NetworkType_4G;
        case 5:
          return NetworkType_Wifi;
        default:
          return NetworkType_None;
      }
    }
  }
  return NetworkType_None;
}
- (NetworkIntensity)getSignalLevelWithType:(NSInteger)networkType subview:(NSDictionary *)subview {
  if (networkType == 0) {
    return NetworkIntensity_Default;
  } else if (networkType == 5) {
    int networkIntensity = [[subview valueForKeyPath:@"wifiStrengthRaw"] intValue];
    NSLog(@"networkIntensity: %d", networkIntensity);
    if (networkIntensity > WIFIValueHigh) {
      return NetworkIntensity_High;
    } else if (networkIntensity > WIFIValueLow) {
      return NetworkIntensity_Mid;
    } else {
      return NetworkIntensity_Low;
    }
  } else {
    // 此API是apple私有API,所以只可运用在越狱设备中,如果提交appstore,会遭遇apple的拒绝上架反馈!
    void *libHandle = dlopen("/System/Library/Frameworks/CoreTelephony.framework/CoreTelephony",RTLD_LAZY);//获取库句柄
    int (*CTGetSignalStrength)(void); //定义一个与将要获取的函数匹配的函数指针
    CTGetSignalStrength = (int(*)(void))dlsym(libHandle,"CTGetSignalStrength"); //获取指定名称的函数
    if(CTGetSignalStrength == NULL) {
      return NetworkIntensity_Default;
    } else {
      int level = CTGetSignalStrength();
      NSLog(@"level: %d", level);
      dlclose(libHandle); //切记关闭库
      if (level > SIMValueHigh) {
        return NetworkIntensity_High;
      } else if (level > SIMValueLow) {
        return NetworkIntensity_Mid;
      } else {
        return NetworkIntensity_Low;
      }
    }
  }
}
- (void)setNetworkType:(NetworkType)networkType {
  _networkType = networkType;
  self.count = 0;
}
- (void)setCount:(NSInteger)count {
  _count = count;
  if (count % 2 == 0) {
    [CallRN callToReactWithNameNumber:1 body:@{@"key" : @"Hello React"}];
  } else {
    [self sendReactWithNetworkStateChanged];
  }
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    self.count ++;
  });
}
- (void)sendReactWithNetworkStateChanged {
  NSDictionary *dict = @{
                         @"NETWORKTYPE" : @(self.networkType),
                         @"SIGNALLEVEL" : @(self.networkIntensity)
                         };
  [self sendDataToReactWithBody:dict];
}
- (void)sendDataToReactWithBody:(NSDictionary *)body {
  [CallRN callToReactWithNameNumber:0 body:body];
}
@end

在这里已经向React发出两个传值的通知了。当self.count为偶数时发出一个测试用的通知,当self.count为奇数时,向React传递了当前的网络信号类型和信号值类型。React接收到以后,就可以直接处理了。

下面一步一步来处理发送和接收的核心
1,iOS端发送
.h文件。发送的通知类型只能是既定的宏,这些通知名称会被加入一个数组中。
“TYZSingleton.h”是单例宏文件

#import 
#import "TYZSingleton.h"
#define CallRN [iOSCallRNAction sharediOSCallRNAction]
/**
 *  以下宏即是所有发出的通知名称
 */
#define CallToRNNotication1 @"NetworkState"
#define CallToRNNotication2 @"notification2"
#define CallToRNNotication3 @"notification3"
#define CallToRNNotication4 @"notification4"
@interface iOSCallRNAction : RCTEventEmitter
TYZSingleto_h(iOSCallRNAction)
/**
 向React发送通知

 @param number 第几个通知
 @param body 参数
 */
- (void)callToReactWithNameNumber:(NSInteger)number body:(NSDictionary *)body;
@end

.m文件

#import "iOSCallRNAction.h"

@interface iOSCallRNAction ()
/**
 *  通知连接第几个
 */
@property(nonatomic, assign) NSInteger number;
/**
 *  通知连接类型
 */
@property(nonatomic, strong) NSArray *reactNotifications;
@end

@implementation iOSCallRNAction
TYZSingleto_m(iOSCallRNAction)
- (NSArray *)reactNotifications {
  if (_reactNotifications == nil) {
    _reactNotifications = @[
                            CallToRNNotication1,
                            CallToRNNotication2,
                            CallToRNNotication3,
                            CallToRNNotication4
                            ];
  }
  return _reactNotifications;
}
RCT_EXPORT_MODULE();
- (NSArray *)supportedEvents {
  return self.reactNotifications;
}
- (void)startObserving {
  [self.reactNotifications enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(emitEventInternal:) name:obj object:nil];
  }];
}
- (void)stopObserving {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)emitEventInternal:(NSNotification *)notification {
  [self sendEventWithName:self.reactNotifications[self.number] body:notification.object];
}
- (void)callToReactWithNameNumber:(NSInteger)number body:(NSDictionary *)body {
  if (number < self.reactNotifications.count) {
    self.number = number;
    [[NSNotificationCenter defaultCenter] postNotificationName:self.reactNotifications[self.number] object:body];
  }
}
@end

已经写成了单例“- (void)stopObserving”不起作用

2,React端接收

const nativeBridge = NativeModules.iOSCallRNAction;
const NativeModule = new NativeEventEmitter(nativeBridge);

复制过去,会默认导入“NativeModules”和“NativeEventEmitter”
在需要处理的地方接收传递过来的值

componentDidMount() {
        let RNCalliOSAction = NativeModules.RNCalliOSAction
        RNCalliOSAction.calliOSActionWithParams('Hello iOS')

this.listener1 = NativeModule.addListener('NetworkState', (data)=>{
            console.log('Hello RN:'+ data.NETWORKTYPE + ' ' + data.SIGNALLEVEL)
        })
        this.listener2 = NativeModule.addListener('notification2', (data)=>{
            console.log('Hello RN:'+ data.key)
        })
}

其他人介绍一种更简便的方法“[self.bridge.eventDispatcher sendAppEventWithName:@"getSelectDate" body:@{@"SelectDate":str_date}]”Xcode报警告,而且调用不灵活。经过我上面的封装,任何地方都可以调用传值的方法,不会对其它功能造成影响。

你可能感兴趣的:(iOS和React互相通信&&iOS获取网络信号质量值)