Xcode 工程结构
Target
-
Target 工程中最小的可编译单元,指定了要编译的product,它根据 Build Phases 和 Build Settings 将源码作为输入,经编译后输出结果产物。
-
一个project可以包含一个或多个target,每个target可以对应产生一个product。
-
一个target和由它产生的product可以关联其他的target。
Project - Targets 的载体
-
Project 就是一个独立的 Xcode 工程,作为一个或多个 Targets 的资源管理器,Project 所管理的资源都来自它所包含的 Targets。
-
Project 本身无法被编译,但它包含编译product所用的所有元素,并帮我们组织这些元素之间的关系。
-
Project 为所包含的 Targets 定义了一份默认编译选项,如果 Target 有自己的配置,则会覆盖 Project 的预设值;
-
Project 能将其他 Project 作为依赖嵌入其中;
Workspace - 容器
-
Workspace 不参与任何编译链接过程,仅用于管理同层级的 Project,一般包含多个 Projects
-
默认情况下,workspace中的projects编译在同一目录下(workspace build directory),文件彼此之间是相互可以引用的。
-
同一个 Workspace 中的 Proejct 文件对于其他 Project 是默认可见的,
-
一个 Xcode Project 可以被包含在多个不同的 Workspace 中,因为每个 Project 都有独立的 Identity,默认是 Project Name;
Scheme
-
Scheme 是对于整个 Build 过程的一个抽象,它描述了 Xcode 应该使用哪种 Build Configurations 、执行什么任务、环境参数等来构建我们所需的 Target。
-
Scheme 中预设了六个主要过程:Build、Run、Test、Profile、Analyze、Archive。包括了我们对 Target 的所有操作,每一个过程都可以单独配置。
CocoaPods
CocoaPods 利用了 Xcode 工程结构的特点,引入 Pods.project 这一中间层,将主工程的 Pods 依赖全部转接到 Pods.project 上,最后再将 Pods.project 作为主项目的依赖。
1. Pod 命令
pod install
-
项目首次进行 cocoaPods 或 Podfile 新增或删除某个Pod后,使用这个命令。
-
首次运行 pod install 会生成 podfile.lock 文件, 同时顺带生成 .xcworkspace和Pods 目录。
-
运行 pod install 下载安装新的 Pod 时,它会为 Podfile.lock 文件中的每下Pod 写入已安装的版本,此文件跟踪并锁定每个Pod 已安装的版本。
-
运行 pod install,它只解析 Podfile.lock 中尚未列在其中的 Pod 依赖库。
pod outdated
- 查看当前项目中所有引用第三方当前版本和最新版本的状态,包
括Podfile.lock中已存在的版本
pod update
-
指定 pod 库,pod update PODNAME 时, CocoaPods将尝试查找PODNAME更新的pod版本, 并忽略掉Podfile.lock中已经存在的版本
-
不指定 pod 库,直接 pod update时 CocoaPods会把Podfile中所有的pod都更新到最新版本.(如果已经是最新版本了, 则不更新)
pod repo update
更新pod资源目录,也就是master下的资源。 简单说,如果有一个第三方库发布了一个最新的版本,如果你不执行pod repo update,那么你的本地是不会知道有一个最新的版本的,还会一直以你本地的资源目录为准,那么你永远都拿不到这个库的最新版本。
但平时使用pod update是会默认执行一遍pod repo update,它会先拉取远程最新目录,再根据目录中的资源重新更新一遍pod.
所以如果不需要更新远程源的话,需要 执行pod repo update --no-repo-update
pod repo
如果有trunk源就删除
pod repo remove trunk
查看本地源 pod repo list
artsy
- Type: git (master)
- URL: https://github.com/Artsy/Specs.git
- Path: /Users/wangpengfei/.cocoapods/repos/artsy
cocoapods
- Type: git (master)
- URL: https://github.com/CocoaPods/Specs.git
- Path: /Users/wangpengfei/.cocoapods/repos/cocoapods
maste
- Type: git (master)
- URL: https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git
- Path: /Users/wangpengfei/.cocoapods/repos/maste
trunk
- Type: CDN
- URL: https://cdn.cocoapods.org/
- Path: /Users/wangpengfei/.cocoapods/repos/trunk
4 repos
pod env
查看当前 pod 环境信息
1. Stack
CocoaPods : 1.10.1
Ruby : ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin20]
RubyGems : 3.0.3
Host : macOS 11.0.1 (20B50)
Xcode : 12.4 (12D4e)
Git : git version 2.24.3 (Apple Git-128)
Ruby lib dir : /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib
Repositories : artsy - git - https://github.com/Artsy/Specs.git @ 55b2a0e46468586d3d93cd77a78dfe12684aca54
cocoapods - git - https://github.com/CocoaPods/Specs.git @ 11fa1b295dbb4a90f4d66d6a5eedb9afb1b96b5e
maste - git - https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git @ a0abf73604cb65bd66eb158d5aac5bc3366927b5
trunk - CDN - https://cdn.cocoapods.org/
2. Installation Source
Executable Path: /usr/local/bin/pod
3. Plugins
cocoapods-deintegrate : 1.0.4
cocoapods-plugins : 1.0.0
cocoapods-search : 1.0.0
cocoapods-stats : 1.1.0
cocoapods-trunk : 1.5.0
cocoapods-try : 1.2.0
2. Podfile
vim 文本编辑器
open podfile --> 打开 Podfile 文件
vim podfile --> 创建 Podfile 文件并使用 VIM 编写
:w --> 保存
:w! --> 强行保存
:q --> 退出
:q! --> 不保存并退出
:wq --> 保存并退出
:x --> 保存并退出
Podfile 写法
简单写法
target 'EWDemo'
pod 'YYModel'
pod 'AFNetworking', '~> 4.0.1'
普通写法
# 下面两行是指明依赖库的来源地址
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/Artsy/Specs.git'
# 说明平台是ios,版本是9.0
platform :ios, '9.0'
# 忽略引入库的所有警告(强迫症者的福音啊)
#inhibit_all_warnings!
# 针对 MyApp 引入AFNetworking
# 针对 MyAppTests 引入OCMock,
target 'MyApp' do
pod 'AFNetworking', '~> 3.0'
target 'MyAppTests' do
inherit! :search_paths
pod 'OCMock', '~> 2.0.1'
end
end
# 指定"安装完成,但是生成的工程还没有写入磁盘"时要执行的操作.
post_install do |installer|
installer.pods_project.targets.each do |target|
puts "#{target.name}"
end
end
Pod 指定项目依赖
依赖项规范是由Pod的名称和一个可选的版本组合一起。
pod 'YYModel'
如果需要特定依赖库的版本,就需要在后面写上具体的版本号
pod 'Objection', '0.9'
如果需要指定版本范围
> 0.1 高于0.1版本(不包含0.1版本)的任意一个版本
>= 0.1 高于0.1版本(包含0.1版本)的任意一个版本
< 0.1 低于0.1版本(不包含0.1版本)的任意一个
<= 0.1 低于0.1版本(包含0.1版本)的任意一个
~> 0.1.2 版本 0.1.2的版本到0.2 ,不包括0.2。
Build configuraton 编译配置
默认情况下, 依赖项会被安装在所有 target 的 build configuration中。为了调试或者处于其他原因,依赖项只能在给定的build configuration中被启用。 下面写法指明只有在Debug和Beta模式下才有启用配置
pod 'PonyDebugger', :configuration => 'Debug'
或
pod 'PonyDebugger', :configurations => ['Debug', 'Beta']
SubSpecs
一般情况我们会通过依赖库的名称来引入,cocoapods会默认安装依赖库的所有内容。 我们也可以指定安装具体依赖库的某个子模块.
# 仅安装QueryKit库下的Attribute模块
pod 'QueryKit/Attribute'
或
# 仅安装QueryKit下的Attribute和QuerySet模块
pod 'QueryKit', :subspecs => ['Attribute', 'QuerySet']
使用本地文件
如果我们想引入我们本地的一个库,可以如下指定依赖库地址
pod 'AFNetworking', :path => '~/Documents/AFNetworking'
使用外部文件
podspec可以从另一个源库的地址引入
pod 'JSONKit', :podspec => 'https://example.com/JSONKit.podspec'
target
在给定的块内定义pod的target(Xcode工程中的target)和指定依赖的范围。默认情况下,target会包含定义在块外的依赖,除非指定不使用inherit!来继承(说的是嵌套的块里的继承问题)
- 单个target
target 'ZipApp' do
pod 'SSZipArchive'
end
- 在 MyApp 仅引入 AFNetworking 库,MyAppTests 引入OCMock的同时也会继承 MyApp 里面的 AFNetworking 库
target 'MyApp' do
pod '', '~> 3.0'
target 'MyAppTests' do
inherit! :search_paths
pod 'OCMock', '~> 2.0.1'
end
end
- target块中嵌套多个子块
target 'ShowsApp' do
# ShowsApp 仅仅引入ShowsKit
pod 'ShowsKit'
# 引入 ShowsKit 和 ShowTVAuth
target 'ShowsTV' do
pod 'ShowTVAuth'
end
# 引入了Specta和Expecta以及ShowsKit
target 'ShowsTests' do
inherit! :search_paths
pod 'Specta'
pod 'Expecta'
end
end
抽象target
定义一个新的抽象目标,它可以方便的用于目标依赖继承
- 简单写法
abstract_target 'Networking' do
pod 'AlamoFire'
target 'Networking App 1'
target 'Networking App 2'
end
- 定义一种abstract_target 包含多个target
# 注意:这是个抽象的target也就是说在工程中并没有这个target引入ShowsKit
abstract_target 'Shows' do
pod 'ShowsKit'
# ShowsiOS target会引入ShowWebAuth库以及继承自Shows的ShowsKit库
target 'ShowsiOS' do
pod 'ShowWebAuth'
end
# ShowsTV target会引入ShowTVAuth库以及继承自Shows的ShowsKit库
target 'ShowsTV' do
pod 'ShowTVAuth'
end
# ShowsTests target引入了Specta和Expecta库,并且指明继承Shows,所以也会引入ShowsKit
target 'ShowsTests' do
inherit! :search_paths
pod 'Specta'
pod 'Expecta'
end
end
abstract! 和 inherit!
abstract! 表示当前的Target 是抽象的,因此不会直接链接Xcode target inherit 设置当前target的继承模式
- :complete 目标从父节点继承所有行为。
- :none 目标不会从父节点继承任何行为。
- :search_paths 目标只继承父类的搜索路径。
target 'App' do
target 'AppTests' do
inherit! search_paths
end
end
Target Configuration(目标配置)
platform
platform 用于指定应建立的表态库的平台。CocoaPods提供的默认的平台版本配置。
iOS->4.3
OS X->10.6
tvOS->9.0
watchOS->2.0
如果指定具体平台和版本,则
platform :ios, '4.0'
platform :ios
project
如果没有显示的project被指定,那么会默认使用target的父target指定的project作为目标。如果没有任何一个target指定目标,那么就会使用和Podefile在同一目录下的project。
同样也能够指定是否这些设置在release或者debug模式下生效。为了做到这一点,你必须指定一个名字和:release/:debuge关联起来
# MyGPSApp这个target引入的库只能在 FastGPS 工程中引用
target 'MyGPSApp' do
project 'FastGPS'
...
end
# MyNotesApp 这个target引入的库只能在 FastNotes 工程中引用
target 'MyNotesApp' do
project 'FastNotes'
...
end
#自定义的编译配置
project 'TestProject', 'Mac App Store' => :release, 'Test' => :debug
inheib_all_warnings!
inhibit_all_warnings! 屏蔽所有来自于cocoapods依赖库的警告。 可以全局定义,也能在子target里面定义,也可以指定某一个库:
# 隐藏SSZipArchive的警告而不隐藏ShowTVAuth的警告
pod 'SSZipArchive', :inhibit_warnings => true
pod 'ShowTVAuth', :inhibit_warnings => false
user_frameworks!
通过指定use_frameworks!要求生成的是framework而不是静态库。 如果使用use_frameworks!命令会在Pods工程下的Frameworks目录下生成依赖库的framework 如果不使用use_frameworks!命令会在Pods工程下的Products目录下生成.a的静态库
Workspace
默认情况下,我们不需要指定,直接使用与Podfile所在目录的工程名一样就可以了。如果要指定另外的名称,而不是使用工程的名称,可以这样指定
Source
source是指定pod的来源。如果不指定source,默认是使用CocoaPods官方的source。(建议使用默认设置)
CocoaPods Master Repository
# 使用其他来源地址
source 'https://github.com/artsy/Specs.git'
# 使用官方默认地址(默认)
source 'https://github.com/CocoaPods/Specs.git'
添加master源
cd ~/.cocoapods/repos
pod repo remove master
git clone https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git master
在工程的Podfile第一行加上(也可以不加 执行pod update操作后 会自动加上):
source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
def
可以能过def 命令来声明一个Pod 集合
def 'CustomPods'
pod 'IQKeyboardManagerSwift'
end
然后就可以在需要引的的targget 处引入
target 'MyTarget' do
customPods
end
3. Podspec 文件
Specification描述了Pod库的一个版本,它包括关于应该从哪里获取源代码、使用什么文件、要应用的构建设置和其他通用元数据(如名称、版本和描述)的详细信息
3.1 创建Podspec
-
pod lib create name 按照 github 模板创建一个组件库,生成的文件中包含一个 Podspec文件,这个文件内容会比较多。
-
pod spec create name 直接创建一个 Podspec 模板文件。
Pod::Spec.new do |spec|
# ―――-- Spec Metadata ―――--
spec.name = "EWDemo" # 库名
spec.version = "0.0.1" # 版本号
spec.summary = "A short description of EWDemo." # 简短说明
spec.description = <<-DESC
DESC
spec.homepage = "http://EXAMPLE/EWDemo" # 主页 URL
# ―――-- Spec License ―――--
spec.license = "MIT" # 许可证
# spec.license = { :type => "MIT", :file => "FILE_LICENSE" }
# ―――-- Author Metadata ―――-- # 维护者名称邮件
spec.author = { "wangpengfei" => "wpf_register@163.com" }
# spec.authors = { "wangpengfei" => "wpf_register@163.com" }
# spec.social_media_url = "https://twitter.com/wangpengfei"
# ―――-- Platform Specifics ―――--
# spec.platform = :ios
# spec.platform = :ios, "5.0"
# When using multiple platforms
# spec.ios.deployment_target = "5.0"
# spec.osx.deployment_target = "10.7"
# spec.watchos.deployment_target = "2.0"
# spec.tvos.deployment_target = "9.0"
# ―――-- Source Location ―――-- # 检索库的位置
spec.source = { :git => "http://EXAMPLE/EWDemo.git", :tag => "#{spec.version}" }
============================ 分割线 ================================
# ―――-- Source Code ―――-- #
# For source files
# giving a folder will include any swift, h, m, mm, c & cpp files.
# For header files it will include any header in the folder.
# Not including the public_header_files will make all headers public.
spec.source_files = "Classes", "Classes/**/*.{h,m}"
spec.exclude_files = "Classes/Exclude"
# spec.public_header_files = "Classes/**/*.h"
# ―――-- Resources ―――-- #
# A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script. Anything else will be cleaned.
# You can preserve files from being cleaned, please don't preserve
# non-essential files like tests, examples and documentation.
# spec.resource = "icon.png"
# spec.resources = "Resources/*.png"
# spec.preserve_paths = "FilesToSave", "MoreFilesToSave"
============================ 分割线 ================================
# ―――-- Project Linking ―――--
# spec.framework = "SomeFramework" #当前target所需系统framework列表
# spec.frameworks = "SomeFramework", "AnotherFramework"
# spec.library = "iconv" #当前target所需系统library列表
# spec.libraries = "iconv", "xml2"
# ―――-- Project Settings ―――-- #
# spec.requires_arc = true
# spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
# spec.dependency "JSONKit", "~> 1.4"
end
3.2 File patterns
- 本地私有库配置
Pod::Spec.new do |spec|
//本地私有库基本设置
spec.name = "EWCom"
spec.version = "0.0.1"
spec.summary = "这是一个测试简要"
spec.homepage = "https://github.com/WFregister/TestDemo"
spec.author = { "wangfei" => "wpf_register@163.com" }
spec.source = { :git => "", :tag => "#{spec.version}" }
# 框架的资源路径:路径可以指向远端代码库,也可以指向本地项目:
# 指向远端代码库: { :git => "XXXX", :tag => "1.0.0" }
# 指向本地项目: { :path => 'EWCom', }
//本地私有库至少要设置一个资源
#文件平夹及其子文件夹下所有.h.m文件
spec.source_files = 'EWCom/**/*.{h,m}'
end
- 匹配所有文件
- c* 匹配所有以c开头的文件
- *c 匹配所有以c结尾的文件
- c 将匹配其中包含c的所有文件(包括开头或结尾)
spec.source_files = 'Classes/**/*.{h,m}'
spec.source_files = 'Classes/**/*.{h,m}', 'More_Classes/**/*.{h,m}'
- 需要包含的源文件
public_header_files 用作公共头的文件模式列
spec.public_header_files = 'Headers/Public/*.h'
private_header_files 用来标记私有文件模式列表
spec.private_header_files = 'Headers/Private/*.h'
- resource_bundels 为了将Pod构建为静态库,官方强烈建议使用此属性来管理资源文件,因为使用resources属性可能会发生名称冲突
spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }
spec.resource_bundles = {
'MapBox' => ['MapView/Map/Resources/*.png'],
'MapBoxOtherResources' => ['MapView/Map/OtherResources/*.png']
}
- resources 为了将Pod构建为静态库,官方建议是使用resource_bundle,因为使用resources属性可能会发生名称冲突。
spec.resource = 'Resources/HockeySDK.bundle'
spec.resources = ['Images/*.png', 'Sounds/*']
3.3 加载资源文件
当不使用 use_frameworks!
3.3.1 使用resource
spec.resource = spec.resource = 'EWCom/Resource/*'
查看产品包内容,图片资源直接在主Bundle中,
UIImage *iamge = [UIImage imageNamed:@"yin.jpg"];
或者项目中直接新建bundle,可以通过传入class类型查找对应bundle 目录
-(UIImage*)getImageWithClass:(id)obj{
NSBundle *bundle = [NSBundle bundleForClass:[obj class]];
NSURL *bundleURL = [bundle URLForResource:@"EWCom" withExtension:@"bundle"];
NSBundle *resourceBundle = [NSBundle bundleWithURL:bundleURL];
NSInteger scale = [UIScreen mainScreen] scale];
//完整路径
NSString *path = [NSString stringWithFormat:@"%@%zdx.png",name,scale];
UIImage *image = [UIImage imageWithContentsOfFile:[bundle pathForResource:imgName ofType:nil];
return image;
}
3.3.2 使用 resource_bundles
spec.resource_bundles = {'EWCom' => 'EWCom/Resource/*'}
查看产品包内容,图片资源被打包成Bundle 放在在主Bundle下
图片加载方式
-(UIImage*)getImageWithName:(NSString*)name{
NSURL *associateBundleURL = [NSBundle mainBundle] URLForResource:@"EWCom" withExtension:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithURL:associateBundleURL];
NSInteger scale = [UIScreen mainScreen] scale];
//完整路径
NSString *imgName = [NSString stringWithFormat:@"%@%zdx.png",name,scale];
UIImage *image = [UIImage imageWithContentsOfFile:[bundle pathForResource:imgName ofType:nil];
return image;
}