一步步建立自己的 iOS 框架
建立你的第一個iOS框架
如果你曾經試圖去建立一個自己的iOS框架的話,你應該知道這件事並不是那些畏懼困難的人能夠成功完成的-畢竟管理依賴和編寫測試並不容易。這篇文章將從開始到最終完成一步步的進行講解,以便你掌握後可以更好的建立自己的框架。
在教程中我們會構建一個框架,框架裡面會暴露一個名為RGBUIColor(red:green:blue)的函式,該函式會返回使用這些引數建立的UIColor物件。我們會使用Swift語言,並且使用Carthage作為依賴項的管理工具。我們的框架將會支援通過Carthage、CocoaPods或者git來使用。
讓我們開始吧!
建立Xcode工程
- 選擇 File
- 在左側的選擇 iOS -> Framework & Library,右側選擇“Cocoa Touch Framework”。
- 點選“下一步”,並填寫選項提示。確保以及勾選了“Include Unit Tests”。
- 選擇工程儲存的位置。
- 不要勾選“Create Git repository on My Mac”,我們在後面手動進行設定。
- 點選“建立”並且開啟工程。
- 選擇File -> Save As Workspace並使用工程相同的名字儲存到相同的目錄中。之所以建立workspace是因為我們需要新增Carthage中的依賴作為子模組;使用Xcode
編譯他們的時候必須是在一個workspace中。 - 選擇File -> Close Project關閉工程。
- 然後選擇File -> Open開啟*workspace*檔案。
- Xcode左上角的scheme並選擇“Manage Schemes”。我們需要確保sheme勾選了“shared”,以便能使用“Carthage”來構建工程。
初始化git
首先,切換到工程所在的目錄。
- 執行git init初始化空版本庫。
- 建立一個 .gitignore的檔案。該檔案會過濾一些Xcode或者依賴檔案中一些我們不想也不需要上傳的檔案。
這裡是一個標準的Swift工程的gitignore檔案,我們只是添加了.DS_Store
123456789101112131415161718192021222324252627282930313233 | ## OS X Finder.DS_Store## Build generatedbuild/DerivedData## Various settings*.pbxuser!default.pbxuser*.mode1v3!default.mode1v3*.mode2v3!default.mode2v3*.perspectivev3!default.perspectivev3xcuserdata## Other*.xccheckout*.moved-aside*.xcuserstate*.xcscmblueprint## Obj-C/Swift specific*.hmap*.ipa# Swift Package Manager.build/# CarthageCarthage/Build |
新增Carthage和依賴項
- 在工程的檔案目錄下建立一個名為Cartfile的檔案以及執行時的依賴性。我們新增**Curry]**([連結)
github “thoughtbot/Curry”
- 建立一個名為Cartfile.private的檔案。它會負責私有的一些依賴就像我們的測試框架一樣。我們使用Quick和Nimble。
12 github"Quick/Quick"github"Quick/Nimble" - 新建bin/setup指令碼。它可以提供一個簡單的方式來處理依賴和工程,無論時對於貢獻者還是我們自己。
123 mkdir bintouch bin/setupchmod+xbin/setup - 開啟bin/setup並將一下程式碼加入:
1234567 #!/usr/bin/env shif!command-vcarthage>/dev/null;thenprintf'Carthage is not installed.n'printf'See https://github.com/Carthage/Carthage for install instructions.n'exit1fi carthage update--platform iOS--use-submodules--no-use-binaries
在這個腳本里面,我們假設使用者一句安裝了Carthage連結,然後我們使用update命令來安裝那些依賴項。
我們使用–use-submodules,所有那些依賴項會以子模組的方式被新增。當用戶需要的時候,他就可以直接使用我們的框架而不需要使用Carthage。我們使用了–no-use-binaries,所有這些依賴項都會在我們自己的系統上進行編譯。
當bin/setup建好後,我們直接在終端執行指令碼讓Cartfile自行下載依賴項。
現在我們就可以設定我們的工程並且編譯這些依賴項了。
新增依賴到工作區
因為我們的依賴是作為子模組,我們需要將這些自模組新增到工作區。
1.開啟Carthage/Checkouts然後將每個依賴項的.xcodeproj新增到工作區。你可以使用直接拖拽到專案的工作區。
新增完結束後:
連結執行時依賴
- 在工作區的導航欄選擇”RGB” ,然後在中間選擇”RGB”目標,進而選擇”Build Phases”,展開”Link binary with libraries”。
- 點選”+”然後選擇Curry.framework框架的Curry-iOS。
- 點選新增。
連結開發依賴項
- 在中間的工具欄選擇”RGBTests”。
- 使用上面一樣的步驟,將”Quick”和”Nimble”框架新增到”Link binary with libraries”。
當我們將依賴新增到兩個目標的時候,Xcode會自動在”Build Settings”下新增”Framework Search Paths”。我們可以在”RGB”和”RGBTests”中移除,因為同處同一工作區, Xcode將他們本身的一部分。 - 選擇目標下的兩個目標,選中”Build Settings”下的”Framework Search Paths”,然後按“退格鍵”刪除。
- 接下來,在導航欄選擇”RGB”工程的時候,你就會看見下面you三個剛剛新增的三個框架。然後全選這三個框架,然後右擊選擇”New group from selection”然後將他們放到一個組裡, 我將組命名為”Frameworks”。
現在Carthage已經設定完成,接下來是CocoaPods。
新增CocoaPods支援
為了新增CocoaPods支援,我們需要在工程的根目錄新建.podspec,並且包含工程的資訊。
- 新建RGB.podspec檔案。
- 將下面的例項拷貝並複製到檔案中(自行對照修改相應的部分)。
- 使用專案的資訊來設定那些選項。更多的選項詳情連結,但是該工程中你所需要的那些選擇如下。
123456789101112131415161718 | Pod::Spec.new do|spec|spec.name="RGB"spec.version="1.0.0"spec.summary="Sample framework from blog post, not for real world use.Functional JSON parsing library for Swift."spec.homepage="https://github.com/jakecraige/RGB"spec.license={:type=>'MIT',:file=>'LICENSE'}spec.authors={"Jake Craige"=>'[email protected]',"thoughtbot"=>nil,}spec.social_media_url="http://twitter.com/thoughtbot"spec.source={:git=>"https://github.com/jakecraige/RGB.git",:tag=>"v#{spec.version}",:submodules=>true}spec.source_files="RGB/**/*.{h,swift}"spec.requires_arc=truespec.platform=:iosspec.ios.deployment_target="9.1"spec.dependency"Curry",'~> 1.4.0'end |
這裡面需要注意到的一行是spec.dependency “Curry”, ‘~> 1.4.0’。因為我們需要支援CocoaPods,我們假設框架的使用者會使用CocoaPods而不是Carthage,
所有我們我們在最後一行也宣告依賴而不僅僅只在Carthfile宣告。
當我們設定好了之後,我們在終端中執行pod lib lint命令測試所有的東西是不是都配置好了。如果沒錯的話,我們能看見如下的提示:
當工程的依賴項設定好後,我們就可以寫程式碼了。但是在我們開始之前,先提交程式碼。
1 | git commit-am"Project and dependencies set up" |
編寫第一個測試
開啟RGBTests/RGBTests.swift檔案,你可以看見一個預設的模版。她使用了@testable和XCTest(,但是接下來我們會作出一些調整。
首先,我們會移除@testable,因為我們需要測試那些框架使用者可能呼叫的API介面。隨著框架的增長,我們可能會需要@testable去測試那些不是作為公共介面暴露的部分;總的來說,就是我們想避免測試那些暴露給使用者的介面。這個特性在測試應用的時候會更加有效,而不是在框架測試中。
伴隨者可測試性,你係那種能夠在Swift 2.0框架和應用中編寫測試並且不需要要測試所有的internal和public部分。在XCTest目標而不是其他框架或者應用的測試程式碼中
使用@testable import {ModuleName}。
我們使用Quick和Nimble作測試。Quick提供以一個行為驅動型別的測試介面,與RSpec和Specta非常相近;Nimble給我們提供了強大的斷言以及少量模版就能寫成非同步程式碼的能力。
寫完之後,程式碼如下:
12345678910111213 | import Quickimport Nimbleimport RGBclassRGBTests:QuickSpec{override func spec(){describe("RGB"){it("works"){expect(true).to(beTrue())}}}} |
使用快捷鍵CMD + U或者Product -> Test執行測試程式碼,會顯示測試成功。
所以,到現在已經完成了!
開玩笑而已。讓我們來一些真正的測試。
我們暴露一個RGBUIColor(red: 195, green: 47, blue: 52)呼叫介面,介面會返回一個漂亮的thoughtbot red的UIColor。
程式碼如下:
12345678910111213 | describe("RGBUIColor"){it("is a correct representation of the values"){let thoughtbotRed=UIColor(red:CGFloat(195/255),green:CGFloat(47/255),blue:CGFloat(52/255),alpha:1)let color=RGBUIColor(red:195,green:47,blue:52)expect(color).to(equal(thoughtbotRed))}} |
如果你此時執行此時的話,會像預料中的那樣-失敗。因為Swift語言的型別檢查會組織我們執行一個沒有定義的RGBUIColor函式。接下來讓我們完成它。
編寫實現程式碼
右擊RGB選擇新建一個檔案。建立一個名為RGBUIColor.swift的檔案,並將下面的程式碼拷貝過去。
1234567891011121314 | import Curryfunc RGBUIColor(red red:Int,green:Int,blue:Int)->UIColor{returncurry(createColor)(red)(green)(blue)}privatefunc createColor(red:Int,green:Int,blue:Int)->UIColor{returnUIColor(red:CGFloat(red/255),green:CGFloat(green/255),blue:CGFloat(blue/155),alpha:1)} |
這裡使用Curry作為一個執行時的依賴性的例子來使用。這裡採用了一個不標準的使用斌強沒有提供任何值。讓我們繼續測試!
第一眼看過去,我們可能會感到很奇怪。我們明明已經定義了RGBUIColor函式啊?
確實我們定義了該函式但是,我們並沒有將她宣告為public。
這意味著,如果有人繫有人使用我們的框架的話,他們是不能使用這個函式介面的。如果你想看見什麼不同的話,將@testable添加回來,你會發現你的測試通過了。
通過這個錯誤我們就知道為什麼要在iomport前面將@testable移除。這能讓我們在釋出框架之前更好的捕捉到錯誤。
讓我們將函式宣告為public,來修復這個問題。執行測試,問題解決了。然後我們提交程式碼。
1 | git commit-am"Completed my first iOS framework!" |
我寫的程式碼。轉載請表明出處,謝謝。