Swift iOS : 使用CocoaPods管理依赖

5,913 阅读6分钟

开发一个可用的系统,再怎么小也需要依赖第三方库,比如大家常常采用Alamofire这个第三方库来访问网络。CocoaPods是一种非常优秀的包含依赖工程到本工程内的方式。CocoaPods为很多开源仓库建立了一组podspec文件列表,此podspec指定了到何处去下载代码,以及如何和当前工程集成。

以我自己为例,我5月份开始研究Swift,为学习的目的而开发了一个cnode 客户端。最初为了让自己可以集中精力于cnode本身的业务逻辑,因此决定使用了一些第三方开源库:

Alamofire
ObjectMapper 
AlamofireObjectMapper
Cartography
Kingfisher 
GTMRefresh
DrawerController
SwiftIcons
PKHUD

有了CocoaPods,只要编写一个Podfile,写入这些文件和版本:

use_frameworks!
target 'cnode' do
    pod 'Alamofire', '~> 4.4.0'
    ...
end

就可以使用pod命令,一键更新全部依赖。没有它的话,我得把这些第三方开源库的源代码文件复制到项目中,或者设置成git submodule。那些恼人的和依赖关系,还有编译参数配置什么的,都可以使用Pod而得以免除。

本文探讨的是如何使用业已存在和久经考验的Swift第三方Pod,以及如何创建一个自己的Pod,并把它发布到CocoaPods仓库内。

使用Pod

准备CocoaPods

CocoaPods需要系统内已经安装了ruby,如果没有安装,请首先安装它。

可以使用如下命令:

sudo gem install cocoapods

安装gem工具。随即使用:

pod setup --verbose

之后,只要pod目录没有更新,就尽可能使用这个命令(快得多):

pod install --verbose --no-repo-update

做配置。命令执行完毕,cocoapods就是可用的了。

创建一个演示工程

步骤如下:

  1. 打开xcode
  2. 点击“Create a new Xcode project”
  3. 选择Single View App
  4. 填写product name为poddemo;填写语言为Swift
  5. 设置目录

完成创建后,退出xcode

初始化

打开Terminal,导航到工程目录,执行命令:

pod init

此命令会在目录内创建Podfile文件。接下来使用xcode打开Podfile文件:

open -a Podfile

加入alamofire文件的依赖,修改后的Podfile为:

target 'poddemo' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for poddemo
  pod 'Alamofire', '~> 4.4'
end

退出xcode,在terminal内执行命令:

pod install

安装Alamofire,并且创建一个扩展名为xcworkspace的文件。

使用Alamofire访问HTTP

再次使用xcoce打开xcworkspace文件。编辑AppDelegate.swift为:

import UIKit
import Alamofire
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window : UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        foo()
        window = UIWindow()
        window!.rootViewController = UIViewController()
        window!.rootViewController!.view.backgroundColor = .blue
        window!.makeKeyAndVisible()
        return true
    }
    func foo(){
        Alamofire.request("https://httpbin.org/get").responseJSON { response in
            print(response.request)
            print(response.response)
            print(response.data)
            print(response.result)
            if let JSON = response.result.value {
                print("JSON: \(JSON)")
            }
        }
    }
}

编译运行,期望的输出为:

Optional(https://httpbin.org/get)
Optional(<NSHTTPURLResponse: 0x600000222840> { URL: https://httpbin.org/get } { status code: 200, headers {
    "Access-Control-Allow-Credentials" = true;
    "Access-Control-Allow-Origin" = "*";
    Connection = "keep-alive";
    "Content-Length" = 359;
    "Content-Type" = "application/json";
    Date = "Wed, 26 Apr 2017 01:15:59 GMT";
    Server = "gunicorn/19.7.1";
    Via = "1.1 vegur";
} })
Optional(359 bytes)
SUCCESS
JSON: {
    args =     {
    };
    headers =     {
        Accept = "*/*";
        "Accept-Encoding" = "gzip;q=1.0, compress;q=0.5";
        "Accept-Language" = "en;q=1.0";
        Connection = close;
        Host = "httpbin.org";
        "User-Agent" = "poddemo/1.0 (home.poddemo; build:1; iOS 10.2.0) Alamofire/4.4.0";
    };
    origin = "221.237.156.243";
    url = "https://httpbin.org/get";
}

如果输出是对的,就说明通过CocoaPods导入的第三方库已经成功。

创建Pod

你创建了一个库函数,想要共享它。怎么办?cocoapods可以帮忙。

准备代码

首先,我们创建这样的Cocoa touch framework。添加一个swift文件(名字为:robot.swift),内容为:

import Foundation
public func inc(_ i : Int)->Int{return i + 1}

编译通过即可。

为此代码创建podspec文件

想要CocoaPods帮忙分享,就得按照它的要求,编写一个规格文件,以便说明作者,主页,授权等信息。特别重要的是,指明源代码的位置。

现在导航到工程目录,执行如下命令:

touch robot.podspec

粘贴内容为:

Pod::Spec.new do |s| s.name = 'robot' s.version = '0.1.0' s.summary = '一些屁话' s.description = '一些屁话' s.homepage = 'https://github.com/1000copy/robot' s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { '1000copy' => '1000copy@gmail.com' } s.source = { :path => '~/github/pod/robot' } s.ios.deployment_target = '10.0' s.source_files = 'robot/*' end

其中特别重要的是s.name ,s.version , s.source ,s.source_files,没有这些信息,发布pod是不可能的。

最好执行下这个命令,检查此podspec是否正确:

pod lib lint

引用pod

创建一个使用pod的Single View App工程,放到usepod内

cd usepod pod init

粘贴内容到Podfile

pod 'robot',:path=> '~/github/pod/robot/robot.podspec'

执行命令:

pod install --verbose --no-repo-update

引用代码

打开usepod.workspace文件,贴入引用代码到AppDelegate.swift内:

import UIKit
import robot
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window : UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        print(inc(41))
        return true
    }
}

发布到github仓库

文件我们会放到github仓库上,因此,一个github的账号是必要的。仓库需要创建一个release,因为podspec会引用此release。我的相关信息:

  1. github账号是1000copy
  2. 用于存储分享代码的仓库名字:robot

你需要在如下的命令和操作中,替代为你的对应信息。首先:

  1. 创建一个仓库,命名为robot

  2. 在命令行内导航到你的工程目录,执行命令以便把代码传递到github仓库内

    git init git add . git commit -m "init" git remote add origin git push -u origin master

  3. 在github上,对此仓库做一个release,版本号设置为0.1.0,这个号码值会在podspec文件内引用的。

发布到cocoapods

执行命令发布,首先注册你的邮件,然后推送podspec。

pod trunk register 1000copy@gmail.com
pod trunk push robot.podspec

注意,注册邮件后,cocoapods会发送一个邮件给你,你点击里面的确认链接,即可生效。

你应该看到如下的信息反馈:

 🎉  Congrats

 🚀  robot (0.1.0) successfully published
 📅  August 16th, 06:03
 🌎  https://cocoapods.org/pods/robot
 👍  Tell your friends!

现在,你可以像其他pod一样使用robot了。

一些原理

当使用pod setup命令配置pod时,你会发现这个命令执行的时间真够久的,这令我想要考察它到底下载了什么东西。

我探寻了本地的存储,发现了一些cocoapod的内部组织层面的东西。

实际上,CocoaPods把整个的Pod仓库都拉了下来,整个巨型仓库在~/.cocoapods内存放。我这里的大小是:

$ du -sh ~/.cocoapods
1.5G  /Users/lcj/.cocoapods

每一个开源库都有多个版本,每个版本都有一个目录。目录存放在此:

/Users/lcj/.cocoapods/repos/master/Specs/d/a/2/Alamofire

共有这多么的版本:

$ls /Users/lcj/.cocoapods/repos/master/Specs/d/a/2/Alamofire
1.1.3   2.0.0   3.0.0-beta.2  3.2.0   4.0.0
1.1.4   2.0.0-beta.1  3.0.0-beta.3  3.2.1   4.0.1
1.1.5   2.0.0-beta.2  3.0.1   3.3.0   4.1.0
1.2.0   2.0.0-beta.3  3.1.0   3.3.1   4.2.0
1.2.1   2.0.0-beta.4  3.1.1   3.4.0   4.3.0
1.2.2   2.0.1   3.1.2   3.4.1   4.4.0
1.2.3   2.0.2   3.1.3   3.4.2   4.5.0
1.3.0   3.0.0   3.1.4   3.5.0   4.5.1
1.3.1   3.0.0-beta.1  3.1.5   3.5.1

其中的Alamofire版本1.1.3的podspec文件是这个:

$ls /Users/lcj/.cocoapods/repos/master/Specs/d/a/2/Alamofire/1.1.3/Alamofire.podspec.json

内容如下:

$ cat /Users/lcj/.cocoapods/repos/master/Specs/d/a/2/Alamofire/1.1.3/Alamofire.podspec.json 
{
  "name": "Alamofire",
  "version": "1.1.3",
  "license": "MIT",
  "summary": "Elegant HTTP Networking in Swift",
  "homepage": "https://github.com/Alamofire/Alamofire",
  "social_media_url": "http://twitter.com/mattt",
  "authors": {
    "Mattt Thompson": "m@mattt.me"
  },
  "source": {
    "git": "https://github.com/Alamofire/Alamofire.git",
    "tag": "1.1.3"
  },
  "platforms": {
    "ios": "8.0"
  },
  "source_files": "Source/*.swift",
  "requires_arc": true
}

以上分析,就可以得到每次CocoaPods执行install操作的过程。就是首先在本地知道对应的产品和版本的目录,找到podspec文件,和此文件内soure属性指定的文件源比对,如果发现有修改,就会更新最新的仓库到本地。

至于把整个仓库保存到客户端是否合适,我持有保留意见。

参考:

  1. http://blog.devtang.com/2014/05/25/use-cocoapod-to-manage-ios-lib-dependency/
  2. http://ishalou.com/blog/2012/10/16/how-to-create-a-cocoapods-spec-file/
  3. http://www.yudiz.com/creating-your-own-ios-framework-and-distributed-using-cocoapods/
  4. https://stackoverflow.com/questions/16020216/working-with-git-submodules-cocoapods