应用启动的主要流程st=>start: AppDelegate islogin=>condition: isLogin? login=>operation: http登录 tcp=>operation: tcp注册登录 newdesc=>operation: 功能引导 rootVC=>operation: 主控制器 mainVC=>operation: 主页 sync=>operation: 数据同步 e=>end st->islogin islogin(yes)->tcp->rootVC->e islogin(no)->newdesc->login(left)->tcp->rootVC->mainVC->sync->e 1. 整体架构与模块化划分设计项目采用Category方式设计把项目按照某个具体业务逻辑功能划分、模块之间未能够完全解耦,所以导致项目没办法使用pod方式管理 (采用通知回调是能够完全解耦,不实际、未采用) 项目功能模块384CB19E-4141-47C3-8AB5-D93DA9A509DC.png 项目文件结构项目文件结构.png 每一个模块文件结构相同,模块中的Actions与Category 负责本模块的功能被调度 项目文件结构展开.png #import "IComMediator+DynamicModuleActions.h" // action 类名 Dynamic 模块统一用Dynamic 模块名 NSString * const kIComMediatorTargetDynamic = @"Dynamic"; // 类对应方法名 一个模块下不同控制器的类名 NSString * const kIComNativeFetchDynamicViewController = @"nativeFetchDynamicViewController"; NSString * const kIComNativeFetchDiscoverViewController = @"nativeFetchDiscoverViewController"; //DiscoverViewController @implementation IComMediator (DynamicModuleActions) - (UIViewController *)IComMediator_DynamicViewController:(NSDictionary *)params { UIViewController *viewController = [self performTarget:kIComMediatorTargetDynamic action:kIComNativeFetchDynamicViewController params:params]; if ([viewController isKindOfClass:[UIViewController class]]) { // view controller 交付出去之后,可以由外界选择是push还是present return viewController; } else { // 这里处理异常场景,具体如何处理取决于产品 return [[UIViewController alloc] init]; } } - (UIViewController *)IComMediator_DiscoverViewController: (NSDictionary *)params { UIViewController *viewController = [self performTarget:kIComMediatorTargetDynamic action:kIComNativeFetchDiscoverViewController params:params]; if ([viewController isKindOfClass:[UIViewController class]]) { return viewController; } else { return [[UIViewController alloc] init]; } } @end 公共部分负责项目每个模块的整体调度与协作 负责公共部分.png 每个模块的Category调度器均为主调度器(IComMediator)的Category IComMediator.h #import <Foundation/Foundation.h> @interface IComMediator : NSObject +(instancetype)sharedInstance; // 远程App调用入口 - (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion; // 本地组件调用入口 - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params; @end IComMediator.m #import "IComMediator.h" #import "NSDictionary+URL.h" @implementation IComMediator +(instancetype)sharedInstance { static IComMediator *mediator; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ mediator = [[IComMediator alloc] init]; }); return mediator; } // 远程App调用入口 - (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion { if (![url.scheme isEqualToString:@"icom"]) { // 外部启动规则 // 这里就是针对远程app调用404的简单处理了 return @(NO); } NSDictionary *params = [NSDictionary dictionaryWithURLQuery:[url query]]; NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""]; id result = [self performTarget:url.host action:actionName params:params]; if (completion) { if (result) { completion(@{ @"result" : result }); } else { completion(nil); } } return result; } // 本地组件调用入口 - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params { // 运行时方式 让对应的目标类执行对应的目标方法 NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName]; NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName]; Class targetClass = NSClassFromString(targetClassString); id target = [[targetClass alloc] init]; SEL action = NSSelectorFromString(actionString); if (target == nil) { // 这里是处理无响应请求的地方之一,这个demo做得比较简单,如果没有可以响应的target,就直接return了。实际开发过程中是可以事先给一个固定的target专门用于在这个时候顶上,然后处理这种请求的 return nil; } if ([target respondsToSelector:action]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" return [target performSelector:action withObject:params]; #pragma clang diagnostic pop } else { // 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理 SEL action = NSSelectorFromString(@"notFound:"); if ([target respondsToSelector:action]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" return [target performSelector:action withObject:params]; #pragma clang diagnostic pop } else { // 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。 return nil; } } } 模块间调度 UIViewController *viewController = [[IComMediator sharedInstance] IComMediator_DiscoverViewController:nil]; [self.navigationController pushViewController:viewController animated:YES]; 2. 长连接模块设计B215DD52-0EB9-4EA8-B342-CA7DCAB4C0F5.png st=>start: TCP登录 islogin=>condition: TCP登录成功? sendBeat=>operation: 创建轮询发送心跳请求 sendBeatStatus=>condition: 心跳包发送成功? reSendBeat=>operation: 重新发送机制 isbeatFailOut=>condition: 失败次数超限? reSendF=>operation: 失败次数达到上限后判断是否断开连接,做相应处理 reConnect=>operation: tcp断开重新连接机制 reIsSuccess=>condition: 重连接机? disConnect=>condition: 断开连接? alert=>operation: 提示用户无法正常连接 login=>operation: http登录 tcp=>operation: tcp注册登录 newdesc=>operation: 功能引导 rootVC=>operation: 主控制器 mainVC=>operation: 主页 e=>end st->islogin islogin(yes)->sendBeat->sendBeatStatus(yes)->e sendBeatStatus(no)->isbeatFailOut(yes,right)->reConnect isbeatFailOut(no)->e disConnect(no,bottom)->e islogin(no)->reConnect(right)->reIsSuccess reIsSuccess(yes,left)->sendBeat reIsSuccess(no)->alert->e 3. iCome数据同步机制流程B3EBE84C-9FF0-47C6-99F7-F9300C91CBF9.png st=>start: TCP登录成功 sync=>operation: imserver/groupList syncStatus=>condition: grouplist? saveDB=>operation: 修改(状态、未读数等) updateGroupInfo=>operation: 更新群组信息 saveDB2=>operation: 存储群组最新信息 syncCmd=>operation: imserver/cmdList syncCmdStatus=>condition: cmdList? updateStatus=>operation: 同步本地的信息(修改头像、踢出群组、日程同步等) e=>end st->sync->syncStatus(yes)->saveDB->updateGroupInfo->saveDB2->syncCmd->updateStatus->e syncStatus(no)->e 4. 消息发送机制st=>start: 用户输入 isFile=>condition: file? checkFile=>condition: 服务器存在? createMessage=>operation: 创建消息体、生成消息唯一标识 sendFile=>operation: 创建临时消息体,发送文件拿到filekey saveDB=>operation: 消息存储 sendMessage=>operation: 更新UI界面,消息发送 isSuccess=>condition: 消息发送成功? disConnect=>condition: tcp连接? update=>operation: 更新DB和UI sendFail=>operation: 更新UI sysReSend=>operation: tcp重新连接后系统重新发送 isTimeOut=>condition: 消息发送超时? e=>end st->isFile isFile(yes)->checkFile isFile(no)->createMessage->saveDB->sendMessage->disConnect(yes)->isSuccess checkFile(yes,right)->createMessage checkFile(no)->sendFile->createMessage disConnect(no)->isTimeOut(no)->sysReSend->isSuccess isTimeOut(yes)->sendFail isSuccess(no)->sendFail->e isSuccess(yes,right)->update->e 5.消息接收机制进入聊天模块调用st=>start loadData=>operation: 本地数据库加载数据 cheak=>condition: db.lastId-db.firstId>=max? repullMessage=>operation: repullMessage saveDB=>operation: save DB update=>operation: 更新UI界面 e=>end st->loadData->cheak cheak(yes)->repullMessage->saveDB->update->e cheak(no)->update->e 消息返拉逻辑调用st=>start loadData=>operation: 本地数据库加载数据(data.firstId) cheakCount=>condition: count>0? cheak=>condition: data.firstId-db.firstId>=max? repullMessage=>operation: repullMessage cheakMaxId=>condition: maxId == nil? isLastId=>condition: 最后一条数据? update=>operation: 更新UI界面 e=>end st->loadData->cheakCount cheakCount(yes)->cheak cheakCount(no)->cheakMaxId cheakMaxId(yes)->repullMessage cheakMaxId(no)->isLastId isLastId(yes, right)->update isLastId(no)->repullMessage cheak(yes)->repullMessage->update->e cheak(no)->update->e 消息接收逻辑调用st=>start tcpReceived=>operation: messageDidReceived cmd=>condition: isCmd ? updateCmd=>operation: updateCmdList cmdListAction=>operation: 处理对应action cheak=>condition: messageType == 0 groupList=>operation: updateGroupList isExist=>condition: groupIsExist? updateGroupStatus=>operation: 更新状态、未读、声音提示等信息 saveDB=>operation: 消息存储到本地数据库 update=>operation: 通知更新聊天与消息列表界面 e=>end st->tcpReceived->cmd cmd(yes,right)->updateCmd->cmdListAction->e cmd(no)->cheak cheak(yes, right)->groupList->e cheak(no)->isExist isExist(yes)->updateGroupStatus->saveDB->update->e isExist(no)->groupList->e 6. 数据存储C6CB068E-0EA6-4AA7-BD03-6F3DC90781CD.png 7. JS与WKWebView交互模块st=>start: 协议定制 jsrequest=>operation: js请求 isBase=>condition: 基础接口? isValidData=>condition: ValidData? validData=>operation: 权限校验 hasPermission=>condition: 权限? load=>operation: 调用客户端逻辑 loadSuccess=>condition: isSuccess? success=>operation: 成功回调 fail=>operation: 错误回调 e=>end st->jsrequest->isBase isBase(yes)->load->loadSuccess isBase(no)->hasPermission hasPermission(yes)->load hasPermission(no)->validData->loadSuccess loadSuccess(yes)->success->e loadSuccess(no)->fail->e 8. 第三方使用source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'ICome' do pod 'FMDB', '~> 2.6.2' pod 'MBProgressHUD', '~> 1.0.0' pod 'Masonry', '~> 1.0.2' pod 'SDWebImage', '~> 3.7.6' #修改过源代码 请勿更新 pod 'CocoaAsyncSocket', '~> 7.4.3' pod 'SSKeychain', '~> 1.4.0' pod 'MJExtension', '~> 3.0.10' pod 'YYKit', '~> 1.0.5' #修改过源代码 请勿更新 pod 'MLLabel', '~> 1.9.1' #修改过源代码 请勿更新 pod 'UITableView+FDTemplateLayoutCell', '~> 1.4' pod 'AFNetworking', '~> 3.1.0' pod 'BaiduMapKit', '~> 3.3.1' pod 'MJRefresh', '~> 3.1.12' pod 'UMengAnalytics', '~> 4.2.4' pod 'FDFullscreenPopGesture', '~>1.1' pod 'CYLTabBarController', '~> 1.6.0' #修改过源代码 请勿更新 pod 'CocoaLumberjack', '~> 3.0.0' #修改过源代码 请勿更新 pod 'JSPatchPlatform', '~> 1.6.4' pod 'TZImagePickerController', '1.7.9' #修改过源代码 请勿更新 pod 'mailcore2-ios', '0.6.4' pod 'DKNightVersion', '2.4.3' pod 'LKDBHelper', '2.4.2' pod 'Bugtags' end 9. 后期架构调整 |
|