标签:
转自:https://robots.thoughtbot.com/creating-your-first-ios-framework
If you’ve ever tried to create your own iOS framework, you know that it’s not for the faint of heart – managing dependencies and writing tests doesn’t make it any easier. This tutorial will walk you through creating your first iOS framework from start to finish so that you can go out and create your own.
We’re going to build a framework that exposes a function calledRGBUIColor(red:green:blue)
that returns a new UIColor
created from those values. We’ll build it using Swift, with Carthage as our dependency manager. Our framework will be consumable using Carthage, CocoaPods, or git submodules.
Let’s begin!
Let’s jump over to the terminal.
First, navigate to the directory you saved your project in.
git init
to initialize an empty repository..gitignore
which will 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 .DS_Store
and removed fastlane and extra comments.
## OS X Finder
.DS_Store
## Build generated
build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
# Swift Package Manager
.build/
# Carthage
Carthage/Build
Cartfile
and the runtime dependencies to it. We’ll add Curry.github "thoughtbot/Curry"
Cartfile.private
. It will house private dependencies like our test frameworks. We’ll use Quick and Nimble.github "Quick/Quick"
github "Quick/Nimble"
bin/setup
script. It’s used to give our contributors (and us) a simple way to set up the project and the dependencies.mkdir bin
touch bin/setup
chmod +x bin/setup
bin/setup
and fill it with:
In this script, we make sure the user has Carthage installed and run its update
command to install the iOS dependencies.
We’re using --use-submodules
so that our dependencies are added as submodules. This allows users to consume our framework outside of Carthage if they want. We use--no-use-binaries
so that our dependencies are built on our system.
With bin/setup
created, let’s run it so that Carthage will download our dependencies.
bin/setup
.Now we need to set up our project to build and link the new dependencies.
Since our dependencies are submodules, we need to add them to our workspace.
Carthage/Checkouts
and add each dependency’s .xcodeproj
to the root of the workspace. They can be dragged from Finder into the navigator of the Xcode project.When you’re done it should look like:
Curry.framework
from the Curry-iOS
target.When adding dependencies to each target, Xcode will automatically add them to the “Framework Search Paths” under the “Build Settings” tab. We can remove these from the “RGB” and “RGBTests” target because Xcode treats them as implicit dependencies due to them being in the same workspace.
Now that Carthage is set up, let’s add CocoaPods.
To add CocoaPods support, we need to create a .podspec
at the root of our project and fill in our project info.
RGB.podspec
.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."
spec.homepage = "https://github.com/jakecraige/RGB"
spec.license = { type: ‘MIT‘, file: ‘LICENSE‘ }
spec.authors = { "Your Name" => ‘your-email@example.com‘ }
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: "v#{spec.version}", submodules: true }
spec.source_files = "RGB/**/*.{h,swift}"
spec.dependency "Curry", "~> 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 framework to be using it instead of Carthage, so we have to specify dependencies here and in the Cartfile
.
Once this is 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 like this:
With the project and dependencies set up, we’re almost ready to write some code. Before we do that, let’s create our first commit.
git commit -am "Project and dependencies set up"
Open RGBTests/RGBTests.swift
so that we can take a look at the default template. It uses @testable
and XCTest
, 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 will use. As our framework grows, we may need @testable
to test parts that are not exposed publicly; generally we want to avoid that so we are testing what’s exposed to the consumer. This feature is most useful in testing applications rather than frameworks.
From the Apple Docs on Testability:
With testability, you are now able to write tests of Swift 2.0 frameworks and apps without having to make all of your internal routines public. Use @testable import {ModuleName} in your test source code to make all public and internal routines usable by XCTest targets, but not by other framework and app targets.
We’ll use Quick and Nimble for testing. Quick provides a nicer testing interface with a behavior-driven style that is 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 are made, the test file should look like:
import Quick
import Nimble
import RGB
class 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 RGBUIColor(red: 195, green: 47, blue: 52)
will return a beautiful “thoughtbot red” UIColor
.
In code, this looks like:
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))
}
}
If we run the tests, now they will fail as we expect. Swift’s type checking will prevent us from running the tests because we never defined the RGBUIColor
function.
Let’s do that.
Right click on the “RGB” group in the Navigator and select “New File”.
Create a Swift file called RGBUIColor.swift
and 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 curry
here is used as an example of using a runtime dependency. This is 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 the RGBUIColor
function, right?
We did, but it’s not marked as public
.
What this means is that if someone tries to use our framework, they won’t be able to see this function. If you want to see the difference in action, add @testable
back and your tests will pass.
Experiencing this error is why we removed the @testable
from 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 public
like so:
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.
git commit -am "Completed my first iOS framework!"
That’s it. There were a lot of steps but we’ve successfully created a marginally usefulframework that could be published to GitHub. As a matter of fact, we published thesource for 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
标签:
原文地址:http://www.cnblogs.com/feiyu-mdm/p/5630566.html