IOS集成阿里百川IM

这篇文章没什么技术含量,只是普通的集成技巧,希望能让有集成阿里百川IM的同学少踩一点坑。为什么是阿里百川,以为他有一个很好用的客服客户端。

阿里百川集成

准备工作

  • 官方有比较详细的前后端集成文档,接入之前多读几遍。《官方说明文档》

  • 下载官方Demo,Demo中有一个更加详细的文档,在Doc目录下面,可以看一下。官方Demo下载地址

  • 运行官方的DEMO,测试一下基本的通讯功能。

替换工作

  • AppKey,在控制台中新建自己的应用会生成AppKey,在Demo中需要替换。

  • 安全图片,yw_1222.jpg,在控制台中》安全图片获取中,根据自己的AppId获取安全图片,在项目中替换。此处要注意的是版本,我以为下载的最新版本的SDK应该对应的是V5图片,其实是V4,在安全图片获取那里有说明的。安全图片版本说明

  • 推送证书名,如果通讯模块需要推送功能,那么需要在控制台,IM应用管理后台上传证书,证书的名字在客户端中需要修改。

  • 客服名,如果程序中用到客服功能,客服端使用的是千牛,客户端中需要将客服号改成自己注册的牵牛号码。

CheckList

说一下集成IM通讯功能的必要条件(包含通讯、客服、推送):

  • appstore账号,注册BundleID、开发证书、授权文件、推送证书。为什么要注册账号,因为这里如果使用到推送功能,需要用到推送证书。

  • 客服注册,如果是电商平台,需要一个多人的客服团队,用阿里百川还是比较不错的,因为它的客户端比较好用,集成过程

  • 应用注册,在控制台新建自己的应用。

  • 客户端瘦身,因为官方的Demo功能比较齐全,但说句实在话虽然代码注释的很不错,但代码写的还是有点乱。对于一个强迫症的我来说,实在受不了,所以我在原基础作为参考,重写了所有的方法。

代码

SPKit.h

//
//  SPKit.h
//  IMV1
//
//  Created by DaLei on 2017/6/30.
//  Copyright © 2017年 DaLei. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <WXOpenIMSDKFMWK/YWFMWK.h>
#import <WXOUIModule/YWUIFMWK.h>

//日志打印
#ifdef DEBUG
#define DLog(...) NSLog(@"%s 第%d行 \n %@\n\n",__func__,__LINE__,[NSString stringWithFormat:__VA_ARGS__])
#else
#define DLog(...)
#endif


#define im_AppKey @"24526749"
#define im_CerName @"sanddamowang"
#define im_Service @"面皮大师001"


@interface SPKit : NSObject

@property (nonatomic, readonly) id<UIApplicationDelegate> appDelegate;
@property (nonatomic, readonly) UIWindow *rootWindow;
@property (strong, nonatomic, readwrite) YWIMKit *ywIMKit;
@property (nonatomic, assign) YWIMConnectionStatus lastConnectionStatus;//IM长连接状态

/**
 单利方法
 @return 单利对象
 */
+ (instancetype)sharedInstance;

#pragma mark - 使用下面三个函数即可完成从程序启动到登录再到登出的完整流程

/**
 初始化入口函数
 程序完成启动,在appdelegate中的 application:didFinishLaunchingWithOptions:一开始的地方调用
 */
- (void)callThisInDidFinishLaunching;

/**
 登录入口函数
 用户在应用的服务器登录成功之后,向云旺服务器登录之前调用
 @param ywLoginId 用来登录云旺IMSDK的id
 @param passWord 用来登录云旺IMSDK的密码
 @param aPreloginedBlock 预登录回调
 @param aSuccessBlock 登陆成功的回调
 @param aFailedBlock 登录失败的回调
 */
- (void)callThisAfterISVAccountLoginSuccessWithYWLoginId:(NSString *)ywLoginId passWord:(NSString *)passWord preloginedBlock:(void(^)())aPreloginedBlock successBlock:(void(^)())aSuccessBlock failedBlock:(void (^)(NSError *))aFailedBlock;

/**
 登出入口函数
 用户即将退出登录时调用
 */
- (void)callThisBeforeISVAccountLogout;


#pragma mark - UI pages

/**
 创建会话列表页面
 */
- (YWConversationListViewController *)spMakeConversationListControllerWithSelectItemBlock:(YWConversationsListDidSelectItemBlock)aSelectItemBlock;

/**
 打开某个会话
 */
- (void)spOpenConversationViewControllerWithConversation:(YWConversation *)aConversation fromNavigationController:(UINavigationController *)aNavigationController;

/**
 打开单聊页面
 */
- (void)spOpenConversationViewControllerWithPerson:(YWPerson *)aPerson fromNavigationController:(UINavigationController *)aNavigationController;

/**
 打开客服会话
 @param aPersonId 客服Id
 */
- (void)spOpenEServiceConversationWithPersonId:(NSString *)aPersonId fromNavigationController:(UINavigationController *)aNavigationController;

/**
 创建某个会话Controller
 */
- (YWConversationViewController *)spMakeConversationViewControllerWithConversation:(YWConversation *)aConversation;


@end

SPKit.m

//
//  SPKit.m
//  IMV1
//
//  Created by DaLei on 2017/6/30.
//  Copyright © 2017年 DaLei. All rights reserved.
//

#import "SPKit.h"
#import <WXOUIModule/YWIndicator.h>
#import <AVFoundation/AVFoundation.h>

@interface SPKit()<YWMessageLifeDelegate>

@end


@implementation SPKit

- (id)init {
    self = [super init];
    if (self) {
        //初始化登陆状态
        [self setLastConnectionStatus:YWIMConnectionStatusDisconnected];
    }
    return self;
}

+ (instancetype)sharedInstance {
    static SPKit *spKit = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        spKit = [[SPKit alloc] init];
    });
    return spKit;
}



#pragma mark - properties

- (id<UIApplicationDelegate>)appDelegate {
    return [UIApplication sharedApplication].delegate;
}

- (UIWindow *)rootWindow {
    UIWindow *result = nil;
    do {
        if ([self.appDelegate respondsToSelector:@selector(window)]) {
            result = [self.appDelegate window];
        }
        if (result) {
            break;
        }
    } while (NO);
    return result;
}

- (UINavigationController *)conversationNavigationController {
    UINavigationController *navigationController = (UINavigationController *)self.rootWindow.rootViewController;
    if (![navigationController isKindOfClass:[UINavigationController class]]) {
        return nil;
    }
    return navigationController;
}

#pragma mark - 使用下面三个函数即可完成从程序启动到登录再到登出的完整流程

/**
 初始化入口函数
 程序完成启动,在appdelegate中的 application:didFinishLaunchingWithOptions:一开始的地方调用
 */
- (void)callThisInDidFinishLaunching{
    
    //证书名
    [[[YWAPI sharedInstance] getGlobalPushService] setXPushCertName:im_CerName];
    
    if ([self spInit]) {
        //监听消息生命周期回调
        [self spListenMyMessageLife];
        DLog(@"初始化成功");
        
        //可在此处添加APNS、头像样式、消息生命周期、导航栏样式等。
        [self spHandleAPNSPush];
        
    } else {
        DLog(@"初始化失败");
    }
}

/**
 登录入口函数
 用户在应用的服务器登录成功之后,向云旺服务器登录之前调用
 @param ywLoginId 用来登录云旺IMSDK的id
 @param passWord 用来登录云旺IMSDK的密码
 @param aPreloginedBlock 预登录回调
 @param aSuccessBlock 登陆成功的回调
 @param aFailedBlock 登录失败的回调
 */
- (void)callThisAfterISVAccountLoginSuccessWithYWLoginId:(NSString *)ywLoginId passWord:(NSString *)passWord preloginedBlock:(void(^)())aPreloginedBlock successBlock:(void(^)())aSuccessBlock failedBlock:(void (^)(NSError *))aFailedBlock{
    
    //监听连接状态
    [self spListenConnectionStatus];
    
    //设置声音播放模式
    [self spSetAudioCategory];
    
    //设置头像和昵称
    [self spSetProfile];
    
    //监听新消息
    [self spListenNewMessage];
    
    //设置提示
    [self spSetNotificationBlock];
    
    if ([ywLoginId length] > 0 && [passWord length] > 0) {
        //预登陆
        [self spPreLoginWithLoginId:ywLoginId successBlock:aPreloginedBlock];
        //真正登录
        [self spLoginWithUserID:ywLoginId password:passWord successBlock:aSuccessBlock failedBlock:aFailedBlock];
    } else {
        if (aFailedBlock) {
            aFailedBlock([NSError errorWithDomain:YWLoginServiceDomain code:YWLoginErrorCodePasswordError userInfo:nil]);
        }
    }
}

/**
 登出入口函数
 用户即将退出登录时调用
 */
- (void)callThisBeforeISVAccountLogout{
    [self spLogout];
}


#pragma mark - 基础方法

/**
 初始化的示例代码
 @return 初始化状态
 */
- (BOOL)spInit{
    //设置日志级别
    [[[YWAPI sharedInstance] getGlobalLogService] setLogLevel:YWLogLevelDebug];
    //异步初始化IM SDK
    NSError *error = nil;
    [[YWAPI sharedInstance] syncInitWithOwnAppKey:im_AppKey getError:&error];
    if (error.code != 0 && error.code != YWSdkInitErrorCodeAlreadyInited) {
        //初始化失败
        DLog(@"初始化失败,%@",error);
        return NO;
    } else {
        if (error.code == 0) {
            //首次初始化成功,获取一个IMKit并持有
            self.ywIMKit = [[YWAPI sharedInstance] fetchIMKitForOpenIM];
        } else {
            //已经初始化
            DLog(@"已经初始化");
        }
        return YES;
    }
}

/**
 预登陆
 */
- (void)spPreLoginWithLoginId:(NSString *)loginId successBlock:(void(^)())aPreloginedBlock {
    //预登录
    if ([[self.ywIMKit.IMCore getLoginService] preLoginWithPerson:[[YWPerson alloc] initWithPersonId:loginId]]) {
        //预登录成功,直接进入页面,这里可以打开界面
        if (aPreloginedBlock) {
            aPreloginedBlock();
        }
    }
}

/**
 是否已经预登录进入
 */
- (BOOL)spIsPreLogined {
    #warning TODO: NEED TO CHANGE TO YOUR JUDGE METHOD
    //这个是Demo中判断是否已经进入IM主页面的方法,你需要修改成你自己的方法
    return YES;
}

/**
 登录
 @param aUserID 用户名
 @param aPassword 密码
 @param aSuccessBlock 成功
 @param aFailedBlock 失败
 */
- (void)spLoginWithUserID:(NSString *)aUserID password:(NSString *)aPassword successBlock:(void(^)())aSuccessBlock failedBlock:(void(^)(NSError *aError))aFailedBlock{
    aSuccessBlock = [aSuccessBlock copy];
    aFailedBlock = [aFailedBlock copy];
    //登录之前,先告诉IM如何获取登录信息。
    //当IM向服务器发起登录请求之前,会调用这个block,来获取用户名和密码信息。
    [[self.ywIMKit.IMCore getLoginService] setFetchLoginInfoBlock:^(YWFetchLoginInfoCompletionBlock aCompletionBlock) {
        aCompletionBlock(YES, aUserID, aPassword, nil, nil);
    }];
    //发起登录
    [[self.ywIMKit.IMCore getLoginService] asyncLoginWithCompletionBlock:^(NSError *aError, NSDictionary *aResult) {
        if (aError.code == 0 || [[self.ywIMKit.IMCore getLoginService] isCurrentLogined]) {
            //登录成功
            DLog(@"登录成功");
            if (aSuccessBlock) {
                aSuccessBlock();
            }
        } else {
            //登录失败
            DLog(@"登录失败");
            if (aFailedBlock) {
                aFailedBlock(aError);
            }
        }
    }];
}

/**
 注销
 */
- (void)spLogout {
    [[self.ywIMKit.IMCore getLoginService] asyncLogoutWithCompletionBlock:NULL];
}

/**
 监听连接状态
 */
- (void)spListenConnectionStatus{
    __weak typeof(self) weakSelf = self;
    [[self.ywIMKit.IMCore getLoginService] addConnectionStatusChangedBlock:^(YWIMConnectionStatus aStatus, NSError *aError) {
        [weakSelf setLastConnectionStatus:aStatus];
        //手动登出、被踢、自动连接失败
        if (aStatus == YWIMConnectionStatusForceLogout || aStatus == YWIMConnectionStatusMannualLogout || aStatus == YWIMConnectionStatusAutoConnectFailed) {
            if (aStatus == YWIMConnectionStatusForceLogout) {
                DLog(@"被踢");
            }
            if (aStatus == YWIMConnectionStatusMannualLogout) {
                DLog(@"退出登录");
            }
            if (aStatus == YWIMConnectionStatusAutoConnectFailed) {
                DLog(@"IM长连接自动连接失败");
            }
        } else if (aStatus == YWIMConnectionStatusConnected) {
            
        }
    } forKey:[self description] ofPriority:YWBlockPriorityDeveloper];
}

/**
 监听自己发送的消息的生命周期
 */
- (void)spListenMyMessageLife{
    [[self.ywIMKit.IMCore getConversationService] addMessageLifeDelegate:self forPriority:YWBlockPriorityDeveloper];
}

- (YWMessageLifeContext *)messageLifeWillSend:(YWMessageLifeContext *)aContext{
    DLog(@"开始发送");
    //此处可以过滤敏感词
    return nil;
}

- (void)messageLifeDidSend:(NSString *)aMessageId conversationId:(NSString *)aConversationId result:(NSError *)aResult{
    if (aResult.code == 0) {
        DLog(@"发送成功");
    } else {
        DLog(@"发送失败 : %@",aResult);
    }
}

/**
 设置声音播放模式
 */
- (void)spSetAudioCategory {
    //设置为扬声器模式,这样可以支持靠近耳朵时自动切换到听筒
    [self.ywIMKit setAudioSessionCategory:AVAudioSessionCategoryPlayback];
}


/**
 设置Profile
 */
- (void)spSetProfile {
    //在这里设置客服的显示名称
    [self.ywIMKit setFetchProfileForEServiceBlock:^(YWPerson *aPerson, YWProfileProgressBlock aProgressBlock, YWProfileCompletionBlock aCompletionBlock) {
        YWProfileItem *item = [[YWProfileItem alloc] init];
        item.person = aPerson;
        item.displayName = aPerson.personId;
        item.avatar = [UIImage imageNamed:@"demo_customer_120"];
        aCompletionBlock(YES, item);
    }];
}

/**
 *  监听新消息
 */
- (void)spListenNewMessage {
    [[self.ywIMKit.IMCore getConversationService] addOnNewMessageBlockV2:^(NSArray *aMessages, BOOL aIsOffline) {
        //可以在此处根据需要播放提示音
        //展示透传消息
        [aMessages enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            id<IYWMessage> msg = obj;
            YWMessageBodyCustomize *body = nil;
            if ([msg respondsToSelector:@selector(messageBody)]) {
                body = [[msg messageBody] isKindOfClass:[YWMessageBodyCustomize class]] ? (YWMessageBodyCustomize *)[msg messageBody] : nil;
            }
        }];
    } forKey:self.description ofPriority:YWBlockPriorityDeveloper];
}

/**
 设置提示信息
 */
- (void)spSetNotificationBlock {
    //当IMSDK需要弹出提示时,会调用此回调,你需要修改成你App中显示提示的样式
    [self.ywIMKit setShowNotificationBlock:^(UIViewController *aViewController, NSString *aTitle, NSString *aSubtitle, YWMessageNotificationType aType) {
        DLog(@"提示信息:%@,%@,%ld",aTitle,aSubtitle,aType);
    }];
}

#pragma mark - UI PAGES

/**
 创建会话列表页面
 */
- (YWConversationListViewController *)spMakeConversationListControllerWithSelectItemBlock:(YWConversationsListDidSelectItemBlock)aSelectItemBlock {
    YWConversationListViewController *result = [self.ywIMKit makeConversationListViewController];
    [result setDidSelectItemBlock:aSelectItemBlock];
    return result;
}

/**
 打开某个会话
 */
- (void)spOpenConversationViewControllerWithConversation:(YWConversation *)aConversation fromNavigationController:(UINavigationController *)aNavigationController {
    UINavigationController *conversationNavigationController = nil;
    conversationNavigationController = aNavigationController;
    __block YWConversationViewController *conversationViewController = nil;
    [aNavigationController.viewControllers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([obj isKindOfClass:[YWConversationViewController class]]) {
            YWConversationViewController *c = obj;
            if (aConversation.conversationId && [c.conversation.conversationId isEqualToString:aConversation.conversationId]) {
                conversationViewController = c;
                *stop = YES;
            }
        }
    }];
    if (!conversationViewController) {
        //重新fetch一个新的,防止原对象中还有一些状态未结束。
        if ([aConversation isKindOfClass:[YWP2PConversation class]]) {
            aConversation = [YWP2PConversation fetchConversationByPerson:[(YWP2PConversation *)aConversation person] creatIfNotExist:YES baseContext:self.ywIMKit.IMCore];
            //如果需要,可以在这里设置其他一些需要修改的属性
        }
        conversationViewController = [self spMakeConversationViewControllerWithConversation:aConversation];
    }
    NSArray *viewControllers = nil;
    if (conversationNavigationController.viewControllers.firstObject == conversationViewController) {
        viewControllers = @[conversationNavigationController.viewControllers.firstObject];
    } else {
        DLog(@"conversationNavigationController.viewControllers.firstObject:%@", conversationNavigationController.viewControllers.firstObject);
        DLog(@"conversationViewController:%@", conversationViewController);
        viewControllers = @[conversationNavigationController.viewControllers.firstObject, conversationViewController];
    }
    [conversationNavigationController setViewControllers:viewControllers animated:YES];
}

/**
 打开单聊页面
 */
- (void)spOpenConversationViewControllerWithPerson:(YWPerson *)aPerson fromNavigationController:(UINavigationController *)aNavigationController {
    YWConversation *conversation = [YWP2PConversation fetchConversationByPerson:aPerson creatIfNotExist:YES baseContext:self.ywIMKit.IMCore];
    [self spOpenConversationViewControllerWithConversation:conversation fromNavigationController:aNavigationController];
}

/**
 打开客服页面
 */
- (void)spOpenEServiceConversationWithPersonId:(NSString *)aPersonId fromNavigationController:(UINavigationController *)aNavigationController {
    YWPerson *person = [[SPKit sharedInstance] spFetchEServicePersonWithPersonId:aPersonId groupId:nil];
    [[SPKit sharedInstance] spOpenConversationViewControllerWithPerson:person fromNavigationController:aNavigationController];
}

/**
 获取客服对象
 */
- (YWPerson *)spFetchEServicePersonWithPersonId:(NSString *)aPersonId groupId:(NSString *)aGroupId {
    YWPerson *person = [[YWPerson alloc] initWithPersonId:aPersonId EServiceGroupId:aGroupId baseContext:self.ywIMKit.IMCore];
    //下面这一行用于控制锁定某个子账号,不分流。
    //[person setLockShunt:YES];
    return person;
}

/**
 创建某个会话Controller
 */
- (YWConversationViewController *)spMakeConversationViewControllerWithConversation:(YWConversation *)conversation {
    YWConversationViewController *conversationController = nil;
    conversationController = [YWConversationViewController makeControllerWithIMKit:self.ywIMKit conversation:conversation];
    [self.ywIMKit addDefaultInputViewPluginsToMessagesListController:conversationController];
    
//    /// 添加自定义插件
//    [self exampleAddInputViewPluginToConversationController:conversationController];
//    
//    /// 添加自定义表情
//    [self exampleShowCustomEmotionWithConversationController:conversationController];
//    
//    /// 设置显示自定义消息
//    [self exampleShowCustomMessageWithConversationController:conversationController];
//    
//    /// 设置消息长按菜单
//    [self exampleSetMessageMenuToConversationController:conversationController];
    
    conversationController.hidesBottomBarWhenPushed = YES;
    return conversationController;
}

#pragma mark - apns

/**
 *  在-[AppDelegate application:didFinishLaunchingWithOptions:]中第一时间设置此回调
 *  在IMSDK截获到Push通知并需要您处理Push时,IMSDK会自动调用此回调
 */
- (void)spHandleAPNSPush {
    __weak typeof(self) weakSelf = self;
    [[[YWAPI sharedInstance] getGlobalPushService] addHandlePushBlockV4:^(NSDictionary *aResult, BOOL *aShouldStop) {
        BOOL isLaunching = [aResult[YWPushHandleResultKeyIsLaunching] boolValue];
        UIApplicationState state = [aResult[YWPushHandleResultKeyApplicationState] integerValue];
        NSString *conversationId = aResult[YWPushHandleResultKeyConversationId];
        Class conversationClass = aResult[YWPushHandleResultKeyConversationClass];
        
        if (conversationId.length <= 0) {
            return;
        }
        
        if (conversationClass == NULL) {
            return;
        }
        
        if (isLaunching) {
            //用户划开Push导致app启动
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.3f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                if ([self spIsPreLogined]) {
                    //说明已经预登录成功
                    YWConversation *conversation = nil;
                    if (conversationClass == [YWP2PConversation class]) {
                        conversation = [YWP2PConversation fetchConversationByConversationId:conversationId creatIfNotExist:YES baseContext:weakSelf.ywIMKit.IMCore];
                    } else if (conversationClass == [YWTribeConversation class]) {
                        conversation = [YWTribeConversation fetchConversationByConversationId:conversationId creatIfNotExist:YES baseContext:weakSelf.ywIMKit.IMCore];
                    }
                    if (conversation) {
                        [weakSelf spOpenConversationViewControllerWithConversation:conversation fromNavigationController: [weakSelf conversationNavigationController]];
                    }
                }
            });
            
        } else {
            /// app已经启动时处理Push
            
            if (state != UIApplicationStateActive) {
                if ([self spIsPreLogined]) {
                    //说明已经预登录成功
                    YWConversation *conversation = nil;
                    if (conversationClass == [YWP2PConversation class]) {
                        conversation = [YWP2PConversation fetchConversationByConversationId:conversationId creatIfNotExist:YES baseContext:weakSelf.ywIMKit.IMCore];
                    } else if (conversationClass == [YWTribeConversation class]) {
                        conversation = [YWTribeConversation fetchConversationByConversationId:conversationId creatIfNotExist:YES baseContext:weakSelf.ywIMKit.IMCore];
                    }
                    if (conversation) {
                        [weakSelf spOpenConversationViewControllerWithConversation:conversation fromNavigationController:[weakSelf conversationNavigationController]];
                    }
                }
            } else {
                //应用处于前台
                //建议不做处理,等待IM连接建立后,收取离线消息。
            }
        }
    } forKey:self.description ofPriority:YWBlockPriorityDeveloper];
}

@end

AppDelegate.m

//
//  AppDelegate.m
//  IMV1
//
//  Created by DaLei on 2017/6/30.
//  Copyright © 2017年 DaLei. All rights reserved.
//

#import "AppDelegate.h"
#import "SPKit.h"
#import "RootViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    //IM程序入口
    [[SPKit sharedInstance] callThisInDidFinishLaunching];

    
    self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    RootViewController *rvc = [[RootViewController alloc]initWithNibName:@"RootViewController" bundle:nil];
    UINavigationController *nvc = [[UINavigationController alloc]initWithRootViewController:rvc];
    self.window.rootViewController = nvc;
    [self.window makeKeyAndVisible];
    
        
    //向APNS注册PUSH服务,需要区分iOS SDK版本和iOS版本。
    #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeAlert|UIUserNotificationTypeSound) categories:nil];
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
        
    } else
    #endif
    {
        /// 去除warning
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
    #pragma clang diagnostic pop
    }
    
    
    return YES;
}

//iOS8下申请DeviceToken
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerForRemoteNotifications)]) {
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    }
}
#endif


- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}


- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}


@end

RootViewController.m

//
//  RootViewController.m
//  IMV1
//
//  Created by DaLei on 2017/7/3.
//  Copyright © 2017年 DaLei. All rights reserved.
//

#import "RootViewController.h"
#import "SPKit.h"

@interface RootViewController ()

@end

@implementation RootViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


//登陆
-(IBAction)login:(id)sender{
    [[SPKit sharedInstance] callThisAfterISVAccountLoginSuccessWithYWLoginId:self.userName.text passWord:self.password.text preloginedBlock:^{
        NSLog(@"登陆中");
    } successBlock:^{
        NSLog(@"登陆成功");
    } failedBlock:^(NSError *aError) {
        NSLog(@"登陆失败");
    }];
}

//登出
-(IBAction)loginOut:(id)sender {
    [[SPKit sharedInstance] callThisBeforeISVAccountLogout];
}

//联系我们
-(IBAction)contactUs:(id)sender {
    [[SPKit sharedInstance] spOpenEServiceConversationWithPersonId:im_Service fromNavigationController:self.navigationController];
}

//打开列表
-(IBAction)chartList:(id)sender{
    
    YWConversationListViewController *conversationListController = [[SPKit sharedInstance].ywIMKit makeConversationListViewController];
    
    __weak __typeof(conversationListController) weakConversationListController = conversationListController;
    conversationListController.didSelectItemBlock = ^(YWConversation *aConversation) {
        if ([aConversation isKindOfClass:[YWCustomConversation class]]) {
            //自定义菜单选择
        } else {
            [[SPKit sharedInstance] spOpenConversationViewControllerWithConversation:aConversation fromNavigationController:weakConversationListController.navigationController];
        }
    };
    
    //会话列表空视图
    if (conversationListController) {
        CGRect frame = CGRectMake(0, 0, 100, 100);
        UIView *viewForNoData = [[UIView alloc] initWithFrame:frame];
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"login_logo"]];
        imageView.center = CGPointMake(viewForNoData.frame.size.width/2, viewForNoData.frame.size.height/2);
        [imageView setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleBottomMargin];
        [viewForNoData addSubview:imageView];
        conversationListController.viewForNoData = viewForNoData;
    }
    
    [[SPKit sharedInstance].ywIMKit setUnreadCountChangedBlock:^(NSInteger aCount) {
        NSString *badgeValue = aCount > 0 ?[ @(aCount) stringValue] : nil;
        NSLog(@"badgeValue : %@",badgeValue);
    }];
    
    [self.navigationController pushViewController:conversationListController animated:YES];
}



@end

运行效果

Simulator Screen Shot 2017年7月4日 上午10.17.52.png
Simulator Screen Shot 2017年7月4日 上午10.18.03.png
Simulator Screen Shot 2017年7月4日 上午10.17.56.png

代码下载

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容