版本
本文已 ReactNative 集成 NIM_iOS_Demo_v4.2.0 為例。Xcode
版本為9.0
本人是在已有的ReactNative(以下簡稱RN)工程下集成云信IM(其他各版本集成方式大同小異)
1. 使用 cocoapods 來安裝網(wǎng)易云信依賴
1.首先在 terminal 里進(jìn)入到自己RN項目ios目錄下喝滞, 運行 pod init
,(如果電腦沒安裝cocoapods的坛缕,請先安裝 cocoapods )推励,運行完成后,會在當(dāng)前所在目錄下生成 Podfile 文件夸溶。
- 下載云信 IM demo 源碼
前往 網(wǎng)易云信 下載iOS版 云信IM demo
2. 在 Podfile 里添加網(wǎng)易云信的依賴
terminal 進(jìn)入到 RN ios目錄下執(zhí)行 pod init吴旋,執(zhí)行后在 ios 目錄下生成 Podfile 文件损肛。
在下載好的云信demo里 NIMDemo 目錄下找到 Podfile 文件,復(fù)制文件里面的內(nèi)容到自己的 Podfile 文件里邮府。此部完成后的結(jié)果如下
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
# Mall為 RN 工程名字
workspace 'Mall.xcworkspace'
target 'Mall' do
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
pod 'React', :path => '../node_modules/react-native'
pod 'SDWebImage', '4.0.0'
pod 'Toast', '~> 3.1.0'
pod 'M80AttributedLabel', '~> 1.6.3'
pod 'TZImagePickerController', '~> 1.9.0'
pod 'FMDB', '~> 2.7.2'
pod 'Reachability', '~> 3.2'
pod 'CocoaLumberjack', '~> 3.2.1'
pod 'SSZipArchive', '~> 1.8.1'
pod 'SVProgressHUD', '~> 2.0.3'
# 網(wǎng)易云信的 NIMKit 包
pod 'NIMKit/Full', '~> 1.9.1'
end
此處和源碼里的podfile內(nèi)容有點不一致荧关,如果不太清楚各項配置的意思,按照我的配置來就行了褂傀。
- 添加完成后在 terminal 窗口里(Podfile 目錄中)執(zhí)行
pod install
,執(zhí)行完成后會在ios
目錄下生成對應(yīng)的Mall.xcworkspace
文件加勤,以后使用Xcode打開項目就是雙擊此文件即可仙辟。
3. 拷貝IM源碼到RN里
- 將demo中的 NIMDemo 目錄下的 Classes 和 Supporting Files 拷貝到項目的 ios/Mall 目錄下 (與Images.xcassets文件夾同級)
將 Supporting Files 目錄下的
Info.plist
nim_debug.xcconfig
nim_release.xcconfig
和main.m
文件刪除將
NTESAppDelegate.m
內(nèi)容和自己工程中的AppDelegate.m
文件中的代碼進(jìn)行合并,并修改和去掉部分不需要的代碼鳄梅。
如下代碼是我的AppDelegate.m
文件的內(nèi)容
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "NTESLoginViewController.h"
#import "UIView+Toast.h"
#import "NTESService.h"
#import "NTESNotificationCenter.h"
#import "NTESLogManager.h"
#import "NTESDemoConfig.h"
#import "NTESSessionUtil.h"
#import "NTESMainTabController.h"
#import "NTESLoginManager.h"
#import "NTESCustomAttachmentDecoder.h"
#import "NTESClientUtil.h"
#import "NTESNotificationCenter.h"
#import "NIMKit.h"
#import "NTESSDKConfigDelegate.h"
#import "NTESCellLayoutConfig.h"
#import "NTESSubscribeManager.h"
#import "NTESRedPacketManager.h"
#import "NTESBundleSetting.h"
@import PushKit;
NSString *NTESNotificationLogout = @"NTESNotificationLogout";
@interface AppDelegate ()<NIMLoginManagerDelegate,PKPushRegistryDelegate>
@property (nonatomic,strong) NTESSDKConfigDelegate *sdkConfigDelegate;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"Mall"
initialProperties:nil
launchOptions:launchOptions];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
[self initYunXin];
return YES;
}
- (void)initYunXin
{
[self setupNIMSDK];
[self setupServices];
[self registerPushService];
[self commonInitListenEvents];
// 因為本項目想實現(xiàn)的功能是啟動進(jìn)入的是 RN 界面叠国,在需要的時候,從 RN 頁面跳轉(zhuǎn)到原生 iOS 界面
//self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//self.window.backgroundColor = [UIColor grayColor];
//[self.window makeKeyAndVisible];
//[application setStatusBarStyle:UIStatusBarStyleLightContent];
//[self setupMainViewController];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[[NIMSDK sharedSDK] loginManager] removeDelegate:self];
}
#pragma mark - ApplicationDelegate
- (void)applicationWillResignActive:(UIApplication *)application {
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSInteger count = [[[NIMSDK sharedSDK] conversationManager] allUnreadCount];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:count];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
}
- (void)applicationWillTerminate:(UIApplication *)application {
}
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[[NIMSDK sharedSDK] updateApnsToken:deviceToken];
DDLogInfo(@"didRegisterForRemoteNotificationsWithDeviceToken: %@", deviceToken);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
DDLogInfo(@"receive remote notification: %@", userInfo);
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
DDLogError(@"fail to get apns token :%@",error);
}
#pragma mark PKPushRegistryDelegate
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
if ([type isEqualToString:PKPushTypeVoIP])
{
[[NIMSDK sharedSDK] updatePushKitToken:credentials.token];
}
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
DDLogInfo(@"receive payload %@ type %@",payload.dictionaryPayload,type);
NSNumber *badge = payload.dictionaryPayload[@"aps"][@"badge"];
if ([badge isKindOfClass:[NSNumber class]])
{
[UIApplication sharedApplication].applicationIconBadgeNumber = [badge integerValue];
}
}
- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(NSString *)type
{
DDLogInfo(@"registry %@ invalidate %@",registry,type);
}
#pragma mark - openURL
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[[NTESRedPacketManager sharedManager] application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
return YES;
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{
[[NTESRedPacketManager sharedManager] application:app openURL:url options:options];
return YES;
}
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
//目前只有紅包跳轉(zhuǎn)
return [[NTESRedPacketManager sharedManager] application:application handleOpenURL:url];
}
#pragma mark - misc
- (void)registerPushService
{
//apns
[[UIApplication sharedApplication] registerForRemoteNotifications];
UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types
categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
//pushkit
PKPushRegistry *pushRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
pushRegistry.delegate = self;
pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}
//- (void)setupMainViewController
//{
// LoginData *data = [[NTESLoginManager sharedManager] currentLoginData];
// NSString *account = [data account];
// NSString *token = [data token];
//
// //如果有緩存用戶名密碼推薦使用自動登錄
// if ([account length] && [token length])
// {
// NIMAutoLoginData *loginData = [[NIMAutoLoginData alloc] init];
// loginData.account = account;
// loginData.token = token;
//
// [[[NIMSDK sharedSDK] loginManager] autoLogin:loginData];
// [[NTESServiceManager sharedManager] start];
// NTESMainTabController *mainTab = [[NTESMainTabController alloc] initWithNibName:nil bundle:nil];
// self.window.rootViewController = mainTab;
// }
// else
// {
// [self setupLoginViewController];
// }
//}
- (void)commonInitListenEvents
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(logout:)
name:NTESNotificationLogout
object:nil];
[[[NIMSDK sharedSDK] loginManager] addDelegate:self];
}
//- (void)setupLoginViewController
//{
// [self.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
// NTESLoginViewController *loginController = [[NTESLoginViewController alloc] init];
// UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:loginController];
// self.window.rootViewController = nav;
//}
#pragma mark - 注銷
-(void)logout:(NSNotification *)note
{
[self doLogout];
}
- (void)doLogout
{
[[NTESLoginManager sharedManager] setCurrentLoginData:nil];
[[NTESServiceManager sharedManager] destory];
// [self setupLoginViewController];
}
#pragma NIMLoginManagerDelegate
-(void)onKick:(NIMKickReason)code clientType:(NIMLoginClientType)clientType
{
NSString *reason = @"你被踢下線";
switch (code) {
case NIMKickReasonByClient:
case NIMKickReasonByClientManually:{
NSString *clientName = [NTESClientUtil clientName:clientType];
reason = clientName.length ? [NSString stringWithFormat:@"你的帳號被%@端踢出下線戴尸,請注意帳號信息安全",clientName] : @"你的帳號被踢出下線粟焊,請注意帳號信息安全";
break;
}
case NIMKickReasonByServer:
reason = @"你被服務(wù)器踢下線";
break;
default:
break;
}
[[[NIMSDK sharedSDK] loginManager] logout:^(NSError *error) {
[[NSNotificationCenter defaultCenter] postNotificationName:NTESNotificationLogout object:nil];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"下線通知" message:reason delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
[alert show];
}];
}
- (void)onAutoLoginFailed:(NSError *)error
{
//只有連接發(fā)生嚴(yán)重錯誤才會走這個回調(diào),在這個回調(diào)里應(yīng)該登出,返回界面等待用戶手動重新登錄项棠。
DDLogInfo(@"onAutoLoginFailed %zd",error.code);
[self showAutoLoginErrorAlert:error];
}
#pragma mark - logic impl
- (void)setupServices
{
[[NTESLogManager sharedManager] start];
[[NTESNotificationCenter sharedCenter] start];
[[NTESSubscribeManager sharedManager] start];
[[NTESRedPacketManager sharedManager] start];
}
- (void)setupNIMSDK
{
//在注冊 NIMSDK appKey 之前先進(jìn)行配置信息的注冊悲雳,如是否使用新路徑,是否要忽略某些通知,是否需要多端同步未讀數(shù)等
self.sdkConfigDelegate = [[NTESSDKConfigDelegate alloc] init];
[[NIMSDKConfig sharedConfig] setDelegate:self.sdkConfigDelegate];
[[NIMSDKConfig sharedConfig] setShouldSyncUnreadCount:YES];
[[NIMSDKConfig sharedConfig] setMaxAutoLoginRetryTimes:10];
[[NIMSDKConfig sharedConfig] setMaximumLogDays:[[NTESBundleSetting sharedConfig] maximumLogDays]];
[[NIMSDKConfig sharedConfig] setShouldCountTeamNotification:[[NTESBundleSetting sharedConfig] countTeamNotification]];
//appkey 是應(yīng)用的標(biāo)識香追,不同應(yīng)用之間的數(shù)據(jù)(用戶合瓢、消息、群組等)是完全隔離的透典。
//如需打網(wǎng)易云信 Demo 包晴楔,請勿修改 appkey ,開發(fā)自己的應(yīng)用時峭咒,請?zhí)鎿Q為自己的 appkey 税弃。
//并請對應(yīng)更換 Demo 代碼中的獲取好友列表、個人信息等網(wǎng)易云信 SDK 未提供的接口凑队。
NSString *appKey = [[NTESDemoConfig sharedConfig] appKey];
NIMSDKOption *option = [NIMSDKOption optionWithAppKey:appKey];
option.apnsCername = [[NTESDemoConfig sharedConfig] apnsCername];
option.pkCername = [[NTESDemoConfig sharedConfig] pkCername];
[[NIMSDK sharedSDK] registerWithOption:option];
//注冊自定義消息的解析器
[NIMCustomObject registerCustomDecoder:[NTESCustomAttachmentDecoder new]];
//注冊 NIMKit 自定義排版配置
[[NIMKit sharedKit] registerLayoutConfig:[NTESCellLayoutConfig new]];
}
#pragma mark - 登錄錯誤回調(diào)
- (void)showAutoLoginErrorAlert:(NSError *)error
{
NSString *message = [NTESSessionUtil formatAutoLoginMessage:error];
UIAlertController *vc = [UIAlertController alertControllerWithTitle:@"自動登錄失敗"
message:message
preferredStyle:UIAlertControllerStyleAlert];
if ([error.domain isEqualToString:NIMLocalErrorDomain] &&
error.code == NIMLocalErrorCodeAutoLoginRetryLimit)
{
UIAlertAction *retryAction = [UIAlertAction actionWithTitle:@"重試"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
LoginData *data = [[NTESLoginManager sharedManager] currentLoginData];
NSString *account = [data account];
NSString *token = [data token];
if ([account length] && [token length])
{
NIMAutoLoginData *loginData = [[NIMAutoLoginData alloc] init];
loginData.account = account;
loginData.token = token;
[[[NIMSDK sharedSDK] loginManager] autoLogin:loginData];
}
}];
[vc addAction:retryAction];
}
UIAlertAction *logoutAction = [UIAlertAction actionWithTitle:@"注銷"
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * _Nonnull action) {
[[[NIMSDK sharedSDK] loginManager] logout:^(NSError *error) {
[[NSNotificationCenter defaultCenter] postNotificationName:NTESNotificationLogout object:nil];
}];
}];
[vc addAction:logoutAction];
[self.window.rootViewController presentViewController:vc
animated:YES
completion:nil];
}
@end
4. Xcode中設(shè)置文件引用
- Xcode左邊導(dǎo)航中點擊項目鹅搪,Build Settings --> Prefix Header 的值設(shè)置為
$(PROJECT_DIR)/Mall/NIMDemo-Prefix.pch
- 打開云信demo NIM.xcworkspace 查看云信的 Header Search Paths 配置踱讨,照貓畫虎的搬到自己項目里來。
記得把
$(SRCROOT)/../NIMKit/NIMKit
這項給去掉,因為在前面我們把NIMKit使用 cocoapods 安裝好了溺拱,不用像demo里一樣把 NIMKit 這個文件夾復(fù)制下來。使用 cocoapods 來安裝哥攘,非常方便日后的升級和版本變更针史。
- 將demo中的
Images.xcassets
與自己工程中的Images.xcassets
合并
5. Link Binary With Libraries
- libPods-yuexing-NIMKit.a
- libc++.tbd
- lib.tbd
- libsqlite3.0.tbd
- VideoToolbox.framework
- CoreMedia.framework
- AudioToolbox.framework
- CoreLocation.framework
- MapKit.framework
- AVFoundation.framework
- MobileCoreServices.framework
()
上述操作完成后赋访,記得 Clean(快捷鍵
command
+shift
+k
) 一下可都,然后運行試試。出現(xiàn)錯誤可嘗試刪除ios
目錄下的Pods
Podfile.lock
和Mall.xcworkspace
蚓耽,然后在terminal
中的項目ios
目錄下運行pod install
重新安裝下渠牲。如出現(xiàn)其他錯誤,根據(jù)錯誤提示修改或Google解決步悠。
此步完成后签杈,基本上可以正常運行起來了。集成配置也基本完成鼎兽,后續(xù)的步驟主要是對接 RN 界面和 原生界面互相跳轉(zhuǎn)答姥。
6. 原生添加 navigation 方便后面RN界面和原生界面跳轉(zhuǎn)
- 項目正常運行后需要修改幾個地方,比如從 ReactNative 頁面跳轉(zhuǎn)到 原生頁面的適配問題等谚咬。
- 在
AppDelegate.h
文件中聲明變量 navigation
@property (nonatomic, strong) UINavigationController *navigation;
- 修改
AppDelegate.m
文件中didFinishLaunchingWithOptions
代碼
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 此處省略其他代碼
RootViewController *rootViewController = [RootViewController new];
//UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
// 初始化 navigation
self.navigation = [[UINavigationController alloc] init];
// 將 RN 界面 push 進(jìn)來
[self.navigation pushViewController:rootView.reactViewController animated:YES];
[self.window addSubview:self.navigation.view];
// 隱藏界面的 Header
[self.navigation setNavigationBarHidden:YES];
[self.window setRootViewController:self.navigation];
[self.window makeKeyAndVisible];
// ...
return YES;
}
7. 創(chuàng)建輔助類鹦付,用來提供給RN調(diào)用原生方法
- 在Xcode中創(chuàng)建一個名為
RN2Native
(類名自己定) 的CocoaTouchClass
類,類繼承至UIViewController
择卦,創(chuàng)建并添加代碼后如下
RN2Native.h
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface RN2Native : RCTEventEmitter<RCTBridgeModule>
/**
* 接收消息
*/
- (void)receiveMessage:(NSDictionary *)obj;
@end
RN2Native.m
#import "RN2Native.h"
#import <NIMSDK/NIMSDK.h>
#import <SVProgressHUD/SVProgressHUD.h>
#import "NTESLoginManager.h"
#import "NTESService.h"
#import "NTESMainTabController.h"
#import "NTESSessionViewController.h"
@implementation RN2Native
// 檢測 js 端是否有監(jiān)聽事件
// 更多詳情請參考 http://facebook.github.io/react-native/docs/native-modules-ios.html#optimizing-for-zero-listeners
bool hasListeners;
// 將當(dāng)前類設(shè)置為主線程
- (dispatch_queue_t)methodQueue {
return dispatch_get_main_queue();
}
// 導(dǎo)出模塊給 js
RCT_EXPORT_MODULE()
// 導(dǎo)出要發(fā)送給js端的方法
- (NSArray<NSString *> *) supportedEvents {
return @[@"receiveMessage"];
}
// 跳到原生IM界面
RCT_EXPORT_METHOD(toYunXinIM) {
[[NTESServiceManager sharedManager] start];
NTESMainTabController *mainTab = [[NTESMainTabController alloc] initWithNibName:nil bundle:nil];
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UINavigationController *navigation = ((UINavigationController *) window.rootViewController);
[navigation pushViewController:mainTab animated:YES];
}
// 登錄云信 IM
RCT_EXPORT_METHOD(login:(NSString *)account password:(NSString *)password resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
[[[NIMSDK sharedSDK] loginManager]
login:account
token:password
completion:^(NSError *error) {
[SVProgressHUD dismiss];
if (error == nil) {
LoginData *sdkData = [[LoginData alloc] init];
sdkData.account = account;
sdkData.token = password;
[[NTESLoginManager sharedManager] setCurrentLoginData:sdkData];
NSInteger count = [[NIMSDK sharedSDK].conversationManager allUnreadCount];
NSLog(@"云信ios版[%@]登錄成功敲长,未讀消息數(shù) %ld", account, count);
NSDictionary *dic = @{
@"code": @"200",
@"unreadCount": [NSString stringWithFormat:@"%ld", count]
};
resolve(dic);
} else {
NSString *errorCode = [NSString stringWithFormat:@"%ld", error.code];
NSLog(@"登錄失敗郎嫁,錯誤碼 %@", errorCode);
reject(errorCode, @"", error);
}
}
];
}
// 發(fā)起一對一聊天
RCT_EXPORT_METHOD(toP2PChat:(NSString *)userId) {
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UINavigationController *navigation = ((UINavigationController *) window.rootViewController);
NIMSession *session = [NIMSession session:userId type:NIMSessionTypeP2P];
NTESSessionViewController *vc = [[NTESSessionViewController alloc] initWithSession:session];
[navigation pushViewController:vc animated:YES];
}
// 發(fā)送消息
RCT_EXPORT_METHOD(chatWithCS:(NSString *)csID message:(NSString *)textMsg) {
// 構(gòu)造消息
NIMMessage *message = [[NIMMessage alloc] init];
message.text = textMsg;
// 構(gòu)造會話
NIMSession *session = [NIMSession session:csID type:NIMSessionTypeP2P];
// 發(fā)送消息
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:nil];
}
// 發(fā)送 tip 提醒給指定用戶
RCT_EXPORT_METHOD(p2pTipMsg:(NSString *)userId message:(NSString *)textMsg) {
//構(gòu)造消息
NIMTipObject *tipObject = [[NIMTipObject alloc] init];
NIMMessage *message = [[NIMMessage alloc] init];
message.messageObject = tipObject;
message.text = textMsg;
//構(gòu)造會話
NIMSession *session = [NIMSession session:userId type:NIMSessionTypeP2P];
//發(fā)送消息
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:nil];
}
// 退出云信 IM
RCT_EXPORT_METHOD(logout) {
[[[NIMSDK sharedSDK] loginManager] logout:^(NSError *error)
{
NSLog(@"退出云信ios版");
// extern NSString *NTESNotificationLogout;
// [[NSNotificationCenter defaultCenter] postNotificationName:NTESNotificationLogout object:nil];
}
];
}
// 發(fā)送事件 receiveMessage 給 js
- (void)receiveMessage:(NSDictionary *)obj {
if (self.bridge == nil) {
NSLog(@"bridge is null %@", self.bridge);
} else {
NSLog(@"bridge has value %@", self.bridge);
}
if (hasListeners) {
NSLog(@"React Native 端有監(jiān)聽函數(shù),此處應(yīng)該 sendEventWithName %@", obj);
[self sendEventWithName:@"receiveMessage" body:obj];
}
}
// 獲取未讀消息數(shù)量
RCT_EXPORT_METHOD(fetchUnreadMessage) {
// js端有監(jiān)聽函數(shù)時才執(zhí)行下面的代碼
if (hasListeners) {
NSInteger count = [[NIMSDK sharedSDK].conversationManager allUnreadCount];
NSString *countStr = [NSString stringWithFormat:@"%ld", count];
// NSLog(@"未讀消息數(shù) %@", countStr);
[self sendEventWithName:@"receiveMessage" body:@{@"unreadCount": countStr}];
}
}
// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}
// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}
@end
上述代碼中導(dǎo)出給js端的方法根據(jù)業(yè)務(wù)需要進(jìn)行取舍祈噪,改動原生代碼需要重新 run 一下項目
此時可以在 RN 里寫跳轉(zhuǎn)到 原生 IM 界面的方法了泽铛。
8. 修改云信 Demo 中的 appKey
修改 Classes/Util/NTESDemoConfig.m 第30行 _appKey
,值為你在云信官網(wǎng)注冊的钳降,如下圖所示
修改成自己的 App Key 后厚宰,表示將云信用戶管理,消息記錄管理等接入到自己的后臺遂填,還需修改一個地方铲觉,(如果你的項目中云信登錄密碼沒使用md5加密,則不需要進(jìn)行這一步操作)吓坚。
上述操作完成后撵幽,然后我們在 RN 里調(diào)用我們在 RN2Native.m 文件里暴露給 js 端的方法,示例如下
// 調(diào)用原生暴露給js的登錄方法
NativeModules.RN2Native.login(yunxinId, yunxingToken)
.then(data => {
console.log(`返回結(jié)果`, data);
});
// 跳轉(zhuǎn)到 IM 界面
NativeModules.RN2Native.toYunXinIM();
// 發(fā)送 tip 消息給指定用戶
NativeModules.RN2Native.p2pTipMsg('p153760', '咨詢');
// 發(fā)起一對一的聊天窗口
NativeModules.RN2Native.toP2PChat(yunxinId);
// ...
此處有兩個要提的就是礁击,當(dāng)我們從 RN 跳轉(zhuǎn)到原生 IM 的時候盐杂,出現(xiàn)的問題,問題如下圖所示哆窿。
情形1:沒有返回到RN界面的按鈕
分析:
此界面是從 RN 界面跳轉(zhuǎn)過來的链烈,若此時我們想回到原來的RN界面,怎么辦呢挚躯?
別著急强衡,下面就是在此界面上添加一個返回的按鈕,返回到我們原來的RN界面码荔。
編輯文件Classes/Sections/SessionList/ViewController/NTESSessionListViewController.m
- (void)viewDidLoad{
// ...
[self setUpNavItem];
}
- (void)setUpNavItem{
// 設(shè)置左邊返回到 react native 界面的返回按鈕(icon_back_normal.png在images.xcassets中)
UIImage *backImage = [UIImage imageNamed:@"icon_back_normal.png"];
UIBarButtonItem *barBackButton = [[UIBarButtonItem alloc]
initWithImage :backImage
style :UIBarButtonItemStylePlain
target :self
action :@selector(backAction:)];
self.navigationItem.leftBarButtonItem = barBackButton;
// 右邊按鈕
// ...
}
// 返回到上一個視圖
- (void)backAction:(id)sender {
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UINavigationController *navigation = (UINavigationController *)window.rootViewController;
[navigation popViewControllerAnimated:YES];
}
添加完再次運行之后漩勤,從 RN 界面跳轉(zhuǎn)到原生IM界面時,界面如下
情形2:發(fā)起一對一聊天時缩搅,無法返回到原界面
上述情況需要修改 Classes/Sections/Session/ViewController/NTESSesionViewController.m
文件。
- 在聲明變量的地方加個變量硼瓣,用于保存當(dāng)前聊天頁面是從哪個界面跳轉(zhuǎn)過來的究飞。
// 記錄前一個頁面name
@property (nonatomic,strong) NSString *preViewCtrlName;
- 新增/編輯
- (void)viewWillAppear:(BOOL)animated
函數(shù),代碼如下
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[self navigationController] setNavigationBarHidden:NO animated:animated];
// 獲取 UIViewController 總數(shù)量
NSInteger count = [[[self navigationController] viewControllers] count];
// 根據(jù)索引獲取前一個 UIViewController
UIViewController *vc = [self.navigationController.viewControllers objectAtIndex:count - 2];
// 獲取類名
self.preViewCtrlName = NSStringFromClass([vc class]);
// 隱藏"返回"按鈕上顯示未讀消息數(shù)量
if ([@"UIViewController" isEqualToString:self.preViewCtrlName]) {
[[self navigationItem] setLeftBarButtonItem:Nil];
}
}
- 編輯
- (void)viewWillDisappear:(BOOL)animated
函數(shù)巨双,代碼如下
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[[NIMSDK sharedSDK].mediaManager stopRecord];
[[NIMSDK sharedSDK].mediaManager stopPlay];
if ([@"UIViewController" isEqualToString:self.preViewCtrlName]) {
[[self navigationController] setNavigationBarHidden:YES animated:animated];
} else {
[[self navigationController] setNavigationBarHidden:NO animated:animated];
}
}
到此處基本已經(jīng)告一段落了噪猾。
屏蔽或刪除掉不需要的功能,減少打包app的大小
刪除云信里的 關(guān)于 頁面相關(guān)文件筑累,刪除目錄
Classes/Sections/Settings
下的NTESAboutViewController.h
,NTESAboutViewController.m
,NTESAboutViewController.xib
文件。刪除文件夾
Classes/Sections/Settings/NetDetect
刪除文件夾
Classes/Sections/Settings/Log
刪除文件夾
Classes/Sections/Login/ViewController
刪除/注銷
Classes/Sections/Setting/NTESSetingViewController.m
中的
同時記得把他們相應(yīng)調(diào)用的函數(shù)也刪掉
@{
Title :@"查看日志",
CellAction :@"onTouchShowLog:",
},
@{
Title :@"上傳日志",
CellAction :@"onTouchUploadLog:",
},
@{
Title :@"音視頻網(wǎng)絡(luò)探測",
CellAction :@"onTouchNetDetect:",
},
//...
@{
HeaderTitle:@"",
RowContent :@[
@{
Title : @"注銷",
CellClass : @"NTESColorButtonCell",
CellAction : @"logoutCurrentAccount:",
ExtraInfo : @(ColorButtonCellStyleRed),
ForbidSelect : @(YES)
},
],
FooterTitle:@"",
},
搜索“云信 Demo”替換為你的app名稱丝蹭。
Classes/Common/Controller/NTESMainTabController.m 241行慢宗,將"云信"改成"消息"
注釋/刪除掉直播間功能,
Classes/Common/Controller/NTESMainTabController.m
中的以下代碼
//...
#define TabBarCount 4 // 將4改為3
//...
@(NTESMainTabTypeChatroomList): @{
TabbarVC : @"NTESChatroomListViewController",
TabbarTitle : @"直播間",
TabbarImage : @"icon_chatroom_normal",
TabbarSelectedImage: @"icon_chatroom_pressed",
},
刪除 Images.xcassets 里一些沒用用到的圖片 (刪除前先搜索下是否還有其他地方引用)
上述刪掉的代碼,在其他地方有引用镜沽,記得也需要刪除/注釋
修改 Classes/Util/NTESNotificationCenter.m 文件敏晤,修改后大致如下
下面這段代碼的用途是,當(dāng)云信收到消息時缅茉,將獲取未讀消息總數(shù)嘴脾,然后將獲取到的未讀消息總數(shù)發(fā)送給js端。js端需要監(jiān)聽事件蔬墩,根據(jù)獲取到的未讀消息數(shù)去更新頁面顯示的未讀消息數(shù)译打。
#import "RN2Native.h"
//...
#pragma mark - NIMChatManagerDelegate
- (void)onRecvMessages:(NSArray *)messages
{
static BOOL isPlaying = NO;
if (isPlaying) {
return;
}
isPlaying = YES;
[self playMessageAudioTip];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
isPlaying = NO;
});
[self checkMessageAt:messages];
// 獲取未讀消息總數(shù)
NSInteger count = [[NIMSDK sharedSDK].conversationManager allUnreadCount];
NSString *countStr = [NSString stringWithFormat:@"%ld", count];
[[[RN2Native alloc] init] receiveMessage:@{@"unreadCount": countStr}];
}
本文章會持續(xù)更新,有需要的可以關(guān)注下拇颅。^ - ^