試水 Mac 開發(fā) —— 代碼生成器

為何造這個(gè)輪子

國(guó)慶的時(shí)候?qū)懥藗€(gè)小工具來(lái)將 JSON 轉(zhuǎn)化成 Model,也算是我的第一個(gè) Mac App蛛枚,今天重構(gòu)了下,順便跟大家分享下 Mac 開發(fā)有多不方便……

項(xiàng)目地址在這里该园,如果是跟我一樣使用 ObjectMapper 的朋友可以直接下載使用一下兆解。市面上其實(shí)已經(jīng)有一些 JSON 轉(zhuǎn) Model 的工具了,那我為何還要重復(fù)造輪子呢亲雪?顯然是因?yàn)閯e人寫的并不能滿足我的需求勇凭,代碼生成器絕對(duì)是個(gè)需要定制化的東西,畢竟每個(gè)人每個(gè)團(tuán)隊(duì)义辕,都有一套代碼風(fēng)格(不單是指縮進(jìn)虾标、空格、大括號(hào)換不換行這些灌砖,因?yàn)檫@些其實(shí)每個(gè)社區(qū)幾乎都有廣為人接受的最佳實(shí)踐)璧函,比如我寫 Model 不喜歡把 String、Int 等類型的屬性聲明為 Optional基显,而是習(xí)慣給它們一個(gè)初始值蘸吓,但是對(duì)象類型的屬性給它個(gè)初始值我又覺得開銷有點(diǎn)大,一般就用 Optional撩幽。所以我寫的這個(gè)工具也不一定適合你們库继,但是我把我的一點(diǎn)微小經(jīng)驗(yàn)分享給大家箩艺,你們就可以隨意修改我的代碼進(jìn)行定制化或者自己重新造個(gè)最趁手的輪子。當(dāng)然宪萄,如果你確實(shí)有需要艺谆,而自己又沒時(shí)間造輪子,可以留言告訴我拜英,我會(huì)考慮擴(kuò)展功能静汤。

界面

好了話不多說(shuō)先放張截圖,圖中的 JSON 數(shù)據(jù)來(lái)自 GitHub API 文檔:

screenshot.png

UI 非常簡(jiǎn)單聊记,左邊用來(lái)輸入 Model Name 和粘貼 JSON撒妈,右邊是轉(zhuǎn)化結(jié)果。我對(duì) Mac 開發(fā)其實(shí)一無(wú)所知……我就是直接打開 Xcode排监,新建了個(gè) macOS 的項(xiàng)目狰右,然后在 Storyboard 上拖了一個(gè) TextField 和兩個(gè) TextView 進(jìn)去,設(shè)置好約束之后舆床,我準(zhǔn)備把三個(gè)控件連到代碼中……然后我尷尬地發(fā)現(xiàn) NSTextView 連到 IBOutlet 之后棋蚌,類型是 NSScrollView [黑人問號(hào)???]。這個(gè)時(shí)候我還沒有意識(shí)到自己已經(jīng)一只腳踏進(jìn)坑里挨队,我心想難道 NSTextView 是 NSScrollView 的子類谷暮?那我手動(dòng)把它改成 NSTextView 吧……然后迎接我的是各種 crash。后來(lái)仔細(xì)揣摩了一下 Storyboard 里控件的層級(jí)關(guān)系盛垦,我發(fā)現(xiàn)之前真的只是單純地連了個(gè) NSScrollView 到代碼中湿弦,真正的 NSTextView 在一個(gè)奇怪的地方:

TextView.png

我之前拖到代碼中的是最外層的 Bordered Scroll View,它下面還有一層 Clip View腾夯,之后才是我需要的 TextView……

思路分析

有人可能覺得代碼生成器是個(gè)很高端很難實(shí)現(xiàn)的東西颊埃,其實(shí)不然。代碼生成器的難點(diǎn)在于解析輸入蝶俱,而輸入的規(guī)則很多情況下是我們自己定的班利,只要盡可能保證解析規(guī)則簡(jiǎn)單,剩下的工作就是把解析好的信息填到預(yù)定的模版中輸出而已榨呆。拿我的 Model 生成器來(lái)說(shuō)罗标,最難的任務(wù)本應(yīng)該是解析 JSON 字符串,但是我直接把字符串序列化然后生成結(jié)構(gòu)化的 JSON 數(shù)據(jù)积蜻,這一步就只需要兩行代碼:

func json(from text: String) -> Any? {
    guard let data = text.data(using: .utf8) else { return nil }
    return try? JSONSerialization.jsonObject(with: data, options: [])
}

JSON 對(duì)象有了闯割,我們還需要把屬性名進(jìn)行標(biāo)準(zhǔn)化。Swift 中變量名是使用駝峰風(fēng)格的竿拆,如果你的服務(wù)端是用 PHP宙拉,Ruby 之類的語(yǔ)言寫的,返回的 JSON 中的 key 一般是用下劃線分隔單詞的如输,我們可以這么做:

func normalizeVariableName(key: String) -> String {
    var name = key
    if name.contains(underline) {
        var words = name.components(separatedBy: underline)
        name = words.removeFirst()
        words.forEach { name += $0.capitalized }
    }
    return name
}

接下來(lái)就是邊解析 JSON 邊拼接字符串,難點(diǎn)在于嵌套對(duì)象的處理,我使用了遞歸不见,代碼有點(diǎn)長(zhǎng)我就不貼了澳化,大家可以看源碼。還有就是碰到了對(duì)象數(shù)組的話稳吮,對(duì)于該對(duì)象 Model 的命名缎谷,也不太好辦,我的處理是屬性名以“s”或者“List”結(jié)尾的話灶似,就把“s”或者“List”之前的單詞作為 Model 名列林,至于其它的情況(譬如 people、productArray 等)酪惭,就管不過來(lái)了希痴,真的碰到了就手動(dòng)修改下。按《程序員修煉之道》中說(shuō)的:

這是被動(dòng)代碼生成器的一個(gè)有趣的特性:它們不必完全正確春感。你需要在你投入生成器的努力和你花在修正其輸出上的精力之間進(jìn)行權(quán)衡砌创。

在 Mac 開發(fā)中使用 RxSwift

感覺 RxCocoa 對(duì) Cocoa 的支持并不好,譬如沒有為 NSTextView 和 NSTextField 提供 rx.string鲫懒、rx.stringValue 之類的擴(kuò)展嫩实。不過關(guān)系也不大,可以自己用 PublishSubject 去接一下相應(yīng)的委托方法窥岩,然后我們只要訂閱這個(gè) PublishSubject 就好了:

extension ViewController: NSTextViewDelegate {
    func textDidChange(_ notification: Notification) {
        source.onNext(sourceText.string ?? "")
    }
}

extension ViewController: NSTextFieldDelegate {
    override func controlTextDidChange(_ obj: Notification) {
        let model = modelNameText.stringValue.isEmpty ? "Model" : modelNameText.stringValue
        modelName.onNext(model)
    }
}

func parse() {
    Observable
        .combineLatest(source, modelName) { (json: $0.0, modelName: $0.1) }
        .map { (self.json(with: $0.json), $0.modelName) }
        .map(convert)
        .subscribe(onNext: {
            self.resultText.string = $0
        })
        .addDisposableTo(bag)
}

我用 combineLatest 把兩個(gè) Subject 組合在一起甲献,無(wú)論哪個(gè) Subject 發(fā)出新事件,都會(huì)接收到該新事件和另一個(gè) Subject 的發(fā)射過的最新事件颂翼。具體效果就是晃洒,只有在左側(cè)把 Model Name 和 JSON 都填上,右邊才會(huì)顯示結(jié)果疚鲤,之后無(wú)論是改變 Model Name 還是 JSON 內(nèi)容锥累,右側(cè)結(jié)果都會(huì)跟著變化。

最后

覺得有點(diǎn)意思的話可以隨手 Star 一個(gè)哈 ^ ^

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末集歇,一起剝皮案震驚了整個(gè)濱河市桶略,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诲宇,老刑警劉巖际歼,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異姑蓝,居然都是意外死亡鹅心,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門纺荧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)旭愧,“玉大人颅筋,你說(shuō)我怎么就攤上這事∈淇荩” “怎么了议泵?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)桃熄。 經(jīng)常有香客問我先口,道長(zhǎng),這世上最難降的妖魔是什么瞳收? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任碉京,我火速辦了婚禮,結(jié)果婚禮上螟深,老公的妹妹穿的比我還像新娘谐宙。我一直安慰自己,他們只是感情好血崭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布卧惜。 她就那樣靜靜地躺著,像睡著了一般夹纫。 火紅的嫁衣襯著肌膚如雪咽瓷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天舰讹,我揣著相機(jī)與錄音茅姜,去河邊找鬼。 笑死月匣,一個(gè)胖子當(dāng)著我的面吹牛钻洒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锄开,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼素标,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了萍悴?” 一聲冷哼從身側(cè)響起头遭,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎癣诱,沒想到半個(gè)月后计维,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撕予,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年鲫惶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片实抡。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡欠母,死狀恐怖欢策,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赏淌,我是刑警寧澤猬腰,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站猜敢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏盒延。R本人自食惡果不足惜缩擂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望添寺。 院中可真熱鬧胯盯,春花似錦、人聲如沸计露。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)票罐。三九已至叉趣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間该押,已是汗流浹背疗杉。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚕礼,地道東北人烟具。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像奠蹬,于是被迫代替她去往敵國(guó)和親朝聋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,520評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理囤躁,服務(wù)發(fā)現(xiàn)冀痕,斷路器,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • 1. 捧著半兒拉西瓜挖著吃的時(shí)候 2. 躺在床上上無(wú)憂無(wú)慮發(fā)呆的時(shí)候 3. 上完一天班后躺在床上看電影割以,并拿著半兒...
    馬雨_Val閱讀 150評(píng)論 0 1
  • 回家作業(yè): 1.workbook:Homework1 2.Tutorial book:Homework1 在線練習(xí)...
    EmilyJia閱讀 324評(píng)論 0 0
  • 最近經(jīng)逞狭ぃ看到一些產(chǎn)品經(jīng)理寫的文章猜极,最多的還是在分享產(chǎn)品經(jīng)理是干什么的,造成他們有想分享沖動(dòng)的原因大概是他們接觸到了...
    秦時(shí)明月V閱讀 1,382評(píng)論 4 6