如何在 Swift 中使用 CocoaPods

starX 2014-07-02

本文介绍如何在 Swift 项目中使用 CocoaPods 。如果你已经精通 Bridging Header 的方法,请直接跳到 “扩展 CocoaPods” 一节。

什么是 CocoaPods

CocoaPods is the dependency manager for Objective-C projects. It has thousands of libraries and can help you scale your projects elegantly.

从介绍看,它是主要给 Objective-C 项目用的,但是我们可以很容易地混合 Objective-C 和 Swift 到同个项目,从而利用大量的 CocoaPods 库和 Swift 漂亮舒服的语法。

作为 iOS 开发新手,一定是要紧跟前人脚步,学习使用 CocoaPods 。

基础用法

这里简单略过,请参考其他无数的文章。

安装

系统默认安装,可以参考其他教程 。在命令行下执行。

sudo gem install cocoapods 

我的环境是 HomeBrew

# 添加 taobao Mirror 不然被墙掉没办法下载 


gem sources -a http://ruby.taobao.org/ 


# 安装 


gem install cocoapods 


# 更新命令 


rbenv rehash 


# 执行 


pod 


# 此时一般会下载官方的所有 PodSpec 库,也可以用 pod setup 初始化环境 

本文不打算在安装部分耗费太多时间。希望看到这里保证你的命令行下有可用的 pod 命令。

使用

假设我们已经有个项目,叫 ProjName ,需要使用一些注明的 CocoaPods 库,比如 AFNetworking3.

首先,命令行 cd 到我们的项目目录,一般 ls 命令会看到如下几个文件夹

ProjName 


ProjName.xcodeproj 


ProjNameTests 

赞,就是这里,创建一个 Podfile 文本文件,写入如下内容

platform :ios, "8.0" 


pod "AFNetworking", "~> 2.0" 

一般这么简单的文件都是直接 nano 写。 :)

直接创建 Podfile , CocoaPods 会创建一个项目同名的 WorkSpace ,然后添加一个叫 Pods 的项目,这个项目编译结果是一个叫 libPods.a的链接库, 它会添加到我们之前的 ProjName 项目中作为编译依赖。

当然,通过命令行执行 pod init 也可以自动创建 Podfile,而且可以自动分析当前项目的 target ,相对来说更好,也更优雅。具体请参考官方手册。这样的好处是更细致,还可以区分多个子项目子 target 。原理大同小异。

然后接下来,命令行执行 open ProjName.xcworkspace,注意这个可不是 .xcodeproj,这个是 CocoaPods 为我们创建的一个 WorkSpace ,包含我们之前的项目,和 Pods 依赖。

开始编码过程。直接在代码里调用,比如写在某个按钮的 @IBAction 里:

let manager = AFHTTPRequestOperationManager() 


   let url = "http://api.openweathermap.org/data/2.5/weather" 


   println(url) 


  


   let params = ["lat": 39.26, "lon": 41.03, "cnt":0] 


   println(params) 


  


   manager.GET(url, 


       parameters: params, 


       success: { (operation: AFHTTPRequestOperation!, 


                   responseObject: AnyObject!) in 


           println("JSON: " + responseObject.description!) 


       }, 


       failure: { (operation: AFHTTPRequestOperation!, 


                   error: NSError!) in 


           println("Error: " + error.localizedDescription) 


       }) 

这里直接抄了 JakeLin 的 SwiftWeather 代码4,就一小段,希望他不会打我。

Swift 坑爹了

看起来貌似我们已经可以在 Swift 中使用 AFNetworking 了。结果刚写几句代码一堆类和变量找不到定义,而且坑爹的是很多时候我们只能靠猜测,判断这些 Objective-C 的定义转换成 Swift 定义是什么样子,用起来就是完全靠蒙!

这不科学!

这都三礼拜了,所以大家都摸索出了调用的方法,那就是按照和 Objective-C 代码混编的例子,添加 Bridging Header !

继续

之前简单介绍过和 Objective-C 交互的内容5,大家可以去围观。

一般说来,你在 Swift 项目新建 Objective-C 类的时候,直接弹出是否创建 Bridge Header 的窗口,点 YES 就是了,这时候一般多出来个 ProjectName-Bridging-Header.h 。然后删掉这个类, Bridging Header 头文件还在。

在这个 Bridging Header 文件里写入要导入的 CocoaPods 库,就可以在 Swift 中使用了。

#import <AFNetworking/AFNetworking.h> 

如果没有自动创建头文件的话,这个配置在项目的 Build Settings 中的 Swift Compiler – Code Generation 子项里。

创建一个头文件,指定为 Bridging Header 也可以。

然后编译,成功执行!

这就完事了?

实际上,前两天刚写一篇 Swift 的模块系统 , 把任意 Objective-C 库当做 Swift Module 是可行的。当时就觉得这个东西应该是可能完全进入 CocoaPods 的,但是在官方 repo 找了下发现,以前有人提过增加 module.map 支持,结果 CocoaPods 的人认为这个是 llvm 内部特性, issue 被关闭了。#2216 最近又被提起,我在后面提了下 Swift 支持,希望官方靠谱。

所以下面的内容,就是,我们是否可以在 CocoaPods 上加入 module.map 支持,然后直接在 Swift 中 import ModuleName ?

扩展 CocoaPods

考虑了多种方式,最后选择了 Hook 的方式。如果 Ruby 技术足够好,或许可以直接写个插件。或者直接改官方代码给官方提交。但是实在能力有限。相关的 module.map 语法参考 llvm 官方手册 Modules – Clang 3.5 documentation。用了最简单的功能。也许遇到复杂的 PodSpec 就不起作用了,但是原理如此,相信小伙伴们已经知道怎么做了。

目前我的 Podfile 大概是这个样子:

platform :ios, "8.0" 


pod "AFNetworking", "~> 2.0" 


pod "Baidu-Maps-iOS-SDK", "~> 2.0" 


  


post_install do |installer| 


  File.open("#{installer.sandbox_root}/Headers/module.map", 'w') do |fp| 


    installer.pods.each do |pod| 


      normalized_pod_name = pod.name.gsub('-', '') 


      fp.write <<EOF 


module #{normalized_pod_name} [system] { 


  umbrella "#{pod.name}" 


  export * 


} 


EOF 


      puts "Generating Swift Module #{normalized_pod_name.green} for #{pod} OK!" 


    end 


  end 


end 

post_install 是 Podfile 的一种 hook 机制,可以用来加入自定义操作。我在这里的写的逻辑就是,针对所有的 Pod 生成一个 module.map 文件。 位于 Pods/Headers/,这个目录被 CocoaPods 自动设置为项目的 Header Search Path 所以不需要额外处理。默认我们的 Swift 文件就找得到。

其中 normalized_pod_name 用于处理百度地图 API SDK 这一类名字带减号的库,因为他们不能作为 Module Name ,实际上或许有更好的方法来处理。

实际效果

实测发现完全没有问题,直接 import AFNetworking 或者 import BaiduMapsiOSSDK 都可以。

而且很不错的一点是,按住 Command 键,然后鼠标点击模块名、类名等,会跳转到 Swift 定义。

遇到提示 .pcm 文件 outdate 的情况下需要你删除 $HOME/Library/Developer/Xcode/DerivedData/ModuleCache 目录,这个目录保存的是预编译模块,类似于预编译头文件。

目前 Swift 还是有很多 BUG 的,调用 NSObject 也许会让编译器直接 segment fault ,不带任何出错信息。很伤情。此时请第一时间检查语法是否有诡异,其次将所有用到字符串或者 Optional 的地方都额外用变量处理,避免用字面常量。(个人经验)

相关推荐