Transferred from: Https://robots.thoughtbot.com/creating-your-first-ios-framework
If you've ever tried to create your own IOS framework, you're know that it's not for the faint of heart–managing Dependenci Es and writing tests doesn ' t make it any easier. This tutorial would walk you through creating your first IOS framework from start to finish so the can go out and crea Te your own.
We ' re going to build a framework this exposes a function called that RGBUIColor(red:green:blue)
returns a new UIColor
created from those values. W E ' ll build it using Swift, with Carthage as our dependency manager. Our framework would be consumable using Carthage, CocoaPods, or git submodules.
Let ' s begin!
Setting up the Xcode Project
- Select file→new→project.
- Select ios→framework & Library from the left sidebar and select "Cocoa Touch Library" on the right.
- Click "Next" and fill in the option prompts. Make sure to select the "Include Unit Tests" check box.
- select where you ' d like to save your project.
- uncheck "Create Git repository on My Mac", we'll manually set it up later.
- click "Create" and the project would open in Xcode.
- go to file→save as Workspace and Save it in the same directory as your Xcode project With the same name. We put the project in a workspace because we are adding our Carthage dependencies as submodules; They must is in a workspace for Xcode to build them.
- close the Xcode project with file→close project .
- open the workspace with file→open .
- click on the scheme on the top left of Xcode and select "Manage schemes". We need to mark our scheme as "GKFX" so that the project can is built with Carthage.
- locate the "RGB" scheme and check the "Shared" check box and click "Close".
Let's jump over to the terminal.
Initializing Git
First, navigate to the directory of your saved your project in.
- Run to
git init
initialize an empty repository.
- Create a
.gitignore
which would keep out some pesky Xcode and dependency files we don ' t want to track in Git.
Here's a standard one for Swift projects with a few modifications. We added and .DS_Store
removed Fastlane and extra comments.
## 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
Adding Carthage and Dependencies
- Create a file in your project directory named and the
Cartfile
runtime dependencies to it. We ll add Curry.
github "thoughtbot/Curry"
- Create a
Cartfile.private
. It would house private dependencies as our test frameworks. We ' ll use Quick and nimble.
github "Quick/Quick"github "Quick/Nimble"
- Create a
bin/setup
script. It's used to give we contributors (andUS) A simple-to set up the project and the dependencies.
mkdir bintouch bin/setupchmod +x bin/setup
- Open up and
bin/setup
fill it with:
#!/usr/bin/env shif ! command -v carthage > /dev/null; then printf ‘Carthage is not installed.\n‘ printf ‘See https://github.com/Carthage/Carthage for install instructions.\n‘ exit 1ficarthage update --platform iOS --use-submodules --no-use-binaries
In this script, we make sure the user have Carthage installed and run its update
command to install the IOS dependencies.
We ' re using So, our --use-submodules
dependencies is added as Submodules. This allows users and consume our framework outside of Carthage if they want. We use So, our dependencies is built on our --no-use-binaries
system.
bin/setup
with created, let's run it so that Carthage would download our dependencies.
- In the terminal, run
bin/setup
.
Now we need to set up our project to build and link the new dependencies.
Adding Dependencies to the Workspace
Since Our dependencies is submodules, we need to add them to our workspace.
- Open up and add each dependency's to the root of the
Carthage/Checkouts
.xcodeproj
workspace. They can dragged from Finder to the navigator of the Xcode project.
When you ' re-done it should look like:
Link Runtime Dependencies
- With ' RGB ' selected in the Navigator and the ' RGB ' target selected on the middle sidebar, select the ' Build Phases ' tab an D Expand the ' Link Binary with Libraries ' section.
- Click the "+" icon and select the from the
Curry.framework
Curry-iOS
target.
- Click "Add".
Link Development Dependencies
- Select the "rgbtests" target from the middle sidebar.
- Using the same process as before, add the Quick and nimble frameworks to the ' Link Binary with Libraries ' section for this Target.
When adding dependencies to each target, Xcode would automatically add them to the "Framework Search Paths" under the "Buil D Settings "tab. We can remove these from the ' RGB ' and ' rgbtests ' target because Xcode treats them as implicit dependencies due to them be ing in the same workspace.
- Select the target, locate the "Framework Search Paths" setting, highlight it, and press "BACKSPACE" on your keyboard.
- Next, look in the ' RGB ' project in the Navigator; You'll see there is three new frameworks at the root level. To keep the organized, highlight all three, right click and select "New Group from selection" to place them in a Nam Ed Group. I ' ll call mine "frameworks".
Now that Carthage are set up, let's add CocoaPods.
Adding CocoaPods Support
To add CocoaPods support, we need to create a at the root of our project and fill with our .podspec
project info.
- Create a file named
RGB.podspec
.
- Copy and paste the sample below into the file.
- Fill in the options with your project s details. There is a lot more options available to you, but these is what's needed for this project.
Pod:: spec.newDo |spec| Spec.name ="RGB" spec.version ="1.0.0" spec.summary ="The Sample framework from the blog post, not to real world use." Spec.homepage ="Https://github.com/jakecraige/RGB" Spec.license = {type: ' MIT ', file: ' LICENSE '} Spec.authors = { "Your Name" = = ' [email protected] '} spec.social_ Media_url = "Http://twitter.com/thoughtbot" Spec.platform = :ios, "9.1" Spec.requires_arc = true spec.source = {git: "Https://github.com/jakecraige/RGB.git", tag: #{spec.version} ", submodules: true} spec.source_files = Span class= "string" > "rgb/**/*". {H,swift} "spec.dependency " ~> 1.4.0 " End
One line to pay attention to is spec.dependency "Curry", ‘~> 1.4.0‘
. Because we ' re supporting CocoaPods, we expect the consumers of our frameworks to being using it instead of Carthage, so we hav E to specify dependencies here and in the Cartfile
.
Once This was set up we can run the pod lib lint
command to test that everything is configured properly. If all goes well, we'll see something as this:
With the project and dependencies set up, we ' re almost ready to write some code. Before we do and let's create our first commit.
"Project and dependencies set up"
Writing the first Test
Open So, we can take a look at the RGBTests/RGBTests.swift
default template. It uses @testable
XCTest
and, but we'll be changing both of these.
we ' ll remove @testable
because we want to test the public API that consumers of the framework would use. As our framework grows, we could need to test parts that is not @testable
exposed publicly; generally we want to avoid We're testing what's exposed to the consumer. This feature are most useful in testing applications rather than frameworks.
From the Apple Docs on testability:
With testability, you is now able to write tests of Swift 2.0 frameworks and apps without have to do all of your inte rnal routines public. Use @testable Import {modulename} in your test source code to make all public and internal routines usable by Xctest Targe TS, but isn't by the other framework and app targets.
We ll use the Quick and nimble for testing. Quick provides a nicer testing interface with a behavior-driven style that's very similar to RSpec and specta; Nimble gives us many powerful assertions and the ability to write asynchronous code with less boilerplate.
Once Those changes is made, the test file should look like:
import Quickimport Nimbleimport RGBclass RGBTests: QuickSpec { override func spec() { describe("RGB") { it("works") { expect(true).to(beTrue()) } } }}
Run the tests with? U or product→test and they should be green.
And ... we ' re done!
Just kidding. Let ' s write some real tests.
We expect that calling would RGBUIColor(red: 195, green: 47, blue: 52)
return a beautiful "Thoughtbot red" UIColor
.
In code, this looks like:
describe ( "Rgbuicolor") {It ( "is a correct Representation of the values ") {let thoughtbotred = uicolor (r Ed: 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))}}
If We run the tests, now they would fail as we expect. Swift's type checking would prevent us from running the tests because we never defined the RGBUIColor
function.
Let's do that.
Writing the implementation
Right click on the "RGB" group in the Navigator and select "New File".
Create a Swift file called and RGBUIColor.swift
save it. Fill it with this implementation:
import curry Func rgbuicolor (red Red:int, Green:int, Blue:int), Uicolor {return curry (Createcolor) (red) (green) (blue)}private func createcolor (Red:int, Green: Int, Blue:int), uicolor {return uicolor (red: cgfloat (Red/255), Green: cgfloat (green/ 255), Blue: cgfloat (Blue/155), Alpha: 1)}
The use of this are used as an curry
example of using a runtime dependency. This was a non-standard use and doesn ' t provide any value here.
Now let ' s run the tests!
At first glance, this error may seem a bit odd. We clearly defined RGBUIColor
the function, right?
We did, but it's not marked as public
.
What the This means is if someone tries to the use of our framework, they won ' t being able to see this function. If you want to see the difference in action, add back and @testable
your tests'll pass.
Experiencing this error was why we removed the @testable
"the import
" at the beginning. It helps us to catch these kinds of errors earlier, before we release our framework to others.
To fix this, let's mark the function as like so public
:
public func RGBUIColor(red red: Int, green: Int, blue: Int) -> UIColor { return curry(createColor)(red)(green)(blue)}
Let ' s run the tests!
We ' re green!
Let's commit this bad boy.
"Completed my first iOS framework!"
That ' s all folks!
That ' s it. There were a lot of steps but we've successfully created a marginally usefulframework that could is published to GitHub. As a matter of fact, we published Thesource for the This framework on GitHub.
We can ' t wait to see what kinds of awesome open-source projects you ' ll create.
Creating your first IOS Framework