iOS自动签名打包(xcodebuild)

iOS自动打包主要用xcodebuild命令, 在终端输入xcodebuild --help可以查看xcodebuild的参数。

xcodebuild具体语法:

  • workspace的工程

    xcodebuild [-project name.xcodeproj] [[-target targetname] … | -alltargets] [-configuration configurationname] [-sdk [sdkfullpath | sdkname]] [action …] [buildsetting=value …] [-userdefault=value …]

    xcodebuild [-project name.xcodeproj] -scheme schemename [[-destination destinationspecifier] …] [-destination-timeout value] [-configuration configurationname] [-sdk [sdkfullpath | sdkname]] [action …] [buildsetting=value …] [-userdefault=value …]

命令中可以添加一些参数实现在命令执行时配置不同的环境。比如,如果想archiveDebug环境的包,那么就可以在命令中添加-configuration Debug 参数。还可以通过添加PROVISIONING_PROFILE参数来指定签名所用的 Profile 文件的UUID

  • 对于基于workspace的工程,比如cocoapods项目,脚本格式如下:

    xcodebuild -workspace name.xcworkspace -scheme schemename [[-destination destinationspecifier] …] [-destination-timeout value] [-configuration configurationname] [-sdk [sdkfullpath | sdkname]] [action …] [buildsetting=value …] [-userdefault=value …]

当然还有很多可选参数,在这就不一一列举了,如果有兴趣可以通过xcodebuild --help查看。

shell脚本(工程不是WorkSpace)

脚本下载路径:github下载地址

#author by 得力

#注意:脚本目录和xxxx.xcodeproj要在同一个目录,如果放到其他目录,请自行修改脚本。
#工程名字(Target名字)
Project_Name="Target名字,系统默认和工程名字一样"
#配置环境,Release或者Debug
Configuration="Release"

#AdHoc版本的Bundle ID
AdHocBundleID="com.xxx"
#AppStore版本的Bundle ID
AppStoreBundleID="com.xxx"
#enterprise的Bundle ID
EnterpriseBundleID="com.xxx"

# ADHOC
#证书名#描述文件
ADHOCCODE_SIGN_IDENTITY="iPhone Distribution: xxxx"
ADHOCPROVISIONING_PROFILE_NAME="xxxx-xxxx-xxxx-xxxx"

#AppStore证书名#描述文件
APPSTORECODE_SIGN_IDENTITY="iPhone Distribution: xxxx"
APPSTOREROVISIONING_PROFILE_NAME="xxxx-xxxx-xxxx-xxxx"

#企业(enterprise)证书名#描述文件
ENTERPRISECODE_SIGN_IDENTITY="iPhone Distribution: xxxxx"
ENTERPRISEROVISIONING_PROFILE_NAME="xxxx-xxxx-xxxx-xxxx"

#加载各个版本的plist文件
ADHOCExportOptionsPlist=./ADHOCExportOptionsPlist.plist
AppStoreExportOptionsPlist=./AppStoreExportOptionsPlist.plist
EnterpriseExportOptionsPlist=./EnterpriseExportOptionsPlist.plist

ADHOCExportOptionsPlist=${ADHOCExportOptionsPlist}
AppStoreExportOptionsPlist=${AppStoreExportOptionsPlist}
EnterpriseExportOptionsPlist=${EnterpriseExportOptionsPlist}

echo "~~~~~~~~~~~~选择打包方式(输入序号)~~~~~~~~~~~~~~~"
echo "  1 appstore"
echo "  2 adhoc"
echo "  3 enterprise"

# 读取用户输入并存到变量里
read parameter
sleep 0.5
method="$parameter"

# 判读用户是否有输入
if [ -n "$method" ]
then

#clean下
xcodebuild clean -xcodeproj ./$Project_Name/$Project_Name.xcodeproj -configuration $Configuration -alltargets

    if [ "$method" = "1" ]
    then

#appstore脚本
xcodebuild -project $Project_Name.xcodeproj -scheme $Project_Name -configuration $Configuration -archivePath build/$Project_Name-appstore.xcarchive clean archive build  CODE_SIGN_IDENTITY="${APPSTORECODE_SIGN_IDENTITY}" PROVISIONING_PROFILE="${APPSTOREROVISIONING_PROFILE_NAME}" PRODUCT_BUNDLE_IDENTIFIER="${AppStoreBundleID}"
xcodebuild -exportArchive -archivePath build/$Project_Name-appstore.xcarchive -exportOptionsPlist $AppStoreExportOptionsPlist -exportPath ~/Desktop/$Project_Name-appstore.ipa
    elif [ "$method" = "2" ]
    then
#adhoc脚本
xcodebuild -project $Project_Name.xcodeproj -scheme $Project_Name -configuration $Configuration -archivePath build/$Project_Name-adhoc.xcarchive clean archive build CODE_SIGN_IDENTITY="${ADHOCCODE_SIGN_IDENTITY}" PROVISIONING_PROFILE="${ADHOCPROVISIONING_PROFILE_NAME}" PRODUCT_BUNDLE_IDENTIFIER="${AdHocBundleID}"
xcodebuild -exportArchive -archivePath build/$Project_Name-adhoc.xcarchive -exportOptionsPlist $ADHOCExportOptionsPlist -exportPath ~/Desktop/$Project_Name-adhoc.ipa
    elif [ "$method" = "3" ]
    then
#企业打包脚本
xcodebuild -project $Project_Name.xcodeproj -scheme $Project_Name -configuration $Configuration -archivePath build/$Project_Name-enterprise.xcarchive clean archive build CODE_SIGN_IDENTITY="${ENTERPRISECODE_SIGN_IDENTITY}" PROVISIONING_PROFILE="${ENTERPRISEROVISIONING_PROFILE_NAME}" PRODUCT_BUNDLE_IDENTIFIER="${EnterpriseBundleID}"
xcodebuild -exportArchive -archivePath build/$Project_Name-enterprise.xcarchive -exportOptionsPlist $EnterpriseExportOptionsPlist -exportPath ~/Desktop/$Project_Name-enterprise.ipa
else
    echo "参数无效...."
    exit 1
    fi
fi

注意:1.由于脚本配置的路径问题,所以xcodebuild.shxxx.xcodeproj放到同一个目录下,否则会出现路径问题。如图所示:

脚本目录和工程在同一个目录

2.由于Xcode8可以在Project->General中自动配置证书,所以用脚本打包前先去掉该功能。如图所示:

去掉自动配置证书

3.配置脚本,需要配置的信息如下图,不需要的版本可以不用配置。比如只需要AppStoreipa,则只需要配置AppStore版本相关的配置。

配置脚本

4.执行脚本,打开终端,cd到当前脚本所在路径,然后执行: ./xcodebuild.sh即可。
5.导出的ipa包默认保存到桌面,当前保存目录如果需要修改,可以自己修改脚本。如图所示,比如我项目名字是OneProject,则导出的包如下图:

ipa包文件夹

打开文件夹就是ipa包了,如图:

ipa包

6.如何查看证书名字和配置文件的UUID呢?
打开钥匙串访问,找到证书,点击显示简介,里面有个常用名字,复制到脚本中即可,如图所示:

证书

常用名称

配置文件UUID可以在Xcode中查看,Xcode->Preferences...->Accounts,如下图:

点击View Details

点击Show in Finder

配置文件UUID

上面的配置文件的名字是6994F55C-1960-4EF9-AA7E-9C1FABDBA7A8.mobileprovision。则配置的文件的UUID就是6994F55C-1960-4EF9-AA7E-9C1FABDBA7A8。所以复制6994F55C-1960-4EF9-AA7E-9C1FABDBA7A8到脚本中。

一般存在于~/Library/MobileDevice/Provisioning Profiles目录中

 

 

执行脚本

打开终端,cd到当前脚本所在路径,在终端输入:./xcodebuild.sh,点击回车即可。

WorkSpace脚本

如果你的项目用的workspace,或者cocoapods,则上面的脚本不适用了,具体脚本如下,脚本配置和上面一样。
脚本下载路径:github下载地址

#author by 得力

#注意:脚本目录和WorkSpace目录在同一个目录
#工程名字(Target名字)
Project_Name="Target名字,系统默认等于工程名字"
#workspace的名字
Workspace_Name="WorkSpace名字"
#配置环境,Release或者Debug,默认release
Configuration="Release"

#AdHoc版本的Bundle ID
AdHocBundleID="com.xxxx"
#AppStore版本的Bundle ID
AppStoreBundleID="com.xxxx"
#enterprise的Bundle ID
EnterpriseBundleID="com.xxxx"

# ADHOC证书名#描述文件
ADHOCCODE_SIGN_IDENTITY="iPhone Distribution: xxxx"
ADHOCPROVISIONING_PROFILE_NAME="xxxxx-xxxx-xxxx-xxxx-xxxxxx"

#AppStore证书名#描述文件
APPSTORECODE_SIGN_IDENTITY="iPhone Distribution: xxxxx"
APPSTOREROVISIONING_PROFILE_NAME="xxxxx-xxxx-xxxx-xxxx-xxxxxx"

#企业(enterprise)证书名#描述文件
ENTERPRISECODE_SIGN_IDENTITY="iPhone Distribution: xxxx"
ENTERPRISEROVISIONING_PROFILE_NAME="xxxxx-xxxx-xxx-xxxx"

#加载各个版本的plist文件
ADHOCExportOptionsPlist=./ADHOCExportOptionsPlist.plist
AppStoreExportOptionsPlist=./AppStoreExportOptionsPlist.plist
EnterpriseExportOptionsPlist=./EnterpriseExportOptionsPlist.plist

ADHOCExportOptionsPlist=${ADHOCExportOptionsPlist}
AppStoreExportOptionsPlist=${AppStoreExportOptionsPlist}
EnterpriseExportOptionsPlist=${EnterpriseExportOptionsPlist}

echo "~~~~~~~~~~~~选择打包方式(输入序号)~~~~~~~~~~~~~~~"
echo "  1 adHoc"
echo "  2 AppStore"
echo "  3 Enterprise"

# 读取用户输入并存到变量里
read parameter
sleep 0.5
method="$parameter"

# 判读用户是否有输入
if [ -n "$method" ]
then
    if [ "$method" = "1" ]
    then
#adhoc脚本
xcodebuild -workspace $Workspace_Name.xcworkspace -scheme $Project_Name -configuration $Configuration -archivePath build/$Project_Name-adhoc.xcarchive clean archive build CODE_SIGN_IDENTITY="${ADHOCCODE_SIGN_IDENTITY}" PROVISIONING_PROFILE="${ADHOCPROVISIONING_PROFILE_NAME}" PRODUCT_BUNDLE_IDENTIFIER="${AdHocBundleID}"
xcodebuild  -exportArchive -archivePath build/$Project_Name-adhoc.xcarchive -exportOptionsPlist ${ADHOCExportOptionsPlist} -exportPath ~/Desktop/$Project_Name-adhoc.ipa

    elif [ "$method" = "2" ]
    then
#appstore脚本
xcodebuild -workspace $Workspace_Name.xcworkspace -scheme $Project_Name -configuration $Configuration -archivePath build/$Project_Name-appstore.xcarchive archive build CODE_SIGN_IDENTITY="${APPSTORECODE_SIGN_IDENTITY}" PROVISIONING_PROFILE="${APPSTOREROVISIONING_PROFILE_NAME}" PRODUCT_BUNDLE_IDENTIFIER="${AppStoreBundleID}"
xcodebuild  -exportArchive -archivePath build/$Project_Name-appstore.xcarchive -exportOptionsPlist ${AppStoreExportOptionsPlist} -exportPath ~/Desktop/$Project_Name-appstore.ipa

    elif [ "$method" = "3" ]
    then
#企业打包脚本
xcodebuild -workspace $Workspace_Name.xcworkspace -scheme $Project_Name -configuration $Configuration -archivePath build/$Project_Name-enterprise.xcarchive archive build CODE_SIGN_IDENTITY="${ENTERPRISECODE_SIGN_IDENTITY}" PROVISIONING_PROFILE="${ENTERPRISEROVISIONING_PROFILE_NAME}" PRODUCT_BUNDLE_IDENTIFIER="${EnterpriseBundleID}"
xcodebuild  -exportArchive -archivePath build/$Project_Name-enterprise.xcarchive -exportOptionsPlist ${EnterpriseExportOptionsPlist} -exportPath ~/Desktop/$Project_Name-enterprise.ipa
    else
    echo "参数无效...."
    exit 1
    fi
fi

可能出现的问题

1.如果配置的证书名字、BundleID配置文件UUID不一致,脚本就会报错,这个可以看脚本提示错误,由于错误非常明显,所以就不在截图了。
2.如果执行脚本的过程出现如下错误,在终端输入:rvm system,回车即可。

3.如果执行脚本的过程出现-bash: ./xx.sh: Permission denied,表示权限问题,所以在终端执行:chmod 777 xx.sh,然后回车即可,但是要注意: chmod 777后面是脚本路径。

补充

上面脚本默认启动Bitcode,如果你的项目中没有开启Bitcode,则可以在上面几个plist文件中加上两个键值对,分别是:uploadBitcode=NO,compileBitcode=NO

模拟器中运行iOS应用

iOS应用安装包的基础知识

作为完全的iOS新手,困惑的第一个问题就是iOS安装包文件。

在Android系统中,安装App的途径很多,除了各类应用市场,普通用户也经常直接下载apk安装包文件后手动进行安装,因此大家对Android的安装包文件都比较熟悉。

但是对于iOS系统就不一样了,由于我们普通用户在iOS上安装应用的时候基本上只能通过Apple Store进行安装(未越狱),没有机会接触原始的安装包文件,因此往往连iOS应用的安装包到底是什么格式后缀都不清楚。

现在我们想在Appium App中通过模拟器运行被测应用,需要指定iOS app的安装包路径,因此需要首先获得一个iOS app安装包。

Appium initialize iOS Settings

那么iOS app的安装包长啥样呢?

或者在这个问题之前,我们先来看下另一个问题:对于iOS设备来说,如果不通过Apple Store,我们可以怎样安装一个应用?

针对这个问题,我搜了些资料,也请教了周围的同事,了解到的途径有如下几个:

  • 企业证书:该种方式适用于企业内部;通过企业证书编译出的iOS应用,无需上传至Apple Store,即可无限制的安装到企业员工的iOS设备中。只是需要解决的一个问题是,由于iOS设备没有文件管理器,没法将安装包拷贝到iOS设备中,因此常用的做法是将安装包(.ipa文件)上传至一些下载服务器(例如fir.im),并生成二维码,然后用户扫描二维码后即可通过浏览器下载安装包并进行安装。由此联想到另外一个方法,通过微信文件传输助手将安装包(.ipa)传输至iOS设备,然后再进行安装应该也是可以的吧?这种方法不知在原理上是否可行,因为在试验时由于安装包大于30M,微信无法传输,所以没能进行验证。
  • Xcode:该种方式适用于iOS开发者;开发者在Xcode中连上iOS设备对源码进行编译,编译生成的应用会自动安装至iOS设备。当然,该种方式也是需要iOS开发者证书。
  • PP助手:该种方式适用于普通用户;PP助手是一个非苹果官方的设备资源管理工具,可以实现对未越狱的iOS设备进行应用管理,也可以安装本地.ipa文件,前提是.ipa文件具有合适的签名。

在上面列举的安装应用的途径中,反复提到了.ipa文件,那.ipa应该就是iOS应用程序的后缀了吧?暂且这么认为吧。

再回到前面的场景,要在iOS模拟器中运行iOS应用,我们是否可以找研发人员要一个.ipa安装包文件,然后就能在模拟器中加载运行应用呢?

刚开始的时候我是这么认为的。于是我获取到.ipa文件后,在App Path中填写该文件的路径,然后启动Appium Server;接着我再打开Inspector时,发现iOS模拟器启动了,但是在应用启动的时候就出问题了,始终无法正常启动,感觉像是启动崩溃,反复尝试多次仍然如此。

再次经过Google,总算是明白出现问题的原因了,总结下来有如下几点:

  • 不管是从Apple Store或iTunes上下载的应用,还是在Xcode中针对真机设备编译生成的.ipa文件,都是面向于ARM处理器的iOS设备,只能在真机设备中进行安装;
  • 而在Mac OSX系统中运行的iOS模拟器,运行环境是基于Intel处理器的;
  • 因此,若是针对真机设备编译生成的.ipa文件,是无法在iOS模拟器中正常运行的,毕竟处理器架构都不一样;
  • 要想在iOS模拟器中运行应用,则必须在Xcode中编译时选择模拟器类型;编译生成的文件后缀为.app

准备.app文件

接下来,就说下如何获取.app文件。

虽然是测试人员,不会对被测iOS项目贡献代码,但是也不能总是找研发帮忙编译生成.app文件。所以,在本地搭建完整的iOS项目开发环境还是很有必要的。

对于iOS开发环境的搭建,当前社区中应该已经有了很多完整的教程,我在这儿就不详细描述了,只简单说下我搭建过程中涉及到的几个点。

首先,Mac OSX、Xcode、Apple Developer Tools这些基础环境的安装,在上一篇文章中已经进行说明了;

然后,申请项目源码的访问权限,git clone到本地;

接着是项目依赖环境的问题;通常一个较大型的iOS项目都会引用许多第三方库,而这些依赖库并不会直接保存到项目仓库中,通常是采用CocoaPods进行管理;简单地说,CocoaPods是针对SwiftObjective-C项目的依赖管理器,类似于Java中的Maven,Ruby中的Gem,Python中的pip

当然,iOS项目的依赖管理工具也不是只有CocoaPods一个,如果是采用的别的依赖管理器,请自行查找对应的资料。

采用CocoaPods管理的项目,在项目根目录下会包含PodfilePodfile.lock文件,里面记录了当前项目依赖的第三方库以及对应的版本号。

安装CocoaPods很简单,采用gem即可。

1
$ sudo gem install cocoapods

然后,进入到iOS项目的目录,执行pod install命令即可安装当前项目的所有依赖。

1
2
3
4
5
6
7
8
9
10
11
$ cd Project_Folder
$ pod install
Re-creating CocoaPods due to major version update.
Analyzing dependencies
…..(略)
Downloading dependencies
…..(略)
Generating Pods project
Integrating client project
Sending stats
Pod installation complete! There are 27 dependencies from the Podfile and 28 total pods installed.

关于CocoaPods的更多信息,请自行查看官方网站

在依赖安装完成后,正常情况下,就可以在Xcode中编译项目了。

没有别的需要注意的,将target选择为模拟器(iOS Simulator)即可。而且针对模拟器进行编译时,也不会涉及到开发者证书的问题,项目配置上会简单很多。待后续讲到真机上的自动化测试时,我再对证书方面的内容进行补充。

编译完成后,在Products目录下,就可以看到XXX.app文件,这里的XXX就是项目名称;然后,选中XXX.app文件,【Show in Finder】,即可在文件目录中定位到该文件。

接下来,将XXX.app文件拷贝出来,或者复制该文件的Full path,怎样都行,只要在AppiumApp Path中能定位到该文件就行。

模拟器中运行iOS应用

被测应用.app准备就绪后,接下来就可以在iOS模拟器中运行了。

回到前面的那张图。启动Appium app后,对于模拟器运行的情况,在iOS Settings中必须设置的参数项就3个,App PathForce DevicePlatform Version。对于真机运行的情况,后续再单独进行说明。

设置完毕后,点击【Launch】,启动Appium Server

Appium inspector button

然后,点击图中红框处的按钮,即可通过Inspector启动模拟器,并在模拟器中加载iOS应用。

Appium iOS Simulator Console

在模拟器中,我们可以像在真机中一样,体验被测应用的各项功能;并且,在Appium的日志台中,可以实时查看到日志信息。

经历的一个坑

整个过程是挺简单的,不过,在探索过程中我还是有遇到一个坑。

通过Inspector启动模拟器时,总是弹框报错,报错形式如下。

Appium Inspector Error

刚开始出现这问题时百思不得其解,因为提示的信息并不明显,Google了好一阵也没找到原因。最后只有详细去看日志信息,才发现问题所在。

在日志中,发现的报错信息如下:

1
[iOS] Error: Could not find a device to launch. You requested \’iPhone 6 (8.4)\’, but the available devices were: [“Apple TV 1080p (9.2) [98638D25-7C82-48DF-BDCA-7F682F951533] (Simulator)”,”iPad 2 (9.2) [5E22F53E-EAB3-45DF-A1DD-10F58E920679] (Simulator)”,”iPad 2 (9.3) [4B2D2F9A-C099-4C13-8DE9-27C826A521C2] (Simulator)”,”iPad Air (9.2) [825E4997-9CD8-4225-9977-4C7AE2C98389] (Simulator)”,”iPad Air (9.3) [E4523799-E35F-4499-832B-12CF33F09144] (Simulator)”,”iPad Air 2 (9.2) [8057039D-F848-453E-97EC-2F75CAEA2E77] (Simulator)”,”iPad Air 2 (9.3) [0B8F49DA-832A-4248-BA1D-9DA5D11E31FD] (Simulator)”,”iPad Pro (9.2) [AF1F2D06-3067-41B5-AC2B-4B0ED88BF5D9] (Simulator)”,”iPad Pro (9.3) [C39617A6-9D91-4C0B-B25B-741BD57B016C] (Simulator)”,”iPad Retina (9.2) [D3C694E1-E3B4-47BE-AB5E-80B3D4E22FC2] (Simulator)”,”iPad Retina (9.3) [907C7B06-ED2C-48AC-AC46-04E4AD6E0CA3] (Simulator)”,”iPhone 4s (9.2) [1A786195-94E3-4908-8309-7B66D84E4619] (Simulator)”,”iPhone 4s (9.3) [3F76F34B-5A8F-4FD1-928D-56F84C192DDD] (Simulator)”,”iPhone 5 (9.2) [0D79A4CA-71EB-48A6-9EE4-172BEF3EB4E0] (Simulator)”,”iPhone 5 (9.3) [04270D44-F831-4253-95F2-3D205D2BC0D9] (Simulator)”,”iPhone 5s (9.2) [13A16C07-3C5B-4B04-A94B-B40A63238958] (Simulator)”,”iPhone 5s (9.3) [D30A7B34-BA01-4203-80DA-FAEA436725F9] (Simulator)”,”iPhone 6 (9.2) [5D01650F-2A31-4D53-A47A-CCF7FD552ADD] (Simulator)”,”iPhone 6 (9.3) [2F0810F6-C73B-4BA4-93BA-06D4B6D96BDA] (Simulator)”,”iPhone 6 Plus (9.2) [9A840B78-E6CE-4D18-BE83-16B590411641] (Simulator)”,”iPhone 6 Plus (9.3) [27C6557A-B09D-4D8A-9846-DA8FE0A8E8D5] (Simulator)”,”iPhone 6s (9.2) [E7F5B8A5-0E85-404F-A4D4-191D63E7EC1B] (Simulator)”,”iPhone 6s (9.3) [6F702911-13C2-472C-9ECD-BADD4385CB77] (Simulator)”,”iPhone 6s (9.3) Apple Watch – 38mm (2.2) [B63FFAA4-00A4-473B-9462-3664F41F9001] (Simulator)”,”iPhone 6s Plus (9.2) [58837F78-511A-4F0B-9DDF-782E3B9935BD] (Simulator)”,”iPhone 6s Plus (9.3) [C31003C6-DCE2-414D-AD7F-376F6FA995B0] (Simulator)”,”iPhone 6s Plus (9.3) Apple Watch – 42mm (2.2) [E3154768-CA23-45CC-90E5-2D0386A57B7D] (Simulator)”]

问题在于,我设置iOS Settings时,将Force Device设置为”iPhone 6”,将Platform Version设置为“8.4”,但是经过组合,iPhone 6 (8.4)并不在可用的模拟器设备列表中。

再来看日志中提示的可用设备,发现“iPhone 6”设备对应的Platform Version只有“9.2”和“9.3”。然后回到iOS Settings,发现Platform Version的下拉框可选项就没有“9.2”和“9.3”,最新的一个可选版本也就是“8.4”。

Appium iOS Settings bug

这应该是Appium app的一个bug吧。不过好在Platform Version参数虽然是通过下拉框选择,但是也可以在框内直接填写内容。于是我在Platform Version设置框内填写为“9.3”,然后再次启动时,发现iOS模拟器就可以正常启动了。

To be continued …

现在,我们已经成功地通过Appium Inspector调用模拟器并运行iOS应用,接下来,我们就要开始尝试编写自动化测试用例了。

在下一篇文章中,我们将对Appium Inspector的功能进行熟悉,通过Inspector来查看iOS应用的UI元素信息,并尝试采用脚本语言与UI进行交互操作。

ipa再次签名

iOS代码签名

很多人研究到上面也就停止了,然而生命不息,作死不止。上面很多次提到代码签名,那么究竟代码是如何签名的。这对于可能需要做自动签名发布的企业或团队是必须了解的。另外,你可能还需要去阅读iReSign的源码。

ipa的组成

iOS程序最终都会以.ipa文件导出,先来了解一下ipa文件的结构:

事实上,ipa文件只是一个zip包,可以使用如下命令解压:

/usr/bin/unzip -q xxx.ipa -d 

解压后,得到上图的Payload目录,下面是个子目录,其中的内容如下:

  1. 资源文件,例如图片、html、等等。
  2. _CodeSignature/CodeResources。这是一个plist文件,可用文本查看,其中的内容就是是程序包中(不包括Frameworks)所有文件的签名。注意这里是所有文件。意味着你的程序一旦签名,就不能更改其中任何的东西,包括资源文件和可执行文件本身。iOS系统会检查这些签名。
  3. 可执行文件。此文件跟资源文件一样需要签名。
  4. 一个mobileprovision文件.打包的时候使用的,从MC上生成的。
  5. Frameworks。程序引用的非系统自带的Frameworks,每个Frameworks其实就是一个app,其中的结构应该和app差不多,也包含签名信息CodeResources文件

相关的程序和命令

一般我们会用Xcode自带的archive功能来打包ipa和签名,实际上xcode只不过是调用了一些外部程序完成了工作,如果我们有朝一日需要自己实现自动化的签名流程,就需要了解究竟相关的程序和命令有哪些。

用下面命令,列出系统中可用于签名的有效证书:

/usr/bin/security find-identity -v -p codesigning

1) E056929276F94152F3FDF0EA84BD2B06396F2DDD "iPhone Developer: Liang Ding (2U967A2YJ6)"
2) 7C608F653A989E95E1A4D303EC4E6625D95EEB42 "iPhone Distribution: Liang Ding (7XPNRZE9TC)"
  2 valid identities found

可以看到这个命令列出了一个字符串标示的证书名称,如:iPhone Developer: Liang Ding (2U967A2YJ6)。这个名称后面会用到的。

使用如下命令对xxx.app目录签名,codesign程序会自动将其中的文件都签名,(Frameworks不会自动签):

/user/bin/codesign -fs "iPhone Developer: Liang Ding (2U967A2YJ6)" --no-strict Payload/xxx.app

对于每个Framework,也需要使用这个命令签名,上面说了Framework的结构跟app其实差不多,所以签名命令类似。这个命令会自动找到证书相关的私钥。-f表示对于已经签名的app强制重签。

最后用下面命令校验签名是否合法:

/usr/bin/codesign -v xxx.app

如果没有任何输出说明没有问题。

使用zip命令重新打包成ipa包

/usr/bin/zip -qry destination source

对app重新签名的流程

如果要设计一个自动化的重签程序,大致需要这么个流程:

  1. 首先解压ipa
  2. 如果mobileprovision需要替换,替换
  3. 如果存在Frameworks子目录,则对.app文件夹下的所有Frameworks进行签名,在Frameworks文件夹下的.dylib.framework
  4. 对xxx.app签名
  5. 重新打包

iOS设备如何验证app是否合法

关键的几个点:

  1. 解压ipa
  2. 取出embedded.mobileprovision,通过签名校验是否被篡改过 a. 其中有几个证书的公钥,其中开发证书和发布证书用于校验签名 b. BundleId c. 授权列表
  3. 校验所有文件的签名,包括Frameworks
  4. 比对Info.plist里面的BundleId是否符合embedded.mobileprovision文件中的

总结

非对称密钥算法是基石,本文比较详细的阐述了非对称加密算法和摘要算法,并逐渐引出数字签名和数字证书。理解非对称密钥算法是关键。

苹果通过证书来授权开发者开发iOS应用,不同的证书具有不同的用处,建议申请时使用相同的请求文件(即保证私钥统一)。可以通过共享私钥的方式让团队使用相同的私钥和证书,已方便开发。为了保证app的安全性,app中所有的文件都会被签名,这样,签过名的app除非重新签名,否则无法改动其中的任何东西。

mobileprovision是一个配置文件,由苹果签名并发布给开发者。配置文件是一组信息的集合,这组信息决定了某一个应用是否能够在某一个特定的设备上运行。配置文件可以用于让应用在你的开发设备上可以被运行和调试,也可以用于内部测试 (ad-hoc) 或者企业级应用的发布。有了配置文件,苹果对开发者的约束就十分稳固了。

所以,证书(及其对应的私钥)和配置文件是签名和打包的两个必要文件。必须深刻理解,才能在日常的错误中找到解决办法。

 

iOS 自动构建命令:xcodebuild

xcodebuild 是苹果发布的自动构建工具,用法可以在终端输入man xcodebuild 查看。

官方文档:https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html

xcodebuild的使用方法:

在项目目录(包含projectname.xcodeproj文件的目录)下,运行xcodebuild命令:

默认xcodebuild命令会构建第一个target,也可以用-targetname指定target。如果要构建workspace,需指定-workspace和-scheme参数。

编译打包步骤:

第一步:清理

xcodebuild -target targetname clean 清理上一次运行缓存结果

第二步:编译

使用pod进行第三方库管理的工程,工程目录下会有两个文件: .xcworkspace, .xcodeproj xcworkspace是工作区文件,xcodeproj是工程文件,工作区文件包含工程文件。

1, 如果有工作区xcworkspace的可以直接用工作区编译

xcodebuild -workspace workspacename -scheme schemename [-destination destinationspecifier]
            [-destination-timeout value] [-configuration configurationname]
            [-sdk [sdkfullpath | sdkname]] [buildaction ...] [setting=value ...]
            [-userdefault=value ...]

2, 如果只有xcodeproj文件,命令如下

xcodebuild [-project projectname] [-target targetname ...] [-configuration configurationname]
            [-sdk [sdkfullpath | sdkname]] [buildaction ...] [setting=value ...]
            [-userdefault=value ...]

参数:

-project #项目名字

-scheme #scheme名字

-target #target可以通过命令xcodebuild -list获取

-configuration #Debug或者Release

-sdk #可以通过xcodebuild -showsdks获取

构建的动作build, analyze, archive, test, install, clean 默认是build

CONFIGURATIONBUILDDIR #build文件夹

CODESIGNIDENTITY #签名,选择签名的证书,不知道自己证书名字的同学,可以用系统自带的工具“钥匙访问串”来查看,如果不带侧参数,那么在工程配置中一定要配置好证书以及授权文件!

PROVISIONING_PROFILE #签名profile

运行成功后,在项目目录下生成build文件夹,文件下有生成的.app以及dSYM文件。

第三步:打包

找到编译成的targetname.app文件夹,使用下面的命令进行打包生成.ipa文件

xcrun -sdk iphoneos PackageApplication  "所在目录/targetname.app" -o 想要输出的目录/文件名.ipa

打包成功后,输出的目录下找到ipa

一个签名工具:

https://github.com/fastlane/fastlane/tree/master/sigh

 

更多内容可参考这几篇:

Inside Code Signing

代码签名探析

iOS Code Signing 学习笔记

ios 证书的解释

证书的作用

当某台电脑安装开发者证书后,这台电脑是如何拥有这种能力的呢?

  • 苹果在此运用了代码签名技术。代码签名验证允许我们的操作系统来判断是谁(你或者信任的团队成员)对App进行了签名。
  • Xcode会在项目编译期间使用你的代码签名验证,这个验证由一个由Apple认证过的公钥-私钥对组成,存储在你的Keychain(下简称钥匙串)中,公钥包含在证书(Certificates)中,公钥证书本地钥匙串开发者账号都有存储。
  • 另外,还有一个我们可以叫做媒介证书(Intermediate Certificate)的证书来确保我们的证书(Certificates)是经过授权而发布的。当安装好Xcode时,媒介证书就已经安装到我们的钥匙串中去了。如果你不小心删除了你的媒介证书,不用担心。你可以重新下载它。
  • 通过在开发者账号Developer Account)和本地(Mac)都经过验证的证书(Certificate)我们就可以利用合法的证书进行App的测试和发布了。

证书的使用

如果开发者B,登录开发者账号,下载证书(cer文件)运行,只有证书没有私钥,是不能正常使用的。所以如果有新同事加入到开发组的时候,应该从本地钥匙串中选择证书,一定要记得展开证书那一条显示出私钥并将两行都选中,右键导出2项,输入密码之后就生成p12文件(包含证书和私钥)给同事。

另外可以给同事一份描述文件(Provisioning Profiles),用于本地开发识别测试设备。

需要强调一点,证书和项目关系其实并不大,证书一般有效期只有一年,当证书过期后,只需要重新生成一份证书,上传到开发者账号就行,同时因为原有证书过期,需要重新生成Provisioning Profiles文件。然后给同事们最新的p12文件和Provisioning Profiles文件就行

所以开发者账号中的证书,配置文件是可以放心操作的(比如误删了,或者找不到证书私钥了)

  • 生成证书请求文件时,Keychain将生成一个包含开发者身份信息的CSR(Certificate Signing Request)文件。同时,Keychain Access->Keys(密钥)中增加一对Public/Private Key Pair。
  1. 私钥(private key)始终保存在Mac OS的Keychain Access中,用于签名(CodeSign)对外发布的App。
  2. 公钥(public key)一般随证书散布出去,对App签名进行校验认证。

下载的开发证书证书,双击导入Keychain Access,可在Keychain Access->“证书”中查看

展开安装的证书(ios_development.cer)前面的箭头,可以看到其对应的私钥。在KeychainAccess->“密钥”中展开创建CSR时生成的Key Pair中的私钥前面的箭头,可以查看到包含其对应公钥的证书.

注意:一个开发者账号只能创建(1-2个开发(测试)证书),2-3个生产(发布)证书),所以如果你App Store Ad Hoc这个位置前面按钮不能选择,则代表你的这个账号无法再创建新的生产证书了

1.请从别人那里要这个p12文件,尽量不要执行下面第2步

2.如果你想生成的话,把现有的删除一个(建议删除时间比较靠前的)注意:如果删除了一个证书,那么正在使用这个证书的人将不能再使用了,除非重新生成,然后利用.p12重新导入自己电脑里!

我们的每一个证书都可以生成一个.p12文件,这个文件是一个加密的文件,只要知道其密码,就可以供给所有的mac设备使用,使设备不需要在苹果开发者网站重新申请开发和发布证书,就能使用。

注意:一般.p12文件是给与别人使用的,本机必须已经有一个带秘钥的证书才可以生成.p12文件,如图:

 

打开钥匙串,选择我们需要的证书,右击,选择“导出“iPhone Distribition:“`”:

 

为.p12文件填一个名字,点击存储:

填一个“密码”,“验证”和密码相同:

点击好,在桌面上就形成了一个.p12文件了:

在别人的电脑上要成功安装,须具备两个文件:

  • 该证书导出的“.p12”文件。
  • 苹果开发者中的与“证书”对应的“描述文件”

如果需要用于开发的,那么需要这两个文件:

.p12文件本身就是一个加密的证书,所以用这两个文件就可以让其他mac设备使用了:

拿到这两个文件后,依次双击“证书”,“.p12“文件(需要输入密码,密码就是当时生成.p12时设定的密码,),作用是加入钥匙串中,使我们的电脑具备开发的证明,”描述文件“(作用是置于xcode中,让xcode知道我们开发的合法性),添加完后就可以使用了

Provisioning Profile文件包含了上述所有内容:证书App ID设备,后缀名为.mobileprovision。

  1. 一个Provisioning Profile对应一个Explicit App ID或Wildcard App ID。
  2. Provisioning Profile决定Xcode用哪个证书(公钥)/私钥组合(Key Pair/Signing Identity)来签名应用程序(Signing Product),将在应用程序打包时嵌入到.ipa包里。
  3. Provisioning Profile把这些信息全部打包在一起,方便我们在调试和发布程序打包时使用。这样,只要在不同的情况下选择不同的Provisioning Profile文件就可以了。
  4. Provisioning Profile也分为Development和Distribution两类,有效期同Certificate一样。Development版本的ProvisioningProfile用于开发调试,Distribution版本的ProvisioningProfile主要用于提交App Store审核,其不指定开发测试的Devices。
  5. xcode开发时,需在XcodeTarget->Build Settings->Code Signing->Provisioning Profile下配置对应的描述文件(Provisioning Profiles),然后在Code Signing Identity下拉可选择Identities from Profile “…”(即Provisioning Profile中包含的Certificates)。
  6. 在XcodeTarget->Build Settings->Code Signing->Provisioning Profile可选择“Automatic”,xcode会根据该Target的“Bundle identifier”选择默认的配置文件及证书。
下载到本地的Provisioning Profile,双击自动安装到xcode中,在XcodeTarget->Build Settings->Code Signing->Provisioning Profile中可看到此项。
如图,选择此项配置文件,“Code Signing Identity”中会默认显示所关联的证书文件(即创建该配置文件时选择关联的证书,可有多个)

mobileprovision文件包含:

  1. AppId。每个app必须在MC中创建一个对应的AppId。规则不累述了。
  2. 使用哪些证书。上面说了,不同类型的证书就代表了不同的发布方式,还包括一些功能的能否使用(比如APN)
  3. 功能授权列表
  4. 可安装的设备列表。对于AdHoc方式发布的app或者真机调试时,会有一个列表,这个列表里面是iOS设备的UDID,每台iOS设备出厂的UDID都不同,所以可以用来标识设备。可通过iTunes连接设备,或者http://fir.im/udid这里获取
  5. 苹果的签名!

注意5这里的签名是苹果签的,跟我们的私钥没有关系。也就是说mobileprovision文件是苹果签名的,我们除了从MC中获取,别无他法。也不能再获取后随意篡改(比如添加别的设备)。因此上面的1-4就被苹果牢牢的控制在手里,所有的规则都必须由苹果来制定和约束。

[iOS]App上架流程[利用Archive进行上传]

今天给大家带来项目如何上架的教程


准备:

1. 一个开发者账号(需要交过钱的,🍎好黑)

2. 一个做完的项目


检查:

1. 你的Xcode必须是正式版,不可以是beta版本(不能上传项目)

2. 请确认你的Xcode是从App Store或者从开发者网站下的Xcode安装,而不是非法获取的安装包,因为可能有Ghost。如何检查?

开始吧。

一。生成发布证书(证书的作用:类似于驾照,证明你的身份可以进行开发者一些操作)

打开https://developer.apple.com点击右上角开发者中心

这里输入你付款过的Apple 帐号和密码

下面点击Certificates,Identifiers&Profiles 这个选项 (进去是专门生成证书,绑定Bundle Id,绑定device设备,生成描述文件的地方)

这里我们选择Certificates,

然后我们在1对应的位置选择iOS App(因为我们要上传的是iOS项目)然后 点击右上角新添加证书

由于我们要做项目上传,选择生产证书(选择App store那个)

注意:一个开发者账号只能创建(1-2个开发(测试)证书),2-3个生产(发布)证书),所以如果你App Store Ad Hoc这个位置前面按钮不能选择,则代表你的这个账号无法再创建新的生产证书了

1.请从别人那里要这个p12文件,尽量不要执行下面第2步

2.如果你想生成的话,把现有的删除一个(建议删除时间比较靠前的)注意:如果删除了一个证书,那么正在使用这个证书的人将不能再使用了,除非重新生成,然后利用.p12重新导入自己电脑里!

注意:如果你想要删除证书,执行这步骤,否则掠过

然后接着上上张图,生产证书那部继续

这个时候,我们需要上传CSR文件去获取证书,CSR文件需要我们到本机钥匙串里去创建

在其他里面,点击  钥匙串访问 然后弹出如下界面

然后把CSR文件保存到计算机磁盘的某个位置,我这里选择了桌面,进行存储

然后回到网页上,选择CSR文件上传

找到刚才保存到桌面的CSR文件,然后点击打开

然后跳转到如下界面,点击DownLoad  下载生成的证书

注意:这个证书只能下载一次。点击下载后。如果想给别的电脑用。请继续往下看(需要导出成.p12格式文件到别到电脑上运行)

如果不想给别的电脑用,则直接跳转到2,绑定Bundle ID

生成p12文件在别的电脑使用这个发布证书,请看这里

安装证书后,打开钥匙串访问,然后

然后右键

注意(如果没有导出,可以把这个证书删除,然后重新双击下载的证书文件)

然后(可以设置密码,也可以不设置密码,如果设置密码了,别人双击打开你给的p12文件的话就需要输入密码)这里直接不设置密码,继续

然后我刚才是保存在桌面的,那么出来了这个!在桌面

如果想在别的机器上 也能发布你的代码,则必须要安装这个发布证书。

2. 绑定你的App的Bundle Identifier  (Bundle Identifier 是你的App的唯一标识,跟身份证一样)

回到刚才的网页上 (https://developer.apple.com/account/ios/identifiers/bundle/bundleList.action

下面起一个名字,不能使用@,&,*,’,”。

上传应用程序使用的Bundle ID必须是固定的,不能使用占位符

打开工程找到你的Bundle Identifier (不要有-,都是英文+数字) (在Xcode7.0之后,直接在这个位置修改Bundle Identifier就可以,然后回车)

注意:如果你的Bundle Identifier已经在网站上绑定了,如果你修改了你工程里这个Bundle Identifier的话,需要重新去网页绑定

下面选择app中包含的服务,默认有两项,其余的根据自己的需求进行选择

然后点击底部Done

3. 生成描述文件 (描述文件的作用是把证书和Bundle Identifier关联起来)

因为我们是要发布,所以选择下面发布的描述文件

然后选择我们要发布的项目的Bundle Identifier

然后选择发布证书,根据自己电脑上的发布证书日期来选择

然后给描述文件起个名字。

然后下载描述文件,双击安装(闪一下就完事了,没有其他效果)

4. 描述文件下载后。我们网页的旅程还没结束。需要去这个网站 占坑(也就是注册你的应用名字, 如果你已经在itunesConnect网址注册好了你的应用,则忽略4)。

itunesconnect.apple.com

然后还是支付过钱的那个开发者账号才能登陆。

登录成功后,我们点击   我的App

然后点击左上角那个+号,创建我们的应用

然后点击新建App (注意 我们是iOS App开发,不要选Mac App啦)

SKU公司用于做统计数据之类的id,根据公司需求填写

如果都填写好以后,可能会告诉你,你的App名字已经被占用,那么不好意思,你只能改名了!(而且推荐大家的名字不要往比较出名的App上靠)否则审核可能会被拒绝。

下面我们的开发者账号没有签订纳税合同,所以不能上线收费应用,所以只能暂时免费

然后右面这底下 需要填写不同尺寸的手机屏幕截图(也就是拿不同尺寸的模拟器运行后,挑出至少3页最多5页进行截图然后拖到响应的区里)(在模拟器Command+S 就可以保存屏幕截图到桌面了)(注意:如果提示拖进去的图片尺寸不对,则把模拟器弄成100%然后再Command  加  S) 尺寸参照表在下面

尺寸参照

错误提示:如果上传App 图标失败,提示alpha错误的话看下面。

打开你的图标图片,勾掉这个就可以了。

点击分级后面的编辑跳入

如实填写后点击完成

版本发布就是:(然后最下面选择自动发布的话就是如果审核通过,就自动上传到App Store供人下载)

5. 配置Xcode自己的工程

打开你项目工程 第一步,这里不能选择模拟器,选择iOS Device

如果不支持横屏,把这2个勾去掉

然后查看版本号和构建版本号

然后配置发布证书

然后,如果这里没有黄色叹号,代表你的配置没问题,如果有,那就是证书和描述文件不匹配,或者描述文件里刚才选的Bundle和现在的工程的Bundle Identifier不一致,去develop.apple.com 上找到你的描述文件在确认下绑定的bundle Identifier和你工程是不是一样的?

然后继续。断点,全局断点,僵尸模式等都要去掉。

然后设置Release模式(Debug是测试的,Release是发布用的)

在Xcode7.x 时, 出现了一个新的东西叫做bitcode, (官网说, 可以优化我们的二进制文件, 方便以后更新, 但是旧的一些第三方SDK或者frameWork不支持这个bitcode, 可能导致你上传代码失败. 看解决办法:

1. 去找这些第三方SDK或者frameWork要支持bitcode的版本(不太可能实现)

2. 让工程不支持bitcode, 如何设置呢. 看下图

然后可以根据需要设置本地语言和App在手机中显示的名字

然后选择 Xcode下 Product 下 Archive(专门用于传项目,或者打包项目)

出现下图说明你没有添加开发者账号,点击右下脚Add… 按钮就可以添加了

输入那个花钱买的开发者账号

然后可能会弹出这个界面,如果不弹出按command 加 ,

然后回到那个Archive(选择账号,就是我们交过钱的)然后点击Choose

然后等待

下面选择Upload 提交

如下就代表上传成功,如果出错了请参考:http://www.cnblogs.com/lidongxu/p/5143453.html

然后回到Itunesconnect网站上你自己的App信息中查看一下

然后这个构建版本这里就可以添加代码啦

点击+号之后选择代码版本

然后所有东西都填写好了之后,点击页面右上角提交以供审核,然后你的应用就进入到排队等待审核的状态了(大概等1周多吧就能有结果,祝你好运)

然后我的App已经从准备提交,变成正在等待审核啦

[iOS]如何把App打包成ipa文件,然后App上架流程[利用Application Loader]

打开你项目工程 第一步,这里不能选择模拟器,选择iOS Device

如果不支持横屏,把这2个勾去掉

然后查看版本号和构建版本号

然后配置发布证书

然后,如果这里没有黄色叹号,代表你的配置没问题,如果有,那就是证书和描述文件不匹配,或者描述文件里刚才选的Bundle和现在的工程的Bundle Identifier不一致,去develop.apple.com 上找到你的描述文件在确认下绑定的bundle Identifier和你工程是不是一样的?

然后继续。断点,全局断点,僵尸模式等都要去掉。

然后设置Release模式(Debug是测试的,Release是发布用的)
Debug那个也要设置成Release模式 (这里少了个图)

然后可以根据需要设置本地语言和App在手机中显示的名字

然后选择 Xcode下 Product 下 Archive(专门用于传项目,或者打包项目)

这个时候,我们选择Export导出

这里选择App Store

选择账号点击Choose

然后点击Export

然后会在桌面得到了一个ipa的文件

、然后点击Xcode,然后 打开Open Developer Tool 下的Application Loader

啊哦,还需要遵循协议哈哈,同意吧

我的ipa包在桌面(默认应该在桌面)

然后选中打开

然后回到Itunesconnect网站上你自己的App信息中查看一下

然后这个构建版本这里就可以添加代码啦

点击+号之后选择代码版本

然后所有东西都填写好了之后,点击页面右上角提交以供审核,然后你的应用就进入到排队等待审核的状态了(大概等1周多吧就能有结果,祝你好运)

然后我的App已经从准备提交,变成正在等待审核啦

IOS证书(.p12)和描述文件(.mobileprovision)申请

OS有两种证书和描述文件:

证书类型 使用场景
开发(Development)证书和描述文件 用于开发测试,在HBuilder中打包后可在真机环境通过Safari调试
发布(Distribution)证书和描述文件 用于提交Appstore,在HBuilder中打包后可使用Application Loader提交到Appstore审核发布

准备环境

  • 必需要有苹果开发者账号,并且加入了“iOS Developer Program”
  • Mac OS 10.9以上系统(如果已经申请p12证书则不需要)

登录iOS Dev Center

打开网站iOS Dev Center
使用苹果开发者账号登录iOS Dev Center:

登录成功后在页面右侧选择“Certificates, Identifiers & Profiles”:

在“Certificates, Identifiers & Profiles”页面的“iOS Apps”栏中任选一项,如“Certificates”:

打开“iOS Certificates”页面,可以看到所有已经申请的证书及描述文件:

申请苹果App ID

(如果已经申请,可跳过此节)
在“iOS Certificates”页面“Identifiers”下选择“App IDs”,可查看到已申请的所有App ID,点击右上角的加号可创建新“App ID”:

打开“Register iOS App ID”页面,在“App ID Description”栏下的“Name”项中输入名称(如“HBuilderApp”):

在“Explicit App ID”栏下的“Bundle ID”项中输入App ID(使用反向域名格式字符串,如“io.dcloud.HBuilderApp”):
注意:HBuilder中App云端打包界面的AppID栏需要输入此值

在“App Services”栏下选择应用要使用到的服务(如需要使用到消息推送功能,则选择“Push Notifications”):

设置完成后选择“Continue”,弹出确认页面:

确认后选择“Submit”提交,再次确认就可以在“App IDs”页面看到刚创建的App ID:

生成证书请求文件

不管是申请开发(Development)证书还是发布(Distribution)证书,都需要使用证书请求(.certSigningRequest)文件,证书请求文件需在Mac OS上使用“Keychain Access”工具生成。
在“Spltlight Search”中搜索“Keychain”并打开“Keychain Access”工具:

打开菜单“Keychain Access”->“Certificate Assistant”,选择“Request a Certificate From a Certificate Authority…”:

打开创建请求证书页面,在页面中输入用户邮件地址(User Email Address)、证书名称(Common Name),请求类型(Request is)选择保存到磁盘(Saved to disk):

点击“Continue”后弹出保存路径:

点击“Save”后保存证书请求文件,成功提示:

保存文件名称为“CertificateSigningRequest.certSigningRequest”,后面申请开发(Development)证书和发布(Production)证书时用到。

申请开发(Development)证书和描述文件

开发(Development)证书用于测试环境下使用,可以直接安装到手机上(不用提交到Appstore),但一个描述文件最多只能绑定100台设备(因此通过这种证书正式发布应用是行不通的)。

申请开发(Development)证书

在“Certificates, Identifiers & Profiles”页面“Certificates”下选择“Development”,可查看到已申请的所有开发(Development)证书,点击右上角的加号可创建新证书:

打开“Add iOS Certificate”页面,在“Development”栏下选中“iOS App Development”:

在页面底部选择“Continue”:

打开确认证书请求页面,点击“Continue”继续:

打开证书生成页面,点击“Choose File…”选择“生成证书请求文件”章节生成的“CertificateSigningRequest.certSigningRequest”文件,点击“Generate”生成cer证书成功:

生成证书成功后打开证书下载页面,选择“Download”下载保存证书(ios_development.cer):

双击保存到本地的ios_development.cer文件导入到“Keychain Access”:

导入成功后,可以在证书列表中显示:

选中导入的证书,右键选择“Export “Developer” …”:

打开证书保存页面,输入文件名、选择路径后点击“Save”:

打开设置证书密码页面,输入密码及确认密码后点击“OK”:

打开访问“Keychain Access”密码页面,输入Mac OS管理员密码,点击“Allow”,保存开发(Development)证书(如“HBuilderCert.p12”)。

添加调试设备

开发描述文件必须绑定设备,所以在申请开发描述文件之前,先添加调试的设备。
(如果已经添加设备,可跳过此节)
在“Certificates, Identifiers & Profiles”页面“Devices”下选择“All”,可查看到已添加的所有设备信息,点击右上角的加号可添加新设备:

打开“Registering a New Device or Multiple Devices”页面,输入设备名称和UDID:


将设备连接到电脑,启动iTunes,点击次区域可切换显示设备的UDID,右键选择复制

输入完成后,点击“Continue”继续,确认输入信息:

点击“Register”,完成添加。

申请开发(Development)描述文件

在“Certificates, Identifiers & Profiles”页面“Provisioning Profiles”下选择“Development”,可查看到已申请的所有开发(Development)描述文件,点击右上角的加号可创建新描述文件:

打开“Add iOS Provisioning Profile”页面,在“Development”栏下选中“iOS App Development”:

点击“Continue”按钮,打开“App ID”选择页面,选择要使用的“App ID”(如之前创建的“io.dcloud.HBuildApp”),点击“Continue”:

打开“Select certificates”页面,选择前面创建的开发证书:

点击“Continue”,打开选择调试设备页面,选择用于调试安装的设备(最多100太设备):

点击“Continue”,输入描述文件的名称(如“HBuilderProfile”):

点击“Generage”,生成描述文件成功:

点击“Download”下载保存开发描述文件(如HBuilderProfile.mobileprovision)。

申请发布(Distribution)证书和描述文件

发布(Production)证书用于正式发布环境下使用,用于提交到Appstore审核发布。

申请发布(Production)证书

在“Certificates, Identifiers & Profiles”页面“Certificates”Production”,可查看到已申请的所有发布(Production)证书,点击右上角的加号可创建新证书:

打开“Add iOS Certificate”页面,在“Production”栏下选中“App Store and Ad Hoc”:

在页面底部选择“Continue”:

打开确认证书请求页面,点击“Continue”继续:

打开证书生成页面,先点击“Choose File…”选择“生成证书请求文件”章节生成的“CertificateSigningRequest.certSigningRequest”文件,再点击“Generate”生成cer证书:

生成证书成功后打开证书下载页面,选择“Download”下载保存证书(ios_production.cer):

双击保存到本地的ios_production.cer文件导入到“Keychain Access”:

导入成功后,可以在证书列表中显示:

选中导入的证书,右键选择“Export “Developer” …”:

打开证书保存页面,输入文件名、选择路径后点击“Save”:

打开设置证书密码页面,输入密码及确认密码后点击“OK”:

打开访问“Keychain Access”密码页面,输入Mac OS管理员密码,点击“Allow”,保存开发(Production)证书(如“HBuilderCert.p12”)。

申请发布(Distribution)描述文件

在“Certificates, Identifiers & Profiles”页面“Provisioning Profiles”下选择“Distribution”,可查看到已申请的所有发布(Distribution)描述文件,点击右上角的加号可创建新描述文件:

打开“Add iOS Provisioning Profile”页面,在“Development”栏下选中“iOS App Development”:

点击“Continue”按钮,打开“App ID”选择页面,选择要使用的“App ID”(如之前创建的“io.dcloud.HBuildApp”),点击“Continue”:

打开“Select certificates”页面,选择前面创建的发布证书:

点击“Continue”,输入描述文件的名称(如“HBuilderProfileDistribution”):

点击“Generage”,生成描述文件成功:

点击“Download”下载保存开发描述文件(如HBuilderProfileDistribution.mobileprovision)。

gradle 编译ios的一些问题

Initialize mono
Mono path[0] = '/Applications/Unity/Unity.app/Contents/Managed'
Mono path[1] = '/Applications/Unity/Unity.app/Contents/Mono/lib/mono/2.0'
Mono config path = '/Applications/UStacktrace:


Native stacktrace:

 0 libsystem_kernel.dylib 0x00007fffba5a2dd6 __pthread_kill + 10
 1 libsystem_c.dylib 0x00007fffba508420 abort + 129
 2 HIServices 0x00007fffa3c222de ApplicationTypeGet + 0
 3 HIServices 0x00007fffa3c20136 GetCurrentProcess + 24
 4 HIToolbox 0x00007fffa4483fa3 _ZN15MenuBarInstance18GetAggregateUIModeEPjS0_ + 63
 5 HIToolbox 0x00007fffa4483f33 _ZN15MenuBarInstance9IsVisibleEv + 41
 6 AppKit 0x00007fffa2a16d43 _NSInitializeAppContext + 35
 7 AppKit 0x00007fffa2a16105 -[NSApplication init] + 603
 8 AppKit 0x00007fffa2a15cca +[NSApplication sharedApplication] + 142
 9 AppKit 0x00007fffa2a174db NSApplicationMain + 352
 10 Unity 0x0000000102a01c6a _Z10EditorMainiPPKc + 2010
 11 Unity 0x0000000102a023f9 main + 9
 12 Unity 0x0000000100002504 start + 52

Debug info from gdb:

出现这种诡异的东西..不要担心..可能mono正在使用..比如vscode正在更新mono插件

 

FAILED TO establish the default connection to the WindowServer, _CGSDefaultConnection() is NULL

Jenkins by default installs as a LaunchDaemon, which means it has insufficient permissions to launch WindowsServer.

You’ll need to configure it as a LaunchAgent:

  1. sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist
  2. sudo mv /Library/LaunchDaemons/org.jenkins-ci.plist /Library/LaunchAgents/org.jenkins-ci.plist

Then login as Jenkins and keep a session open.

If you don’t know the Jenkins password you can change it with:

  1. sudo passwd jenkins

 

 

 

What went wrong:
Execution failed for task ‘:app:xcodebuild’.
> java.io.IOException: Cannot run program “/Library/Developer/CommandLineTools/Contents/Developer/usr/bin/simctl” (in dir
No such file or directory
I solved this problem by entering

Xcode > Preferences > Locations 

And assigning the Command Line Tools

iOS开发内购全套图文教程

2015年最全的内购图文教程,首先是填各种资料,最后是代码,废话不多说,直接上图

如对你有帮助,请不要吝惜打赏和喜欢,谢谢!

======================第一部分协议===============

第一步.png

第二步.jpg

第三步.jpg

第四步.png

第五步.png

第六步.png

第七步.jpg

第八步.jpg

第九步.jpg

第十步.png

CNAPS CODE 查询地址
https://e.czbank.com/CORPORBANK/query_unionBank_index.jsp

十一步.jpg

十二步.jpg

十三步.png

十四步.png

十五步.jpg

十七步.jpg

十八步.jpg

十九步.png

2016/11/18号更新

十九步(1).png

十九步(2).png

没显示的都是可以不填的,主要是打钩 title 和最后面的Self

二十步.png

=============第二部分创建内购项目============

1.png

2.png

3.png

4.png

5.png

6.png

7.png

===========第三部分贴加内购项目测试账号==========

创建测试账号.png

沙盒测试员.png

账号信息.png

=============第四部分主要实现代码==========

首先导入StoreKit.framework

.h文件

#import <StoreKit/StoreKit.h>

enum{
IAP0p20=20,
IAP1p100,
IAP4p600,
IAP9p1000,
IAP24p6000,
}buyCoinsTag;

//代理
@interface RechargeVC : UIViewController <SKPaymentTransactionObserver,SKProductsRequestDelegate >

{
int buyType;
}

- (void) requestProUpgradeProductData;

-(void)RequestProductData;

-(void)buy:(int)type;

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;

-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction;

- (void) completeTransaction: (SKPaymentTransaction *)transaction;

- (void) failedTransaction: (SKPaymentTransaction *)transaction;

-(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction;

-(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error;

- (void) restoreTransaction: (SKPaymentTransaction *)transaction;

-(void)provideContent:(NSString *)product;

-(void)recordTransaction:(NSString *)product;

@end

.m文件

#import "RechargeVC.h"

//在内购项目中创的商品单号
#define ProductID_IAP0p20 @"Nada.JPYF01"//20
#define ProductID_IAP1p100 @"Nada.JPYF02" //100
#define ProductID_IAP4p600 @"Nada.JPYF03" //600
#define ProductID_IAP9p1000 @"Nada.JPYF04" //1000
#define ProductID_IAP24p6000 @"Nada.JPYF05" //6000

@interface RechargeVC ()

@end

@implementation RechargeVC

- (void)viewDidLoad {

    [super viewDidLoad];

    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    [self buy:IAP0p20];

}

-(void)buy:(int)type
{
    buyType = type;
    if ([SKPaymentQueue canMakePayments]) {
    [self RequestProductData];
    NSLog(@"允许程序内付费购买");
}
else
{
    NSLog(@"不允许程序内付费购买");
    UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@"提示"
    message:@"您的手机没有打开程序内付费购买"
    delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];

    [alerView show];

    }
}

-(void)RequestProductData
{
    NSLog(@"---------请求对应的产品信息------------");
    NSArray *product = nil;
    switch (buyType) {
        case IAP0p20:
        product=[[NSArray alloc] initWithObjects:ProductID_IAP0p20,nil];
        break;
        case IAP1p100:
        product=[[NSArray alloc] initWithObjects:ProductID_IAP1p100,nil];
        break;
        case IAP4p600:
        product=[[NSArray alloc] initWithObjects:ProductID_IAP4p600,nil];
        break;
        case IAP9p1000:
        product=[[NSArray alloc] initWithObjects:ProductID_IAP9p1000,nil];
        break;
        case IAP24p6000:
        product=[[NSArray alloc] initWithObjects:ProductID_IAP24p6000,nil];
        break;

        default:
        break;
}
    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];
    request.delegate=self;
    [request start];

}

// 请求协议
//收到的产品信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{

    NSLog(@"-----------收到产品反馈信息--------------");
    NSArray *myProduct = response.products;
    NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);
    NSLog(@"产品付费数量: %d", (int)[myProduct count]);
    // populate UI
    for(SKProduct *product in myProduct){
    NSLog(@"product info");
    NSLog(@"SKProduct 描述信息%@", [product description]);
    NSLog(@"产品标题 %@" , product.localizedTitle);
    NSLog(@"产品描述信息: %@" , product.localizedDescription);
    NSLog(@"价格: %@" , product.price);
    NSLog(@"Product id: %@" , product.productIdentifier);
}
    SKPayment *payment = nil;
    switch (buyType) {
        case IAP0p20:
        payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP0p20];    //支付25
        break;
        case IAP1p100:
        payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP1p100];    //支付108
        break;
        case IAP4p600:
        payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP4p600];    //支付618
        break;
        case IAP9p1000:
        payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP9p1000];    //支付1048
        break;
        case IAP24p6000:
        payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP24p6000];    //支付5898
        break;
        default:
        break;
}
    NSLog(@"---------发送购买请求------------");
    [[SKPaymentQueue defaultQueue] addPayment:payment];

}
- (void)requestProUpgradeProductData
{
    NSLog(@"------请求升级数据---------");
    NSSet *productIdentifiers = [NSSet setWithObject:@"com.productid"];
    SKProductsRequest* productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
    productsRequest.delegate = self;
    [productsRequest start];

}
//弹出错误信息
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    NSLog(@"-------弹出错误信息----------");
    UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL) message:[error localizedDescription]
    delegate:nil cancelButtonTitle:NSLocalizedString(@"Close",nil) otherButtonTitles:nil];
    [alerView show];

}

-(void) requestDidFinish:(SKRequest *)request
{
    NSLog(@"----------反馈信息结束--------------");

}

-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction{
    NSLog(@"-----PurchasedTransaction----");
    NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];
    [self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];
}

// 千万不要忘记绑定,代码如下:
//----监听购买结果
//[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions//交易结果
{
    NSLog(@"-----paymentQueue--------");
    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:{//交易完成
            [self completeTransaction:transaction];
            NSLog(@"-----交易完成 --------");

            UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@""
            message:@"购买成功"
            delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];

            [alerView show];

        } break;
        case SKPaymentTransactionStateFailed://交易失败
            { [self failedTransaction:transaction];
            NSLog(@"-----交易失败 --------");
            UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"提示"
            message:@"购买失败,请重新尝试购买"
            delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];

            [alerView2 show];

        }break;
        case SKPaymentTransactionStateRestored://已经购买过该商品
            [self restoreTransaction:transaction];
            NSLog(@"-----已经购买过该商品 --------");
            case SKPaymentTransactionStatePurchasing:      //商品添加进列表
            NSLog(@"-----商品添加进列表 --------");
            break;
            default:
            break;
        }
    }
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction

{
    NSLog(@"-----completeTransaction--------");
    // Your application should implement these two methods.
    NSString *product = transaction.payment.productIdentifier;
    if ([product length] > 0) {

    NSArray *tt = [product componentsSeparatedByString:@"."];
    NSString *bookid = [tt lastObject];
    if ([bookid length] > 0) {
    [self recordTransaction:bookid];
    [self provideContent:bookid];
}
}

    // Remove the transaction from the payment queue.

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];

}

//记录交易
-(void)recordTransaction:(NSString *)product{
    NSLog(@"-----记录交易--------");
}

//处理下载内容
-(void)provideContent:(NSString *)product{
    NSLog(@"-----下载--------");
}

- (void) failedTransaction: (SKPaymentTransaction *)transaction{
    NSLog(@"失败");
    if (transaction.error.code != SKErrorPaymentCancelled)
    {

    }
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];

}
-(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{

}

- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
    NSLog(@" 交易恢复处理");

}

-(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{
    NSLog(@"-------paymentQueue----");
}

#pragma mark connection delegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSLog(@"%@",  [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{

}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    switch([(NSHTTPURLResponse *)response statusCode]) {
        case 200:
        case 206:
        break;
        case 304:
        break;
        case 400:
        break;
        case 404:
        break;
        case 416:
        break;
        case 403:
        break;
        case 401:
        case 500:
        break;
        default:
        break;
    }
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"test");
}

-(void)dealloc
{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];//解除监听

}

@end

2016/11/22更新

内购项目不再是单独提交审核了,只要app提交审核内购项目自然跟着改变状态,不消耗物品请不要选择 托管那一项,不然会出现问题

更多经验请点击
技术交流群:534926022(免费) 511040024(0.8/人付费)

如对你有帮助,请不要吝惜你的star和喜欢哦!

推荐一款学习iOS开发的app_____|__| | 传送门
好文推荐:Bison教你3分钟集成支付宝好友分享

 

iOS应用跳转至app store 评分页

[[UIApplicationsharedApplication]openURL:[NSURLURLWithString:@”http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APPID&pageNumber=0&sortOrdering=2&type=Purple+Software&mt=8″]];

参考地址1:
http://stackoverflow.com/questions/18905686/itunes-review-url-and-ios-7-ask-user-to-rate-our-app-appstore-show-a-blank-pag/18907231#18907231
参考地址2:
http://forums.coronalabs.com/topic/38789-ios7-fails-to-launch-app-store-app-review-page/