app框架整體設(shè)計(jì)

簡(jiǎn)書無法正常顯示部分流程圖


應(yīng)用啟動(dòng)的主要流程

st=>start: AppDelegate
islogin=>condition: isLogin?
login=>operation: http登錄
tcp=>operation: tcp注冊(cè)登錄
newdesc=>operation: 功能引導(dǎo)
rootVC=>operation: 主控制器
mainVC=>operation: 主頁
sync=>operation: 數(shù)據(jù)同步

e=>end



st->islogin
islogin(yes)->tcp->rootVC->e
islogin(no)->newdesc->login(left)->tcp->rootVC->mainVC->sync->e


1. 整體架構(gòu)與模塊化劃分設(shè)計(jì)

項(xiàng)目采用Category方式設(shè)計(jì)把項(xiàng)目按照某個(gè)具體業(yè)務(wù)邏輯功能劃分、模塊之間未能夠完全解耦,所以導(dǎo)致項(xiàng)目沒辦法使用pod方式管理
(采用通知回調(diào)是能夠完全解耦,不實(shí)際、未采用)

項(xiàng)目功能模塊

384CB19E-4141-47C3-8AB5-D93DA9A509DC.png

項(xiàng)目文件結(jié)構(gòu)

項(xiàng)目文件結(jié)構(gòu).png

每一個(gè)模塊文件結(jié)構(gòu)相同,模塊中的Actions與Category 負(fù)責(zé)本模塊的功能被調(diào)度


項(xiàng)目文件結(jié)構(gòu)展開.png
#import "IComMediator+DynamicModuleActions.h"
// action 類名 Dynamic 模塊統(tǒng)一用Dynamic 模塊名
NSString * const kIComMediatorTargetDynamic = @"Dynamic";

// 類對(duì)應(yīng)方法名 一個(gè)模塊下不同控制器的類名
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 {
    // 這里處理異常場(chǎng)景,具體如何處理取決于產(chǎn)品
    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

公共部分負(fù)責(zé)項(xiàng)目每個(gè)模塊的整體調(diào)度與協(xié)作

負(fù)責(zé)公共部分.png

每個(gè)模塊的Category調(diào)度器均為主調(diào)度器(IComMediator)的Category

IComMediator.h

#import <Foundation/Foundation.h>

@interface IComMediator : NSObject

+(instancetype)sharedInstance;

// 遠(yuǎn)程App調(diào)用入口
- (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;

// 本地組件調(diào)用入口
- (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;
}

// 遠(yuǎn)程App調(diào)用入口
- (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion {
if (![url.scheme isEqualToString:@"icom"]) { // 外部啟動(dòng)規(guī)則
    // 這里就是針對(duì)遠(yuǎn)程app調(diào)用404的簡(jiǎn)單處理了
    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;
}

// 本地組件調(diào)用入口
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params {
// 運(yùn)行時(shí)方式 讓對(duì)應(yīng)的目標(biāo)類執(zhí)行對(duì)應(yīng)的目標(biāo)方法
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) {
    // 這里是處理無響應(yīng)請(qǐng)求的地方之一,這個(gè)demo做得比較簡(jiǎn)單,如果沒有可以響應(yīng)的target,就直接return了。實(shí)際開發(fā)過程中是可以事先給一個(gè)固定的target專門用于在這個(gè)時(shí)候頂上,然后處理這種請(qǐng)求的
    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 {
    // 這里是處理無響應(yīng)請(qǐng)求的地方,如果無響應(yīng),則嘗試調(diào)用對(duì)應(yīng)target的notFound方法統(tǒng)一處理
    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 {
        // 這里也是處理無響應(yīng)請(qǐng)求的地方,在notFound都沒有的時(shí)候,這個(gè)demo是直接return了。實(shí)際開發(fā)過程中,可以用前面提到的固定的target頂上的。
        return nil;
    }
}
}

模塊間調(diào)度

    UIViewController *viewController = [[IComMediator sharedInstance]         IComMediator_DiscoverViewController:nil];
    [self.navigationController pushViewController:viewController animated:YES];

2. 長(zhǎng)連接模塊設(shè)計(jì)

B215DD52-0EB9-4EA8-B342-CA7DCAB4C0F5.png
st=>start: TCP登錄
islogin=>condition: TCP登錄成功?
sendBeat=>operation: 創(chuàng)建輪詢發(fā)送心跳請(qǐng)求
sendBeatStatus=>condition: 心跳包發(fā)送成功?
reSendBeat=>operation: 重新發(fā)送機(jī)制
isbeatFailOut=>condition: 失敗次數(shù)超限?
reSendF=>operation: 失敗次數(shù)達(dá)到上限后判斷是否斷開連接,做相應(yīng)處理


reConnect=>operation: tcp斷開重新連接機(jī)制
reIsSuccess=>condition: 重連接機(jī)?
disConnect=>condition: 斷開連接?
alert=>operation: 提示用戶無法正常連接

login=>operation: http登錄
tcp=>operation: tcp注冊(cè)登錄
newdesc=>operation: 功能引導(dǎo)
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數(shù)據(jù)同步機(jī)制流程

B3EBE84C-9FF0-47C6-99F7-F9300C91CBF9.png
st=>start: TCP登錄成功
sync=>operation: imserver/groupList
syncStatus=>condition: grouplist?
saveDB=>operation: 修改(狀態(tài)、未讀數(shù)等)
updateGroupInfo=>operation: 更新群組信息
saveDB2=>operation: 存儲(chǔ)群組最新信息
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. 消息發(fā)送機(jī)制

st=>start: 用戶輸入
isFile=>condition: file?
checkFile=>condition: 服務(wù)器存在?
createMessage=>operation: 創(chuàng)建消息體、生成消息唯一標(biāo)識(shí)

sendFile=>operation: 創(chuàng)建臨時(shí)消息體,發(fā)送文件拿到filekey

saveDB=>operation: 消息存儲(chǔ)

sendMessage=>operation: 更新UI界面,消息發(fā)送
isSuccess=>condition:  消息發(fā)送成功?

disConnect=>condition: tcp連接?

update=>operation: 更新DB和UI
sendFail=>operation: 更新UI
sysReSend=>operation: tcp重新連接后系統(tǒng)重新發(fā)送

isTimeOut=>condition: 消息發(fā)送超時(shí)?
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.消息接收機(jī)制

進(jìn)入聊天模塊調(diào)用

st=>start
loadData=>operation: 本地?cái)?shù)據(jù)庫加載數(shù)據(jù)
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

消息返拉邏輯調(diào)用

st=>start
loadData=>operation: 本地?cái)?shù)據(jù)庫加載數(shù)據(jù)(data.firstId)
cheakCount=>condition: count>0?
cheak=>condition: data.firstId-db.firstId>=max?
repullMessage=>operation: repullMessage
cheakMaxId=>condition: maxId == nil?
isLastId=>condition: 最后一條數(shù)據(jù)?
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

消息接收邏輯調(diào)用

st=>start
tcpReceived=>operation: messageDidReceived
cmd=>condition: isCmd ?
updateCmd=>operation: updateCmdList
cmdListAction=>operation: 處理對(duì)應(yīng)action
cheak=>condition: messageType == 0
groupList=>operation: updateGroupList
isExist=>condition: groupIsExist?
updateGroupStatus=>operation: 更新狀態(tài)、未讀、聲音提示等信息
saveDB=>operation: 消息存儲(chǔ)到本地?cái)?shù)據(jù)庫
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. 數(shù)據(jù)存儲(chǔ)

C6CB068E-0EA6-4AA7-BD03-6F3DC90781CD.png

7. JS與WKWebView交互模塊

st=>start: 協(xié)議定制
jsrequest=>operation: js請(qǐng)求
isBase=>condition: 基礎(chǔ)接口?
isValidData=>condition: ValidData?
validData=>operation: 權(quán)限校驗(yàn)
hasPermission=>condition: 權(quán)限?
load=>operation: 調(diào)用客戶端邏輯
loadSuccess=>condition: isSuccess?
success=>operation: 成功回調(diào)
fail=>operation: 錯(cuò)誤回調(diào)

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' #修改過源代碼 請(qǐng)勿更新
pod 'CocoaAsyncSocket', '~> 7.4.3'
pod 'SSKeychain',   '~> 1.4.0'
pod 'MJExtension',  '~> 3.0.10'
pod 'YYKit',    '~> 1.0.5' #修改過源代碼 請(qǐng)勿更新
pod 'MLLabel',  '~> 1.9.1' #修改過源代碼 請(qǐng)勿更新
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' #修改過源代碼 請(qǐng)勿更新
pod 'CocoaLumberjack',  '~> 3.0.0' #修改過源代碼 請(qǐng)勿更新
pod 'JSPatchPlatform', '~> 1.6.4'
pod 'TZImagePickerController',  '1.7.9'  #修改過源代碼 請(qǐng)勿更新
pod 'mailcore2-ios', '0.6.4'
pod 'DKNightVersion', '2.4.3'
pod 'LKDBHelper', '2.4.2'
pod 'Bugtags'

end

9. 后期架構(gòu)調(diào)整

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評(píng)論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,661評(píng)論 25 708
  • 旅行,說起來也是一件逼哥很高的事了。 依然記得安迪在過年前唰機(jī)票一買飛泰國(guó)了,以為去東南亞這種熱帶風(fēng)光之地不是享受...
    警醒閱讀 113評(píng)論 0 0
  • 在這荒誕的世界里 我俯望著,這一整夜的城 點(diǎn)點(diǎn)星云,燈紅酒綠 心,找不到方向 在這荒誕的世界里 我倚在寂靜的墻角 ...
    風(fēng)曦靜默閱讀 447評(píng)論 5 7
  • 昨天完成了Nike Traning里5km教練練習(xí)的周三訓(xùn)練任務(wù),因?yàn)樯现艿某鲂械⒄`了好幾天,所以沒有辦法再拖下去...
    麥田有棵樹閱讀 147評(píng)論 0 1