為你的移動應用選擇正確的架構是一件相當大的事情挎挖,這會對你的工作流程造成影響,陷入面對的問題噪伊,可能是一筆巨大財富也可能是一個巨大負擔畔师。
HubSpot的是一個全功能的app憨降。他是一個分析的app棋枕,也是一個設計媒體的app曹抬,還是一個郵件app棵逊,并且是一個聯(lián)系人管理的app(可能還有更多地驚喜),這些功能集合在一個app中悼吱。去年夏天慎框,當我們開始構建這個相當復雜的app時,我們知道必須有一個可以容易擴展它的架構后添。
實際上笨枯,我們把每個子app當做一個完全完整地獨立的應用程序,然后使用CocoaPods將它們集成到主應用程序中遇西。
截圖中馅精,你可以看到各個子程序的源程序,儀表盤粱檀,社交媒體洲敢,實際上既是一個獨立的iPhone應用程序,也可以納入主要的應用程序的菜單中選擇一個應用程序茄蚯。
這會給我們代理一些巨大的好處:
- 最關鍵的是压彭,我們很容易就能保證每個子應用程序的主分支是準備好發(fā)布,并可以推送一個子應用程序的特定版本渗常。
- 我們在編譯上花費多一些時間壮不,而減少了較多合并的時間。每個獨立app的沙盒可以很容易的在子應用程序內(nèi)循環(huán)皱碘,花最少的時間與其他應用程序進行集成询一。如果你在一個以上的iOS團隊工作過,你毫無疑問已經(jīng)經(jīng)歷過.xcodeproj合并時的痛苦癌椿。雖然他們可以解決家凯,但這個痛苦地步驟我們只想對他敬而遠之。
- 我們能夠在必要時單獨部署每個應用程序如失,這樣我們在一個獨立的app可用性測試上將會是驚人的绊诲。我們可以更早地將我們的app在合成之前提交給測試者,例如導航功能實現(xiàn)之前褪贵,這樣我們可以得到更高質量掂之,和更有針對性的反饋抗俄。
- 由于子應用程序之間的用戶流只是基于URL路徑(后面會詳細解釋)完成的,這意味著路徑可以內(nèi)置或者文檔化世舰,而不是從一堆UIViewController以搜索正確的方式來實例化一個特定的視圖动雹,當然必須要事先定義好路徑。建立類似演練教程或者新的推送通知時跟压,這是非常有用的胰蝠。
這種架構在一個兩人以上的團隊建立多方面的iOSapp時,能節(jié)省出巨大的時間震蒋。
從網(wǎng)頁學習
這種將移動端app分割成獨立app的靈感來自于我們在HubSpot網(wǎng)頁端架構的成功茸塞。
HubSpot網(wǎng)頁端架構是高速發(fā)展和可擴展的。正如我同事所寫查剖,我們使用各種工具盒技術钾虐,讓我們在一天內(nèi)共同部署300次。這一點至關重要笋庄,當HubSpot的產(chǎn)品套件有幾個不同的松耦合應用組成:分析效扫,社交媒體,電子郵件直砂,博客和報告工具菌仁。
在web上,我們可以編譯静暂,測試和發(fā)布HubSpotapp獨立的一小部分——包括后臺API和用Java編寫的任務济丘,前端CoffeeScript工程,Python工程籍嘹。為什么移動端不也這么做?
CocoaPods:用起來弯院!
CocoaPods是iOS中出色的依賴管理解決方案辱士,是一個把所有東西聚合的關鍵工具。
一個多應用程序架構可能對你的使用的案例過度使用听绳,但CocoaPods絕對不會——盡管你只是生成一小撮使用案例颂碘,視圖組件或者網(wǎng)絡的第三方庫——花費幾分鐘來設置它是完全值得你去投入的。Ruby椅挣,gem類似的語法在app中整合開源組件中變得幾乎無縫头岔。
核心庫和共享資源,如登錄鼠证,樣式類峡竣,API/認證持久性和訪問可以構建成帶Kiwi測試和podspec文件的獨立工程。
我們將他們發(fā)布在私有CocoaPods倉庫中量九,然后在實際完全編譯的應用中包含他們适掰。然而颂碧,我們進一步把它們建立成各個子app——所有的社交媒體,電子郵件类浪,資源载城,使用樣例——分成一個單獨的podspec,然后使用CocoaPods將它們編譯到一個app中费就。
這意味著诉瓦,我們可以在內(nèi)部發(fā)行單一功能的測試版本,并且可以在一個單一的應用程序進行快速的變更而無需擔心破壞其他工作在不相關的子app開發(fā)者的編譯力细。
我們最終完app的Podfile如下所示:
platform :ios, '6.0'
# networking, slider navigation, routing
pod 'AFNetworking', '~> 1.2.1'
pod 'ViewDeck', '~> 2.2.11'
pod 'JLRoutes', '~> 1.2'
# sub-apps, pulling from the head of each repo for development. alternately, we can pin it to a release version like we do the other pods
pod 'HSAPIClient', :head
pod 'HSCommonResources', :head
pod 'HSMarketingGraderApp', :head
pod 'HSContactsApp', :head
pod 'HSDashboardApp', :head
pod 'HSLoginApp', :head
pod 'HSSocialApp', :head
pod 'HSSourcesApp', :head
pod 'HSSettingsApp', :head
pod 'HSSocialReach', :head
pod 'HSEmailApp', :head
把它粘在一起
細心地讀者會注意到我們使用了一些在主app中粘合IIViewDeck和JLRoutes兩個子app具有關鍵作用的開源工具睬澡。
做到這點我們就可以不需要在基礎app的不同菜單項提供信息,并且每個子app的路徑都可控艳汽。每個子app提供了一個實現(xiàn)了一些HSBaseApp方法的協(xié)議類猴贰。
HSBaseApp.h文件如下:
@protocol HSBaseApp <NSObject>
+ (UINavigationController *)baseNavigationController;
+ (NSArray *)menuItems;
+ (NSArray *)routesToRegister;
@end
實現(xiàn)的例子HSSocial.m如下:
+ (UINavigationController *)baseNavigationController {
return [[HSNavigationController alloc] initWithRootViewController:[[HSSocialViewController alloc] initWithNibName:@"HSSocialViewController" bundle:nil]];
}
+ (NSArray *)menuItems {
HSMenuItem *calendarMenuItem = [[HSMenuItem alloc] initWithTitle:@"Publishing" icon:@"\\" launchHubSpotApp:[HSSocial class]];
calendarMenuItem.sectionTitle = @"Social";
return @[calendarMenuItem];
}
+ (NSArray *)routesToRegister {
HSRoute *newItemRoute = [HSRoute routeWithUrl:@"social/new" andAction:^BOOL(id<HSRoutingDelegate> routingDelegate, NSString *url, NSDictionary *parameters) {
// handle route, usually by suppying a UIViewController to the routingDelegate
}];
NSArray *routes = @[newItemRoute]; // could be more routes here too
return routes;
}
我們使用路徑來處理傳入的推送通知,然后我們使用相同的方案將主app和子app進行鏈接——例如河狐,當我們從數(shù)據(jù)源或社交媒體返回數(shù)據(jù)米绕。
HSRoutingDelegate有一點點神奇的是它能繞過當前活動的UINavigationController,然后我們能夠在頂部推出或者創(chuàng)建一個基于上下文的模態(tài)視圖跳轉馋艺,除此之外栅干,一個簡單地JLRoutes基于block的語法也可以達到同樣地目的。
我們還能做什么捐祠?
從長遠來看碱鳞,我們希望擴大我們過去簡單一些共享庫的Kiwi測試或在KIF測試中編譯,這樣每個子app的版本傳遞Kiwi和KIF測試是建立在一個持續(xù)集成的設置中踱蛀,我們可以選擇已知每個很好的版本窿给,發(fā)行主程序的每個版本。
你在多人協(xié)作中是如何組織大型的iOSapp的呢率拒?有沒有更好的方法崩泡?我們很樂意聽到您的聲音!