練手 Cocoa 開發(fā),開發(fā)并開源了一款 fir.im 的 Mac 客戶端,此篇主要記錄其開發(fā)歷程,總結(jié)一下遇到的問題
緣由
某一天突然翻到自己 GitHub 下有一個(gè)空倉庫,名字叫 fir-mac,提交時(shí)間在 2016 年 6 月 22 號(hào)事秀,恍然大悟原來又是棄坑項(xiàng)目之一。
由于從事 iOS 開發(fā)后一直對(duì) macOS 開發(fā)比較感興趣野舶,感覺 Mac 應(yīng)用有一種天生的美感易迹,更是在 OS X 10.10 Yosemite 之后的扁平化、毛玻璃平道、各種窗口細(xì)節(jié)的優(yōu)化感到和往日使用 Windows 的差異睹欲;那既然都已入蘋果的坑,何不順勢(shì)玩玩 Cocoa 桌面開發(fā)呢一屋, 業(yè)余時(shí)間前后寫了 MyMacMusicPlayer 和 CubicBezier窘疮,也算小入了一點(diǎn)點(diǎn) Cocoa 的門,想繼續(xù)找項(xiàng)目在實(shí)戰(zhàn)中磨練自己冀墨,然后因?yàn)橛卸螘r(shí)間用 fir.im 比較多闸衫,隧萌生了為其做一個(gè) Mac 客戶端的想法。
準(zhǔn)備
開發(fā)之前首先得想想會(huì)遇到最大的問題是什么诽嘉?那應(yīng)該是 fir 的接口蔚出,官方有沒有開放接口?接口有沒有限制含懊?這是我作為一個(gè)第三方開發(fā)人員無法控制的事情身冬,所辛 fir 在很早的時(shí)候就開放了他們的 API 及其文檔,并且借口都比較齊全岔乔,證明他們對(duì)此是保持開放態(tài)度,那么接口的障礙沒有了還有其他問題嗎滚躯?
這時(shí)我就預(yù)估一下了應(yīng)用的整體功能模塊雏门,以及大致實(shí)現(xiàn)方法,把項(xiàng)目難點(diǎn)提前篩出來看看掸掏,fir-mac 作為一個(gè)基于 Mac 系統(tǒng)的 GUI 客戶端茁影,首先是 UI 的搭建,最初想法丧凤,用一個(gè)列表(NSTableView)呈現(xiàn)賬戶下所有的應(yīng)用募闲,然后點(diǎn)擊應(yīng)用可以展開其詳細(xì)信息,感覺應(yīng)該可以實(shí)現(xiàn)愿待,當(dāng)然 UI 動(dòng)畫浩螺、優(yōu)化什么的暫且先不提靴患,畢竟先得把功能做出來才是第一步。
功能架構(gòu)
功能上主要參考 fir web 端要出,包括上傳應(yīng)用鸳君、查看應(yīng)用列表、查看應(yīng)用詳情患蹂,差不多就這樣或颊,簡(jiǎn)簡(jiǎn)單單實(shí)實(shí)在在。
后來有朋友在微博上評(píng)論說想要掃二維碼下載的功能传于,很快也增加進(jìn)去了囱挑。
如果你是本軟件的使用者,有什么功能上的意見或想法歡迎與我聯(lián)系哦沼溜。
UI設(shè)計(jì)
UI 最終成品基本上也繼承與最初的想法而來看铆,簡(jiǎn)單的 List,彈開應(yīng)用詳情盛末,采用 Vertical Split View Controller 分欄設(shè)計(jì)弹惦,把左右兩邊分開并各自獨(dú)立 NSViewController,從而使業(yè)務(wù)邏輯分離開來悄但,在 Storyboard 上看起來是這個(gè)樣子:
右邊的塊區(qū)域用了 NSBox 作收納子元素和框樣式棠隐,隱藏掉了 Title,想想在 iOS 上搞這個(gè)還得弄個(gè) UIView 設(shè)設(shè)圓角什么的檐嚣。(有時(shí)候總會(huì)想到 CocoaTouch 里的東西去)
Cocoa 里原生提供了三種列表組建助泽,分別是 TableView、Outline View嚎京、Source List嗡贺,而 Outline View、Source List 都是基于 NSOutlineView
的定制鞍帝,而 NSOutlineView
則 NSTableView
的定制诫睬,所以歸其總都是 NSTableView
一個(gè)東西。
fir-mac 左邊側(cè)邊欄的列表則用的 TableView帕涌,自定義其 Cell 來展示的:
最后的實(shí)際運(yùn)行效果如下:
可以看到其實(shí)和 Storyboard 相似度很高摄凡,開發(fā)此類小應(yīng)用使用 Storyboard 構(gòu)建 UI 效率非常高,并且因?yàn)榇翱谠O(shè)計(jì)成固定大小蚓曼,要么縮小要么關(guān)閉亲澡,不能縮放,所以也不用去拉 AutoLayout纫版,真的省心床绪。
困難1:接口的調(diào)試
從最開始本以為很簡(jiǎn)單的 form-upload 操作,廢了兩晚上時(shí)間才搞定,因?yàn)?fir 的應(yīng)用包是存在靜態(tài)文件服務(wù)器上的癞己,而且后端采用了不同的云存儲(chǔ)商膀斋,所以上傳文件需要兩個(gè)步驟,第一步先獲取憑證末秃,第二步再用拿到的憑證進(jìn)行表單上傳概页,因?yàn)閼?yīng)用二進(jìn)制和應(yīng)用圖標(biāo)是分開上傳,所以一次提交應(yīng)用的操作其實(shí)會(huì)發(fā)三個(gè)請(qǐng)求练慕,[拿 Token] -> 上傳圖標(biāo) -> 上傳應(yīng)用惰匙。
有點(diǎn)不明白的是應(yīng)用圖標(biāo)本身也是從其應(yīng)用文件中提取出來的,為什么不把這一步拿到服務(wù)端做呢铃将?為了節(jié)省服務(wù)器資源嗎项鬼?還有其應(yīng)用信息,感覺放到服務(wù)端做會(huì)更可靠劲阎,因?yàn)?fir 不止一個(gè)客戶端工具去做上傳绘盟,比如 Website、Command Line Tool悯仙,各種 IDE 插件龄毡,每個(gè)端都需要去編寫這套包解析和兩次上傳請(qǐng)求,首先維護(hù)成本增高锡垄,解析提交的數(shù)據(jù)還不一定是可靠?jī)?nèi)容沦零,造成臟數(shù)據(jù)提交到服務(wù)端。
好吧先不糾結(jié)這個(gè)接口設(shè)計(jì)了货岭,想說的是為啥在一個(gè)上傳文件的功能上繞了彎路路操,最后找到問題是因?yàn)橐粋€(gè)參數(shù)的參數(shù)名寫錯(cuò)了,并且服務(wù)端沒有錯(cuò)誤反饋千贯,仍然返回的請(qǐng)求成功屯仗,這我就懵了,然后抓了一下 web 所請(qǐng)求的參數(shù)值搔谴,拿過來用錯(cuò)誤的參數(shù)名提交居然也成功了魁袜。這就是為啥蒙圈在這這么久的原因。
困難2:包解析
本以為 fir 的接口如上文所說是由服務(wù)端解析應(yīng)用包己沛,提取出應(yīng)用信息比如應(yīng)用名慌核、版本號(hào)等等,結(jié)果發(fā)現(xiàn)其實(shí)是本地解析拿到這些信息然后再提交給接口申尼,拿 iOS 的包來說,后綴為 .ipa
垫桂,在 macOS 本地的流程大致如下:
- 用
mktemp
命令創(chuàng)建臨時(shí)目錄 - 用
unzip
解壓.ipa
到臨時(shí)目錄 - 逐級(jí)目錄查找到
Payload
文件夾 - 讀取
Info.plist
文件 - 根據(jù)
CFBundleIdentifier
师幕、CFBundleShortVersionString
… 提取出應(yīng)用信息和 ICON 圖標(biāo) - 刪除之前生成的臨時(shí)目錄
mktemp
是一個(gè) linux 命令會(huì)在系統(tǒng)層級(jí)創(chuàng)建一個(gè)臨時(shí)目錄,路徑是隨機(jī)名大致如下 /var/folders/r3/x98gzjpn7rxct9y4rtnjsv5m0000gn/T/
,不會(huì)重復(fù)保證一定安全性
unzip
是 linux 下解壓 zip 文件的命令霹粥,通過參數(shù) -d
來指定解壓目的地為之前生成的臨時(shí)目錄
讀取 Info.plist
文件本打算用 defaults
命令來完成灭将,后來發(fā)現(xiàn)實(shí)在不如讀取進(jìn) Dict 操作方便
以上便是用戶選擇一個(gè) .ipa
文件提交上傳前所需做的工作,由于 Android 包 .apk
數(shù)據(jù)結(jié)構(gòu)又不一樣后控,所以支持 Android 應(yīng)用上傳需要另外編寫一套解析邏輯庙曙,故目前 .apk
上傳還未支持。(話說寫這個(gè)真累浩淘,項(xiàng)目開源歡迎有志之士共享一份力量0破印)
引用
由于應(yīng)用主要建立在與 fir.im 接口的 HTTP 通信上,所以引入了 Alamofire 來做所有的網(wǎng)絡(luò)請(qǐng)求张抄,包括數(shù)據(jù)拉取和文件上傳砂蔽;然后因?yàn)榻涌诜祷厮袛?shù)據(jù)格式均為 JSON,為了人性化編程又引入了 SwiftyJSON署惯;項(xiàng)目列表還存在網(wǎng)絡(luò)圖加載左驾,為了省事也直接用了 Kingfisher;因?yàn)橛辛诉@些項(xiàng)目的積淀极谊,我們才能把更多的注意力放在其他事情上诡右,避免很多重復(fù)而基礎(chǔ)的工作,感謝開源轻猖!
開源協(xié)議
關(guān)于選擇開源協(xié)議帆吻,一直都沒有太深刻的理解,只知道 MIT 最開放自由蜕依,那就 MIT 咯桅锄,后來看到微博上大家評(píng)論說 MIT 難道不怕又被翻去上架賣錢嗎?之前就有 PPRows 的事情样眠,PPRows 的作者也來跟我說讓我換掉開源協(xié)議友瘤。
翻看了一下,像開發(fā)組件大部分都采用 MIT檐束,寬松辫秧,但是很多完整項(xiàng)目都采用了 GPL-3.0,比如 PHP CMS 系統(tǒng) Drupal 被丧,播放器 iina盟戏、SQLite 數(shù)據(jù)庫瀏覽器 sqlitebrowser 等等,他們采用 GPL-3.0 更是為了限制商業(yè)使用甥桂。作為一個(gè)開源作者的心愿首先肯定是希望自己的項(xiàng)目能發(fā)展起來柿究,項(xiàng)目被他人商業(yè)化估計(jì)很多作者都是不愿看到的事,開源界關(guān)于協(xié)議版權(quán)的鬧的事情已經(jīng)很多了黄选,希望那些不勞而獲通過開源項(xiàng)目結(jié)合流量與市場(chǎng)榨取用戶的人蝇摸,能把更多注意力放在做事情本身婶肩,而不是一點(diǎn)蠅頭小利和旁門左道。
一不小心吐槽了一番貌夕,本文差不多就這樣結(jié)束了律歼,如果你還沒有 star 這個(gè)項(xiàng)目,趕緊來吧:https://github.com/isaced/fir-mac