tags:開(kāi)發(fā)隨筆
雖然mac存在了很多年之后才有了iOS,但是對(duì)很多程序員而言,可能是先熟悉了 UIKit之后才去熟悉AppKit。
我自己也是這樣瓮增。在開(kāi)發(fā)了MarkNote之后,我想把MarkNote的筆記體驗(yàn)延伸到mac哩俭。
有沒(méi)有工具或者庫(kù)可以直接將iOS應(yīng)用轉(zhuǎn)換為macOS應(yīng)用
最初我只是想讓iOS版本的MarkNote照搬過(guò)來(lái)绷跑,于是我想看看是否有捷徑可走。最理想的情況下凡资,有一個(gè)工具你踩,直接把iOS上的代碼轉(zhuǎn)換一下,就可以在mac下運(yùn)行讳苦。或者直接在mac上給iOS應(yīng)用提供一個(gè)runtime吩谦?
查了一下之后鸳谜,發(fā)現(xiàn)真有人有類(lèi)似的想法。
- chameleon將UIKit移植到OS X式廷。然而咐扭,這個(gè)庫(kù)只實(shí)現(xiàn)了UIKit中的大部分API。更可惜的是滑废,這個(gè)庫(kù)三年前就停止更新了蝗肪。
- UMEKit也做了類(lèi)似的事,然而蠕趁,實(shí)現(xiàn)的更少薛闪。而且,7年前就停了俺陋。
這些庫(kù)之所以夭折豁延,在很大程度上是因?yàn)槠鋸?fù)雜程度遠(yuǎn)遠(yuǎn)超過(guò)看起來(lái)的那樣昙篙。桌面應(yīng)用和移動(dòng)應(yīng)用的機(jī)制有很大的不同。雖然UIKit和AppKit共享了很多概念诱咏,但是API依然有很大的不同苔可。比如UIView和 NSView雖然看起來(lái)很像,但是實(shí)現(xiàn)細(xì)節(jié)和使用上有很多不同袋狞。
MarkNote的macOS實(shí)現(xiàn)策略
雖然Chameleon看起來(lái)很有意思焚辅,但是我最終沒(méi)有采用這個(gè)方案。一來(lái)我擔(dān)心其局限性的制約苟鸯,二來(lái)我找到了現(xiàn)在看來(lái)效果非常好的策略同蜻。
我的設(shè)計(jì)策略總結(jié)起來(lái)是以下兩點(diǎn):
- 正視macOS和iOS的不同。 mac有mac擅長(zhǎng)的地方倔毙,在很多方面受的局限更小埃仪,應(yīng)用之可以獲得更好的用戶(hù)體驗(yàn)。比如這篇文字陕赃,雖然在iOS上也可以撰寫(xiě)卵蛉,還是不如mac上寫(xiě)起來(lái)暢快淋漓。
- 盡量在2個(gè)平臺(tái)間復(fù)用代碼么库。對(duì)于MarkNote而言傻丝,我希望核心的代碼是完全通用的。比如markdown解析诉儒,筆記管理等等葡缰。
我采用了以下的辦法來(lái)提高復(fù)用:
-
業(yè)務(wù)邏輯和UI操作徹底分離。 核心的業(yè)務(wù)邏輯在理想情況下應(yīng)該只依賴(lài)于
Foundation
而不依賴(lài)于Cocoa
或者UIKit
忱反。比如Markdown解析泛释,就僅僅依賴(lài)于Foundation。 - 將通用的代碼單獨(dú)建project并打包為framewok温算。MarkNote的工程視圖如下:
其中:
MarkNoteParserOC 是Obj-C版的markdown 解析器怜校,已經(jīng)開(kāi)源。
MarkEditor是語(yǔ)法高亮編輯器注竿,支持iOS和macOS茄茁。不僅為mac版和iOS版的MarkNote使用,同時(shí)也是?InstantCoder的代碼編輯器巩割。其一個(gè)早期版本也已經(jīng)開(kāi)源裙顽。
MarkNoteEngine是筆記管理和?iCloud同步的核心庫(kù),為mac版和iOS版的MarkNote使用宣谈。
MarkNotes是mac版的MarkNote愈犹。
MarkNote是iOS版的MarkNote。
-
一個(gè)Framework闻丑,兩個(gè)target甘萧。
在同一個(gè)工程中萝嘁,分別給macOS和iOS創(chuàng)建不同的target。
-
使用宏來(lái)區(qū)分不同的版本扬卷。
雖然代碼很多相同牙言,但是還是有很多系統(tǒng)之間的差異。這個(gè)時(shí)候可以用宏來(lái)處理怪得。比如我需要給mac版的加openWithCompletionHandler咱枉,而無(wú)需給iOS版添加它,就可以用類(lèi)似下面的代碼:
#if !TARGET_OS_IPHONE
- (void)openWithCompletionHandler:(void (^)(BOOL success))completionHandler;
#endif
-
使用宏來(lái)融合UIKit和AppKit的不同徒恋。
AppKit和UIKit中很多API都是相似的蚕断。比如NSColor和UIColor,NSView和UIView入挣,等等亿乳。這個(gè)時(shí)候,用宏定義一個(gè)別名径筏,可能是融合使用的一個(gè)好辦法葛假。
比如,我的文本編輯器在本質(zhì)上只是NSTextView或者 UITextViewUI的一個(gè)category滋恬。
因此我定義了一個(gè)宏BaseTextView來(lái)分別在macOS上指向NSTextView聊训,而在iOS上指向UITextView:
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#define BaseTextView UITextView
#else
#import <Cocoa/Cocoa.h>
#define BaseTextView NSTextView
#endif
這樣,我后面也就直接用BaseTextView好了恢氯。比如
@interface BaseTextView(Editor)
MarkNote IOS版在2015年5月上線(xiàn)带斑,mac版在2015年11月上線(xiàn)。之后兩個(gè)版本都分別更新了十多次勋拟。上面所描述的策略和方法勋磕,使得我的代碼維護(hù)起來(lái)清晰而簡(jiǎn)單。