為你的移動應用選擇正確的架構是一件相當大的事情,這會對你的工作流程造成影響,陷入面對的問題,可能是一筆巨大財富也可能是一個巨大負擔。
HubSpot的是一個全功能的app。他是一個分析的app,也是一個設計媒體的app,還是一個郵件app,并且是一個聯(lián)系人管理的app(可能還有更多地驚喜),這些功能集合在一個app中。去年夏天,當我們開始構建這個相當復雜的app時,我們知道必須有一個可以容易擴展它的架構。
實際上,我們把每個子app當做一個完全完整地獨立的應用程序,然后使用CocoaPods將它們集成到主應用程序中。
截圖中,你可以看到各個子程序的源程序,儀表盤,社交媒體,實際上既是一個獨立的iPhone應用程序,也可以納入主要的應用程序的菜單中選擇一個應用程序。

這會給我們代理一些巨大的好處:
- 最關鍵的是,我們很容易就能保證每個子應用程序的主分支是準備好發(fā)布,并可以推送一個子應用程序的特定版本。
- 我們在編譯上花費多一些時間,而減少了較多合并的時間。每個獨立app的沙盒可以很容易的在子應用程序內循環(huán),花最少的時間與其他應用程序進行集成。如果你在一個以上的iOS團隊工作過,你毫無疑問已經經歷過.xcodeproj合并時的痛苦。雖然他們可以解決,但這個痛苦地步驟我們只想對他敬而遠之。
- 我們能夠在必要時單獨部署每個應用程序,這樣我們在一個獨立的app可用性測試上將會是驚人的。我們可以更早地將我們的app在合成之前提交給測試者,例如導航功能實現(xiàn)之前,這樣我們可以得到更高質量,和更有針對性的反饋。
- 由于子應用程序之間的用戶流只是基于URL路徑(后面會詳細解釋)完成的,這意味著路徑可以內置或者文檔化,而不是從一堆UIViewController以搜索正確的方式來實例化一個特定的視圖,當然必須要事先定義好路徑。建立類似演練教程或者新的推送通知時,這是非常有用的。
這種架構在一個兩人以上的團隊建立多方面的iOSapp時,能節(jié)省出巨大的時間。
從網頁學習
這種將移動端app分割成獨立app的靈感來自于我們在HubSpot網頁端架構的成功。
HubSpot網頁端架構是高速發(fā)展和可擴展的。正如我同事所寫,我們使用各種工具盒技術,讓我們在一天內共同部署300次。這一點至關重要,當HubSpot的產品套件有幾個不同的松耦合應用組成:分析,社交媒體,電子郵件,博客和報告工具。
在web上,我們可以編譯,測試和發(fā)布HubSpotapp獨立的一小部分——包括后臺API和用Java編寫的任務,前端CoffeeScript工程,Python工程。為什么移動端不也這么做?
CocoaPods:用起來!
CocoaPods是iOS中出色的依賴管理解決方案,是一個把所有東西聚合的關鍵工具。
一個多應用程序架構可能對你的使用的案例過度使用,但CocoaPods絕對不會——盡管你只是生成一小撮使用案例,視圖組件或者網絡的第三方庫——花費幾分鐘來設置它是完全值得你去投入的。Ruby,gem類似的語法在app中整合開源組件中變得幾乎無縫。
核心庫和共享資源,如登錄,樣式類,API/認證持久性和訪問可以構建成帶Kiwi測試和podspec文件的獨立工程。
我們將他們發(fā)布在私有CocoaPods倉庫中,然后在實際完全編譯的應用中包含他們。然而,我們進一步把它們建立成各個子app——所有的社交媒體,電子郵件,資源,使用樣例——分成一個單獨的podspec,然后使用CocoaPods將它們編譯到一個app中。
這意味著,我們可以在內部發(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的呢?有沒有更好的方法?我們很樂意聽到您的聲音!