显然,在iOS开发中拥有Mac是最好的选择,但如果您想在iOS上试用您的应用程序,并且只拥有Windows机器,那么这篇文章将向您展示一种实现方法。
本指南将使用Azure Devops的免费版本来构建我们的iOS项目,然后将其部署到我们的iPhone。
您需要注册 Apple Developer Program。
注册后,下一步是创建开发证书。
+
添加新证书。现在苹果需要一个CSR,为此,我们需要一些Windows工具。
下载并安装 openssl 1.X(注意:openssl不提供二进制文件,因此您需要访问该页面上列出的第三方网站之一。)
生成私钥
openssl genrsa -out mykey.key 2048
生成CSR
openssl req -new -key mykey.key -out CertificateSigning.csr -subj "/[email protected], CN=Jason Cassidy, C=IE"
将CSR上传到苹果并下载生成的证书
将证书复制到我们生成CSR和密钥的同一文件夹。
创建.p12(组合私钥和证书)
转换为PEM格式
openssl x509 -in development.cer -inform DER -out development.pem -outform PEM
生成.p12
openssl pkcs12 -export -inkey mykey.key -in development.pem -out development.p12 -legacy
这将要求您输入密码,请记住它,因为我们稍后需要它。
登录您的Apple开发者帐户
选择证书、ID和配置文件
选择设备
,然后点击+
图标
输入设备名称
您现在需要获取您的设备ID,在将您的iPhone连接到PC后运行以下命令。(这可能只在您安装了iTunes的情况下才有效)
ns device
为您的手机列出的设备标识符
是设备ID(UDID),您应该输入它。
在接下来的屏幕中点击继续
。
证书、ID和配置文件
标识符
,然后点击+
图标App ID
,然后点击继续
App
,然后点击继续
描述
和显式捆绑包ID
,例如 my.company.testApp继续
注册
证书、ID和配置文件
配置文件
,然后点击+
图标iOS App Development
,然后点击继续
App ID
(例如 my.company.testApp),然后点击继续
继续
生成
创建一个您选择的类型的示例NativeScript应用程序。
例如:
ns create
编辑nativescript.config.ts
,将id更改为之前创建的显式捆绑包ID
例如:
...
id: 'my.company.testApp',
...
Azure使用yaml文件来控制其构建过程,目前我们将在这里创建一个最小的文件作为占位符
在builds文件夹中创建一个名为build.yaml
的新文件,即builds\build.yaml
,其中包含
trigger:
- main
name: '1.1.$(DayOfYear)$(rev:rr)'
#variables:
# - group: AzureDemoVariables
jobs:
- job: BuildApple
pool:
vmImage: macOS-latest
steps:
- checkout: self
clean: true
path: "TestApp"
- script: |
echo Saying Hello
displayName: 'Saying Hello'
这里我们指示azure在mac上运行我们的作业,并使用一个简单的脚本来说hello。
将此项目检入GitHub
如果您还不是Microsoft Azure Devops用户,可以从 Azure DevOps 设置免费帐户。
为此,我们使用了GitHub凭据。
您将被要求首先创建一个组织
,然后它将包含项目。
然后您将被要求创建您的第一个项目
,它将包含对您的GitHub项目的引用,并将构建您的代码。
Azure提供自己的git仓库、wiki、问题管理等,但在这个例子中,我们只使用Pipeline
功能来构建我们的GitHub仓库。
不幸的是,对于免费帐户,您需要请求在azure上构建的能力,您可以在 这里 阅读相关信息,它归结为需要填写这个 表格
Pipelines
,然后点击Create Pipeline
GitHub
GitHub App
配置您的pipeline
时,选择Existing Azure Pipelines YAML file
Path
下拉菜单中选择之前创建的yaml文件,/builds/build.yaml
继续
Run
旁边的向下箭头,然后点击Save
Run Pipeline
No hosted parallelism has been purchased or granted.
,直到您的请求被批准。将以下行添加到builds\build.yaml
- task: NodeTool@0
inputs:
versionSpec: '14.x'
- script: |
python -m pip install six
displayName: 'Install six'
- script: |
npm i -g nativescript
displayName: 'Install nativescript'
- script: |
npm --version
node --version
ruby --version
gem query --local
echo **************************************
pip --version
pip show six
displayName: 'Show NPM version'
- script: |
ns --version --json
displayName: 'Show nativescript version'
- script: |
ns doctor
displayName: 'Run ns doctor'
这里我们安装了NativeScript依赖项,这些依赖项还没有安装在Microsoft提供的镜像中。
最后,我们运行ns doctor
来检查一切是否正常。
将其检入,作业应该触发并运行(如果不行,您可以手动运行)
我们有3个Secrets,它们必须在mac上可用才能构建我们的应用程序。
我们将它们存储在Pipeline
Library
中
Pipelines
-> Library
Secure Files
+Secure file
development.p12
,然后点击OK
上传。Profile
(.mobilprofile
)。现在我们添加一个变量组,用于保存密码和指向这些文件的指针。
Pipelines
-> Library
Variable groups
标签,然后点击+ Variable Group
AzureDemoVariables
P12password
的新变量,并将值设置为生成的development.p12
的密码现在我们将添加两个变量,它们保存我们之前添加的secret文件的id。
添加一个名为apple_cert_sercure_file_id
的新变量,我们稍后将设置其值
添加一个名为apple_provisioning_profile_file_id
的新变量,我们稍后将设置其值
在您的azure项目中,导航到Pipelines
-> Library
点击Secure Files
点击development.p12链接
在浏览器url字段中,有一个属性secureField,例如secureFileId=abce9b6a-2470-4258-b6dc-48c26e026c3b
,这是secret文件的id,在本例中为abce9b6a-2470-4258-b6dc-48c26e026c3b
。
将此id复制到我们创建的变量apple_cert_sercure_file_id
的值字段
重复此操作,为配置文件设置apple_provisioning_profile_file_id
变量的值为其secureFileId
这种获取这些id的方式似乎很疯狂,但我只找到了这种方法
现在我们已经完成了变量组,我们可以将它们安装到构建机器上。
取消变量部分的注释,即它应该读作
variables:
- group: AzureDemoVariables
将以下行添加到builds\build.yaml
的末尾
- task: InstallAppleCertificate@2
displayName: 'Install an Apple certificate'
inputs:
certSecureFile: '$(apple_cert_sercure_file_id)'
certPwd: '$(P12password)'
- task: InstallAppleProvisioningProfile@1
displayName: 'Install an Apple provisioning profile'
inputs:
provProfileSecureFile: '$(apple_provisioning_profile_file_id)'
这将证书和配置文件安装到构建机器上,并在作业完成后将其删除。
This pipeline needs permission to access 3 resources before this run can continue
进行批准,请点击View
并授予批准。builds\build.yaml
的末尾 - script: |
ns build ios --bundle --for-device
displayName: 'Run ns to build'
- task: CopyFiles@2
displayName: 'Copy Files to: $(build.artifactstagingdirectory)'
inputs:
SourceFolder: '$(system.defaultworkingdirectory)'
Contents: |
**/*.ipa
TargetFolder: '$(build.artifactstagingdirectory)'
flattenFolders: true
preserveTimestamp: true
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: drop'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
这里我们正在构建应用程序,然后将应用程序发布到pipeline工件,这使ipa可供下载。
1 published
,点击它将带您到该构建运行的工件页面。我们将使用OTA(over the wire)分发来将应用程序安装到iPhone,在这里,我们将应用程序托管在我们自己的网站上,并从该网站下载到手机。
这有一些要求
如果您可以使用权威机构的证书(例如Let's Encrypt),那么您可以使用这些证书,并使用这些证书从网站上提供应用程序,但在这里,我们将从头开始使用我们自己的证书。
我们将再次使用openssl创建一个根CA证书,然后使用它来生成一个证书,该证书将用于我们的网站,该网站将把ipa提供给我们的手机。
这要求我们的手机能够与我们的PC通信,并且我们的PC防火墙上必须打开一个端口(8888)。
首先我们需要我们的PC的ip地址,发出命令ipconfig
,这将输出各种连接,您需要的是LAN上可访问的连接。(通常是192.168.X.X)。
在项目中创建一个名为serve/certs
的文件夹
在该文件夹中创建一个名为'thesite.ext'的文件,其内容为
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = thesite.com
IP.1 = 192.168.178.22
将DNS.1的值替换为您的机器FQDN,将IP.1的值替换为您的机器IP地址
在serve/certs
文件夹中创建一个名为createcerts.bat
的批处理文件,其内容如下
rem create the root CA Key
openssl genrsa -des3 -out myCA.key -passout pass:capassword 2048
rem create the root CA cert
openssl req -x509 -new -nodes -key myCA.key -sha256 -days 1825 -out myCA.pem -passin pass:capassword -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=CA.example.com"
rem create private key for site
openssl genrsa -out thesite.key -passout pass:sitepassword 2048
rem create CSR for site
openssl req -new -key thesite.key -out thesite.csr -passin pass:sitepassword -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=site.example.com"
rem create cert for site that serves the ipa
openssl x509 -req -in thesite.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out thesite.crt -days 825 -sha256 -extfile thesite.ext -passin pass:capassword
这里我们正在创建一个根CA密钥和证书,然后是一个网站密钥和证书,您可以编辑密码/详细信息,但它们将在演示目的上起作用。
在serve/certs
目录中运行批处理文件。
现在您将拥有从PC上的网站到手机上提供应用程序所需的证书。
将myCA.pem发送到您可以访问的iPhone帐户的电子邮件。
从手机上的电子邮件中,点击证书并安装配置文件。
在手机上进入设置
,在顶部附近应该有一个新的选项已下载的配置文件
,点击进入它。
点击安装并输入您的密码。
点击安装确认。
导航到设置 -> 通用 -> 关于 -> 证书信任设置
您应该在 启用根证书的完全信任
部分看到 CA.example.com
的条目,将开关切换到开启并点击继续。
您的手机现在将信任来自您新生成的根 CA 的证书。
因此,您应该保管好这些证书,不要将它们检入公共仓库,并在上面的命令中使用您自己的密码。
serve
文件夹中创建一个名为 download.plist
的文件。这是将最初发送到您手机的文件,告诉手机从哪里获取您的应用程序。<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>https://mysite</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>my.app.id</string>
<key>bundle-version</key>
<string>1.0.0</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>MyApp</string>
</dict>
</dict>
</array>
</dict>
</plist>
现在我们将创建一个 node https 服务器来服务 ipa。
创建一个名为 serve\serve.js
的文件。
添加内容
var http = require('https');
var fs = require('fs');
const myArgs = process.argv.slice(2);
var ipaddress = myArgs[0];
const port = 8888;
const options = {
key: fs.readFileSync('./serve/certs/thesite.key', 'utf8'),
cert: fs.readFileSync('./serve/certs/thesite.crt', 'utf8'),
ca: fs.readFileSync('./serve/certs/myCA.pem')
};
console.log(options.key);
http.createServer(options, function (request, response) {
console.log('request starting...');
var filePath;
var contentType;
console.log(request.url);
if(request.url === '/ipa'){
filePath = './serve/ipa/TestApp.ipa';
contentType = 'application/octet-stream';
}else if (request.url === '/plist') {
contentType = 'application/x-plist';
filePath = './serve/download.plist';
}
if(filePath){
fs.readFile(filePath, function(error, content) {
if (error) {
response.writeHead(500);
response.end('An Error Occured: '+error.code+' ..\n');
response.end();
}
else {
var contentToWrite;
if(contentType == 'application/x-plist'){
contentToWrite=content.toString().replace("mysite", ipaddress+':'+port+'/ipa');
} else {
contentToWrite = content;
}
response.writeHead(200, { 'Content-Type': contentType });
response.end(contentToWrite);
}
}
)
} else {
response.writeHead(200, { 'Content-Type': 'text/html' });
response.end("hello Click the link to download an app<br><a href='itms-services://?action=download-manifest&url=https://"+ ipaddress +":"+port+"/plist'>Download </a>");
}
}).listen(port);
这将创建一个 https 服务器,它将使用我们生成的证书并将我们的 IPA 传输到手机。
将您下载的 IPA 放置到 serve/ipa/TestApp.ipa
。
从项目的根目录运行 https 服务器。
node serve\serve.js <Your Ip Address>
从您的手机上,浏览到 https://<您的 IP 地址>:8888
。
点击下载链接。
系统会提示您安装应用程序。
当您的手机连接到 PC 时,您可以获得来自 iPhone 的日志输出。
在您的手机连接的情况下,再次发出上面使用的命令。
ns device
如果显示设备已连接,您可以运行
ns device log > LogOutput.txt
这将输出很多信息,其中大部分您不需要,但您将看到应用程序中的 控制台日志
消息,以及其他错误等。
示例输出
Jan 18 17:52:14 jason-iphone SpringBoard(RunningBoardServices)[50750] : Received state update for 53575
Jan 18 17:52:14 jason-iphone SpringBoard(RunningBoardServices)[50750] : Received state update for 53533
Jan 18 17:52:14 jason-iphone TestApp(NativeScript)[53575] : CONSOLE LOG: Angular is running in development mode. Call enableProdMode() to enable production mode.
Jan 18 17:52:14 jason-iphone kernel[0] : 1609773.409 memorystatus: killing_idle_process pid 53361 [com.apple.WebKit.WebContent] (sustained-memory-pressure 0) 5216KB - memorystatus_available_pages: 66771 compressor_size:120141
Jan 18 17:52:14 jason-iphone UserEventAgent(MemoryMonitor)[50714] : kernel jetsam snapshot note received
在 Yaml 中,空格很重要。一个示例项目可在此处 获取,其中包含一个使用上述内容的项目。
如果您已经走到这一步,那么很明显,如果您一直在不断地更改您的应用程序,上面的方法并不适合,因为周转时间太长。但是,云构建至少可以让您在没有 Mac 支出情况下,尝试 iOS 开发。