从零开始,打造自己的首个 iOS 框架
如果你曾试图创建自己的iOS框架,你知道这不是一个头脑发热作出的决定 — 管理依赖以及写测试用例一点也不简单。本教程将会带你从头到尾创建你的第一个iOS框架,让你可以创建自己的框架。
我们将在框架暴露一个名为 RGBUIColor(red:green:blue) 的函数,这个函数根据参数返回一个新的UIColor。我们将使用 Swift 创建它,并使用 Carthage 作为依赖管理器。在 Carthage、CocoaPods 或者 git submodules 中都可以使用我们的框架。
让我们开始吧!
设置 Xcode 工程
选择 File → New → Project。
在左边栏选择 iOS → Framework & Library,然后在右侧选择 “Cocoa Touch Library”。
点击“Next”并且填写弹出的选项。确保勾选了“Include Unit Tests”复选框。
选择工程的保存位置。
取消选择“在我的 Mac 上创建 Git 版本库”,我们稍后将手动创建它。
点击“创建”,工程将在 Xcode 中打开。
转到File → Save As Workspace,使用同样的名字将你的 Xcode 工程保存到相同的路径下。我们之所以将工程放到工作区中,是因为我们将把Carthage依赖作为子模块加入进来;Xcode必须确保它们在一个工作区中才能 build 它们。
用 File → Close Project 关闭 Xcode 工程。
用 File → Open 打开工作区。
点击 Xcode 左上方的 scheme 并选择“Manage Schemes”。我们需要将我们的 scheme 标记为“shared”,以便工程可以用Carthage构建。
https://github.com/Carthage/Carthage#share-your-xcode-schemes
定位到“RGB”scheme,选中“Shared”复选框然后点击“Close”。
让我们移步到终端去。
初始化 Git
首先,导航到你存储工程所在的目录。
运行 git init 来初始化一个空的版本库。
创建一个.gitignore,将我们不希望在git中追踪的一些讨厌的Xcode文件和依赖文件排除出去。
这里是一个稍作修改的,Swift工程使用的标准的.gitignore文件。
https://github.com/github/gitignore/blob/master/Swift.gitignore
我们增加了.DS_Store,并删除了fastlane和多余的注释。
.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
添加 Carthage 和依赖
在你的工程目录下创建一个名为 Cartfile 的文件,并添加运行时依赖。我们将添加 Curry。
github "thoughtbot/Curry"
创建一个 Cartfile.private 文件。它将包含私有的依赖,例如我们的测试框架。我们将使用 Quick 和 Nimble。
github "Quick/Quick"
github "Quick/Nimble"
创建一个 bin/setup 脚本。它用来给我们的贡献者(以及我们)一个简单的方法来设置工程以及依赖。
mkdir bin
touch bin/setup
chmod +x bin/setup
打开 bin/setup,写入如下内容:
#!/usr/bin/env sh
if ! command -v carthage > /dev/null; then
printf 'Carthage is not installed.n'
printf 'See https://github.com/Carthage/Carthage for install instructions.n'
exit 1
fi
carthage update --platform iOS --use-submodules --no-use-binaries
在这段脚本中,我们确保用户安装了Carthage,并且运行它的 update 命令来安装iOS依赖。
我们使用了 --use-submodules 已使我们的依赖作为子模块被添加进来。这样当用户希望脱离 Carthage 的时候,也能使用我们的框架。我们使用 --no-use-binaries,这样我们的依赖就是在我们的系统上 build 的。
bin/setup创建好之后,让我们运行它,以便Carthage下载我们的依赖。
在终端中运行 bin/setup。
现在我们需要设置我们的工程,来构建和链接新的依赖。
向工作区添加依赖
因为我们的依赖是一些子模块,我们需要把他们添加到我们的工作区。
打开 Carthage/Checkouts,然后把每一项依赖的.xcodeproj 添加到工作区的根目录。可以把他们从 Finder 拖入 Xcode 工程的导航器。
当你完成之后,导航器应该看起来像这样:
链接运行时依赖
在导航器中选择“RGB”并且在中间的工具条选择“RGB”目标,选择“Build Phases”选项卡并展开“Link binary with libraries”小节。
点击“+”图标并从 Curry-iOS 目标中选择 Curry.framework。
点击“Add”。
链接开发依赖
在中间的工具条选择“RGBTests”目标。
使用跟上文相同的过程,在该目标的“Link binary with libraries”小节添加 Quick 以及 Nimble 框架。当我们在每个目标添加依赖的时候,Xcode 会自动将他们添加到“Build Settings”选项卡的“Framework Search Paths”中。我们可以将它们从“RGB”和“RGBTests”目标中移除,由于是在相同的工作区中,Xcode将他们看作隐式依赖。
Select the target, locate the “Framework Search Paths” setting, highlight it, and press “backspace” on your keyboard.
选择该目标,定位到“Framework Search Paths”设置,使其高亮,然后点击键盘上的退格键。
接着,看一下导航器中的“RGB”工程;你将会看见根级有三个新的框架。为了保持这个区域有序,高亮所有这三项,右击并选择“New group from selection”,将它们放入一个有名字的组。我将给我的组取名“Frameworks”。
现在 Carthage 设置好了,让我们添加 CocoaPods。
添加 CocoaPods 支持
要添加 CocoaPods 支持,我们需要在工程的根目录创建一个 .podspec 文件,并且写入我们的工程信息。
创建一个名为 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
需要注意的一行是 spec.dependency "Curry", '~> 1.4.0'。因为我们要支持 CocoaPods,我们期望我们框架的使用者使用 CocoaPods 而不是 Carthage,所以我们必须在这里以及 Cartfile 中指定依赖。
一旦设置好之后我们可以运行 pod lib lint 命令来测试一切是否配置妥当。如果运行没问题,我们将看到类似这样的结果:
当工程和依赖设置好之后,我们已经基本就绪,可以写代码了。当我们这么做之前,让我们创建我们的第一个 commit。
git commit -am "Project and dependencies set up"
编写第一个测试
打开 RGBTests/RGBTests.swift 来看一看缺省的模板。它使用 @testable 以及 XCTest,但我们将把两者都换掉。
我们将移除 @testable,因为我们想要测试公有 API,也就是框架的用户将会使用的部分。当我们的框架发展变大,我们可能需要 @testable 来测试没有公开暴露的部分;总的来说我们想要避免这样的情况,使得我们要测试的是暴露给使用者的部分。这个特性在测试应用而不是框架的时候最有用。
下面是 Apple Docs中关于可测试性的部分:
通过可测试性,你现在可以在不暴露内部程序的情况下,编写 Swift 2.0 框架及应用的测试。在测试源代码中使用 @testable import {ModuleName} 使得所有公有或内部的程序对 XCTest 目标可用,而对其它框架或应用目标不可用。
我们将使用 Quick 和 Nimble 用于测试。Quick 提供了一个良好的测试接口,拥有十分类似于 RSpec 和 Specta 的行为驱动的风格;Nimble 给予我们很多强大的断言,以及用更少的样本文件编写匿名代码的能力。
一旦做了这些改变,测试文件看起来将类似于这样:
import Quick
import Nimble
import RGB
class RGBTests: QuickSpec {
override func spec() {
describe("RGB") {
it("works") {
expect(true).to(beTrue())
}
}
}
}
用 ⌘U 或 Product → Test 运行测试,它们应该是绿的。
然后……我们成功了!
开玩笑的。让我们写点真正的测试。
我们希望调用 RGBUIColor(red: 195, green: 47, blue: 52) 返回一个漂亮的“thoughtbot red”UIColor。
代码看起来类似于:
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”组,选择“New File”。
创建一个名为 RGBUIColor.swift 的 Swift 文件并保存。在里面写上如下实现:
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
)
}
这里 curry 的使用是一个使用运行时依赖的示例。这是一个非标准的用法,并且不提供任何的值。
现在让我们运行这个测试!
乍一看,这个错误可能看起来有点奇怪。我们明明定义了 RGBUIColor 函数,不是吗?
我们确实定义了,但是没有把它标记为 public。
这意味着如果有人尝试使用我们的框架,他们将看不到这个函数。如果你想看见这些不同起作用,把 @testable 添加回来,然后你的测试就通过了。
正因为有这个错误的经验,我们在开始的时候从 import 中移除了 @testable。这有助于我们在向其它人发布我们的框架之前,早点捕捉到这类错误。
为了纠正这个错误,让我们把这个函数标记为 public,就像这样:
public func RGBUIColor(red red: Int, green: Int, blue: Int) -> UIColor {
return curry(createColor)(red)(green)(blue)
}
让我们运行测试!
我们绿了!
让我们提交这个小婊砸。
git commit -am "Completed my first iOS framework!"
以上就是全部内容了!
就是这么溜。尽管步骤很多,但是我们已经成功地创建了一个可以发布到 GitHub,还有点用的框架。事实上,我们在 GitHub 上已经发布了这个框架的源代码。
https://github.com/jakecraige/RGB
我们已经迫不及待地想看到你将会创建什么样的出色的开源项目了。
从零开始,打造自己的首个 iOS 框架的更多相关文章
- 从零开始写一个武侠冒险游戏-0-开发框架Codea简介
从零开始写一个武侠冒险游戏-0-开发框架Codea简介 作者:FreeBlues 修订记录 2016.06.21 初稿完成. 2016.08.03 增加对 XCode 项目文件的说明. 概述 本游戏全 ...
- [转]1小时内打造你自己的PHP MVC框架
简介 MVC框架在现在的开发中相当流行,不论你使用的是JAVA,C#,PHP或者IOS,你肯定都会选择一款框架.虽然不能保证100%的开发语言都会使用框架,但是在PHP社区当中拥有*多数量的MVC框架 ...
- SpringBoot2+Netty打造通俗简版RPC通信框架(升级版)
背景 上篇文章我简单的介绍了自己打造的通俗简版RPC通信框架,这篇是对简版的增强~ 如果大家对此项目还感兴趣的话,可到码云上瞄瞄:Netty-RPC 上 ...
- iOS框架介绍
iOS框架介绍 Cocoa Touch GameKit 实现对游戏中心的支持,让用户能够在线共享他们的游戏相关的信息 iOS设备之间蓝牙数据传输 从iOS7开始过期 局域网游 ...
- IOS框架概览
iOS是执行在iPhone.iPod Touch或iPad上的操作系统,之前叫做iPhone OS,iOS与Mac OS X有共同的基础架构和底层技术.但iOS是依据移动设备的特点而设计的,所以和Ma ...
- SOD框架的数据容器,打造最适合DDD的ORM框架
SOD框架的数据容器,打造最适合DDD的ORM框架 引言:DDD的困惑 最近,我看到园子里面有位朋友的一篇博客 <领域驱动设计系列(一):为何要领域驱动设计? >文章中有下面一段话,对DD ...
- 【从零开始搭建自己的.NET Core Api框架】(七)授权认证进阶篇
系列目录 一. 创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...
- Go语言打造以太坊智能合约测试框架(level1)
传送门: 柏链项目学院 Go语言打造以太坊智能合约测试框架 前言 这是什么? 这是一个基于go语言编写的,自动化测试以太坊智能合约的开发框架,使用此框架,可以自动化的部署合约,自动测试合约内的功能函数 ...
- 【从零开始搭建自己的.NET Core Api框架】(四)实战!带你半个小时实现接口的JWT授权验证
系列目录 一. 创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...
随机推荐
- noproguard.classes-with-local.dex
make: *** [out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/noproguard.classes-with-loca ...
- 【原创】MIPS浅议之——中断系统之我见
最近,准确的说应该是最近两个月的时间,我都在研究MIPS的异常与中断.或者可以说,最近这两个月,我才真正了解中断系统的整个结构和处理流程以及为什么要这样做?这段时间我最大的体会就是以前我们在“计算机组 ...
- MySQL导入数据非常慢的解决办法
MySQL导出的SQL语句在导入时有可能会非常非常慢,经历过导入仅45万条记录,竟用了近3个小时.在导出时合理使用几个参数,可以大大加快导入的速度. -e 使用包括几个VALUES列表的多行INSER ...
- Android 应用中十大常见 UX 错误
[核心提示] Android 开发者关系团队每天都会试用无数的 App 或者受到无数的开发者发来的请求评测的 App,在评测如此之多的应用之后,他们总结出了10个最常见的错误. 作为一个长期使用 An ...
- 全世界只有我们Erlang程序员是正确的
http://www.aqee.net/erlang-solving-the-wrong-problem/ 对某些程序来说是的,但对大多数程序来说不是.对大多数程序来说24个CPU中只有一个被利用.C ...
- 【Android】Android程序保护与破解浅析
此文源自组内成员分享的PPT,其他成员的文档由于没有得到授权,暂不公开. 本文命令如果没有特殊注明,均为windows 7环境. 本文只涉及大概的知识点,不涉及具体的细节,需要注意. 反编译 apkt ...
- [POJ2234]Matches Game
Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 9297 Accepted: 5365 Description Here ...
- Android项目开发全程(一)--创建工程
每个程序员都知道,项目工程的整体架构对开发有着决定性的影响,在后续的开发工作中,能不能有效的减少代码的重复量和有效的人员分工取决于前期工程整体的架构.刚参加工作还不到一个月就意识到之前做的项目在架构方 ...
- BI 多维立方体CUBE
在Bi领域,cube是一个非常重要的概念,是多维立方体的简称,主要是用于支持联机分析应用(OLAP),为企业决策提供支持.Cube就像一个坐标系,每一个Dimension代表一个坐标系,要想得到一个一 ...
- 微软Azure 存储管理器的简单介绍
Windows Azure存储用户经常希望能够在“管理器”中查看他们的数据,管理器指的是一款可用于显示存储帐户数据的工具.我们之前提供了我们所知的存储管理器列表.在本文中,我们将对此列表进行更新,使其 ...