PageSourceSearch

https://mushank.github.io/post/iOS%E5%BC%80%E5%8F%91-App-Store-Con…API-%E8%B0%83%E7%A0%94%E6%8A%A5%E5%91%8A.md

html mushank.github.io collected 2026-10-03 09:38:56 UTC 44,967 bytes, 1,034 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5    <meta charset="utf-8">
6    <meta http-equiv="X-UA-Compatible" content="IE=edge">
7    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
8    <meta name="viewport" content="width=device-width, initial-scale=1">
9    <meta name="description" content="关于移动开发与设计、生活与艺术 | Mushank, Mobile Dev & Design, Life & Art | 这里是 @Jack 的个人博客,与你一起发现更大的世界。">
10    <meta name="keyword"  content="Jack, MUSHANK, BLOG">
11    <link rel="shortcut icon" href="/img/favicon.ico">
12
13    <title>iOS开发|App Store Connect API 调研报告 - MUSHANK (Jack)</title>
14
15    <link rel="canonical" href="http://127.0.0.1:4000/post/iOS%E5%BC%80%E5%8F%91-App-Store-Connect-API-%E8%B0%83%E7%A0%94%E6%8A%A5%E5%91%8A.md">
16
17    <!-- Bootstrap Core CSS -->
18    <link rel="stylesheet" href="/css/bootstrap.min.css">
19
20    <!-- Custom CSS -->
21    <link rel="stylesheet" href="/css/hux-blog.min.css">
22
23    <!-- Pygments Github CSS -->
24    <link rel="stylesheet" href="/css/syntax.css">
25
26    <!-- Custom Fonts -->
27    <!-- <link href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css"> -->
28    <!-- Hux change font-awesome CDN to qiniu -->
29    <link href="http://cdn.staticfile.org/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
30
31
32    <!-- Hux Delete, sad but pending in China
33    <link href='http://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
34    <link href='http://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/
35    css'>
36    -->
37
38
39    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
40    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
41    <!--[if lt IE 9]>
42        
42<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
42
43        
43<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
43
44    <![endif]-->
45
46    <!-- ga & ba script hoook -->
47    
47<script></script>
47
48</head>
49
50
51<!-- hack iOS CSS :active style -->
52<body ontouchstart="">
53
54    <!-- Navigation -->
55<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
56    <div class="container-fluid">
57        <!-- Brand and toggle get grouped for better mobile display -->
58        <div class="navbar-header page-scroll">
59            <button type="button" class="navbar-toggle">
60                <span class="sr-only">Toggle navigation</span>
61                <span class="icon-bar"></span>
62                <span class="icon-bar"></span>
63                <span class="icon-bar"></span>
64            </button>
65            <a class="navbar-brand" href="/">MUSHANK</a>
66        </div>
67
68        <!-- Collect the nav links, forms, and other content for toggling -->
69        <!-- Known Issue, found by Hux:
70            <nav>'s height woule be hold on by its content.
71            so, when navbar scale out, the <nav> will cover tags.
72            also mask any touch event of tags, unfortunately.
73        -->
74        <div id="huxblog_navbar">
75            <div class="navbar-collapse">
76                <ul class="nav navbar-nav navbar-right">
77                    <li>
78                        <a href="/">Home</a>
79                    </li>
80                    
81                    <li>
82                        <a href="/about">About</a>
83                    </li>
84                    
85                    <li>
86                        <a href="/tags">Tags</a>
87                    </li>
88                    
89                </ul>
90            </div>
91        </div>
92        <!-- /.navbar-collapse -->
93    </div>
94    <!-- /.container -->
95</nav>
96<script>
97    // Drop Bootstarp low-performance Navbar
98    // Use customize navbar with high-quality material design animation
99    // in high-perf jank-free CSS3 implementation
100    var $body   = document.body;
101    var $toggle = document.querySelector('.navbar-toggle');
102    var $navbar = document.querySelector('#huxblog_navbar');
103    var $collapse = document.querySelector('.navbar-collapse');
104
105    $toggle.addEventListener('click', handleMagic)
106    function handleMagic(e){
107        if ($navbar.className.indexOf('in') > 0) {
108        // CLOSE
109            $navbar.className = " ";
110            // wait until animation end.
111            setTimeout(function(){
112                // prevent frequently toggle
113                if($navbar.className.indexOf('in') < 0) {
114                    $collapse.style.height = "0px"
115                }
116            },400)
117        }else{
118        // OPEN
119            $collapse.style.height = "auto"
120            $navbar.className += " in";
121        }
122    }
123</script>
123
124
125
126    <!-- Post Header -->
127<style type="text/css">
128    header.intro-header{
129        background-image: url('/img/post-img/post-bg-ios.jpg')
130    }
131</style>
132<header class="intro-header" >
133    <div class="container">
134        <div class="row">
135            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
136                <div class="post-heading">
137                    <div class="tags">
138                        
139                        <a class="tag" href="/tags/#iOS" title="iOS">iOS</a>
140                        
141                        <a class="tag" href="/tags/#WWDC18" title="WWDC18">WWDC18</a>
142                        
143                        <a class="tag" href="/tags/#App Store Connect API" title="App Store Connect API">App Store Connect API</a>
144                        
145                    </div>
146                    <h1>iOS开发|App Store Connect API 调研报告</h1>
147                    
148                    
149                    <h2 class="subheading">自动化执行你在Apple开发者网站和App Store Connect上的任务</h2>
150                    
151                    <span class="meta">Posted by Jack on October 22, 2018</span>
152                </div>
153            </div>
154        </div>
155    </div>
156</header>
157
158<!-- Post Content -->
159<article>
160    <div class="container">
161        <div class="row">
162
163    <!-- Post Container -->
164            <div class="
165                col-lg-8 col-lg-offset-2
166                col-md-10 col-md-offset-1
167                post-container">
168
169				<h2 id="概览">概览</h2>
170
171<p>App Store Connect API是一套标准的REST接口,用于在App生命周期中构建自定义的工作流,并自动执行你在App Store Connect中的操作。这套API使用JSON Web Tokens(JWT)进行授权认证,返回统一的JSON格式响应数据,响应数据包含了指向其他相关资源的链接,你可以使用这些关系链接导航到其他相关资源。</p>
172
173<blockquote>
174  <p>重要</p>
175
176  <p>通过App Store Connect API进行的任何更改都将影响用于开发和分发的生产数据。</p>
177</blockquote>
178
179<p>API为自动化App Store Connect的以下功能提供了操作资源:</p>
180
181<ul>
182  <li>TestFlight:管理你的测试包,测试人员以及测试小组</li>
183  <li>用户可职能:添加、删除用户,调整用户权限</li>
184  <li>报告:下载销售和财务报告</li>
185</ul>
186
187<blockquote>
188  <p>在<a href="https://developer.apple.com/videos/play/wwdc2018/303/">WWDC18: Automating App Store Connect</a>中还提到可以自动化服务配置文件(Provisioning):</p>
189
190  <ul>
191    <li>生成服务配置文件(Generate provisioning profiles)</li>
192    <li>创建和撤销签名证书(Create and revoke signing cer)</li>
193    <li>管理设备与bundle ID(manager devices and bundle IDs)</li>
194  </ul>
195
196  <p>但在实际的API文档中,并未发现此部分接口能力。</p>
197</blockquote>
198
199<h2 id="接口能力">接口能力</h2>
200<blockquote>
201  <p><strong>全部接口能力已汇总到表格:</strong><a href="https://www.icloud.com/numbers/0wZO_iubg9VdZ77rr9NmS2wvg#App_Store_Connect_API_List">App Store Connect API List.numbers</a></p>
202</blockquote>
203
204<h4 id="1-授权">1. 授权</h4>
205<p>这部分说明如何使用私钥(API Key)生成JWT(JSON Web Token)为每个API请求进行授权。</p>
206
207<ul>
208  <li>为App Store Connect API创建密钥(API Key),用于生成验证请求的JWT
209    <ul>
210      <li>密钥的访问权限不能设定为仅限于某个(些)特定的应用程序(我理解是只能设置为允许访问全部App,需要实操验证)</li>
211      <li><a href="https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api">参看官方文档</a></li>
212    </ul>
213  </li>
214  <li>为API请求生成令牌(JWT)
215    <ul>
216      <li>所有的API请求头中都必须携带该令牌</li>
217      <li>WWDC中Apple官方指出:令牌20min失效,建议每18min重新生成一次</li>
218      <li><a href="https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests">参看官方文档</a></li>
219    </ul>
220  </li>
221</ul>
222
223<p><em>举例,JWT的Ruby实现:</em></p>
224
225<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">
225'base64'</span>
226<span class="nb">require</span> <span class="s1">'jwt'</span>
227
228<span class="no">ISSUER_ID</span> <span class="o">=</span> <span class="s2">""</span> <span class="c1"># 复制你的ISSUER_ID到此处</span>
229<span class="no">KEY_ID</span> <span class="o">=</span> <span class="s2">""</span> <span class="c1"># 复制你的KEY_ID到此处</span>
230<span class="n">private_key</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">PKey</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="s2">"/Users/demo/Downloads/AuthKey_#(KEY_ID).p8"</span><span class="p">))</span>
231
232<span class="n">token</span> <span class="o">=</span> <span class="no">JWT</span><span class="p">.</span><span class="nf">encode</span> <span class="p">{</span>
233    <span class="p">{</span>
234        <span class="ss">iss: </span><span class="no">ISSUER_ID</span><span class="p">,</span> <span class="c1"># found on API Keys tab</span>
235        <span class="ss">exp: </span><span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">ti_i</span> <span class="o">+</span> <span class="mi">20</span> <span class="o">*</span> <span class="mi">24</span><span class="p">,</span> <span class="c1"># up to 20 minutes in the future</span>
236        <span class="ss">aud: </span><span class="s2">"appstoreconnect-v1"</span> <span class="c1"># 常量</span>
237    <span class="p">},</span>
238    <span class="n">private_key</span><span class="p">,</span>
239    <span class="s2">"ES256"</span><span class="p">,</span>
240    <span class="n">header_fields</span><span class="o">=</span> <span class="p">{</span>
241        <span class="ss">kid: </span><span class="no">KEY_ID</span> <span class="c1"># found on API Keys tab</span>
242    <span class="p">}</span>
243<span class="p">}</span>
244
245<span class="nb">puts</span> <span class="n">token</span>
246</code></pre></div></div>
247
248<ul>
249  <li>移除密钥(API Key)
250    <ul>
251      <li>当密钥不再使用或者发生密钥泄漏时,应该立即移除对应密钥</li>
252      <li><a href="https://developer.apple.com/documentation/appstoreconnectapi/revoking_api_keys">参看官方文档</a></li>
253    </ul>
254  </li>
255</ul>
256
257<h4 id="2-测试testing--testflight">2. 测试(Testing / TestFlight)</h4>
258
259<p>这部分介绍了如何管理测试(TestFlight)资源。</p>
260
261<p>TestFlight允许我们在上架应用商店之前,将App分发给测试人员进行测试。使用API可以自动化执行在App Store Connect的TestFlight选项中进行的工作,包括邀请测试人员,管理构建版本、测试群组、TestFlight公共链接,提交编译版本进行外部测试等。</p>
262
263<h5 id="21-测试人员beta-testers">2.1 测试人员(Beta Testers)</h5>
264
265<p><code class="language-plaintext highlighter-rouge">betaTesters</code> 资源代表可以安装和测试预发布构建版本的用户。通过API你可以对测试人员进行增删改查操作,可以修改测试人员与应用程序、构建版本、测试群组之间的关联关系。</p>
266
267<p>具体接口能力如下:</p>
268
269<ul>
270  <li>创建或删除测试人员,获取某个或全部测试人员</li>
271  <li>将测试人员添加或移出一个或多个测试群组</li>
272  <li>赋予或解除测试人员对某个构建版本的测试权限</li>
273  <li>解除测试人员对一个或多个App的所有构建版本的测试权限</li>
274  <li>获取指定测试人员具有测试权限的App集合或App的资源ID集合</li>
275  <li>获取指定测试人员具有测试权限的构建版本集合或构建版本的资源ID集合</li>
276  <li>获取指定测试人员所属的测试群组集合或测试群组的资源ID集合</li>
277</ul>
278
279<h5 id="22-测试邀请beta-tester-invitations">2.2 测试邀请(Beta Tester Invitations)</h5>
280
281<p><code class="language-plaintext highlighter-rouge">betaTesterInvitations</code>资源用来向已存在的测试人员发送测试邀请邮件。</p>
282
283<p>具体接口能力如下:</p>
284
285<ul>
286  <li>向测试人员发送或重新发送测试邀请邮件
287    <ul>
288      <li>将测试人员加入测试群组或构建版本时,如果此时App已经可以测试,TestFlight会自动向测试人员发送测试邀请,此时调用该接口为重新发送测试邀请邮件</li>
289      <li>如果你关闭了自动通知,此时调用该接口为首次发送邀请邮件</li>
290    </ul>
291  </li>
292</ul>
293
294<h5 id="23-测试群组beta-groups">2.3 测试群组(Beta Groups)</h5>
295
296<p><code class="language-plaintext highlighter-rouge">betaGroups</code>资源代表一组拥有某些构建版本测试权限的测试人群。一个测试群组与一个App相关联,并且包含一个或多个构建版本。通过API你可以控制测试群组与测试人员、应用程序、构建版本之间的关联关系,并且能够控制TestFlight公共测试链接的启用状态。</p>
297
298<p>具体接口能力如下:</p>
299
300<ul>
301  <li>创建或修改或删除测试群组,获取某个或全部测试群组
302    <ul>
303      <li>创建时必须与某个App关联,可选择开启是否启用TestFlight公共
303链接</li>
304    </ul>
305  </li>
306  <li>获取指定测试群组内的应用程序信息或应用程序资源ID</li>
307  <li>添加或移除指定测试群组内的测试人员,获取指定测试群组内的测试人员集合或测试人员资源ID集合</li>
308  <li>添加或移除指定测试群组内的构建版本,获取指定测试群组内的构建版本集合或构建版本资源ID集合</li>
309</ul>
310
311<h5 id="24-应用程序apps">2.4 应用程序(Apps)</h5>
312
313<p><code class="language-plaintext highlighter-rouge">apps</code> 资源代表你正在开发或者已经可以在App Store进行发布的应用程序。⚠️:你必须使用网页版Connect或Xcode或Transporter进行构建版本的上传,API不允许直接上传App的构建版本,但API的密钥可以与Transporter共享使用。</p>
314
315<p>具体接口能力如下:</p>
316
317<ul>
318  <li>获取App Store Connect中的应用程序集合,获取指定应用程序的信息</li>
319  <li>移除测试人员对指定应用程序的测试权限</li>
320  <li>获取指定应用程序的测试群组集合或测试群组的资源ID集合</li>
321  <li>获取指定应用程序的构建版本集合或构建版本的资源ID集合</li>
322  <li>获取指定应用程序的预发布测试版本(指等待发布的测试版本,<code class="language-plaintext highlighter-rouge">PrereleaseVersion</code>)集合或预发布测试版本的资源ID集合</li>
323  <li>获取指定应用程序的测试版本审核详细信息(<code class="language-plaintext highlighter-rouge">BetaAppReviewDetail</code>)或详细信息的资源ID</li>
324  <li>获取指定应用程序的测试证书许可协议(<code class="language-plaintext highlighter-rouge">BetaLicenseAgreement</code>)或许可协议的资源ID</li>
325  <li>获取指定应用程序的本地化测试信息(<code class="language-plaintext highlighter-rouge">BetaAppLocalization</code>)集合或测试信息的资源ID集合</li>
326</ul>
327
328<h5 id="25-预发布测试版本prerelease-versions">2.5 预发布测试版本(Prerelease Versions)</h5>
329
330<p><code class="language-plaintext highlighter-rouge">preReleaseVersions</code>资源指代TestFlight中的测试用应用程序版本,并非指等待发布至应用商店的版本。</p>
331
332<p>具体接口能力如下:</p>
333
334<ul>
335  <li>获取全部应用程序的预发布测试版本集合</li>
336  <li>获取指定预发布测试版本的信息</li>
337  <li>获取指定预发布测试版本的应用程序信息或应用程序资源ID</li>
338  <li>获取指定预发布测试版本的构建版本集合或构建版本的资源ID集合</li>
339</ul>
340
341<h5 id="26-测试版应用程序本地化beta-app-localizations">2.6 测试版应用程序本地化(Beta App Localizations)</h5>
342
343<p><code class="language-plaintext highlighter-rouge">betaAppLocalization</code>资源表示一组对测试人员可见的应用程序本地化配置信息。当你在TestFlight上发布一个测试版本时,测试人员会在他们的设备上看到相关的描述、URL、隐私政策等。</p>
344
345<p>具体接口能力如下:</p>
346
347<ul>
348  <li>创建或更新或删除一个本地化配置</li>
349  <li>获取指定或全部应用程序的本地化配置</li>
350  <li>获取与指定的本地化配置相关联的应用程序或其资源ID</li>
351</ul>
352
353<h5 id="27-应用程序加密声明app-encryption-declarations">2.7 应用程序加密声明(App Encryption Declarations)</h5>
354
355<p><code class="language-plaintext highlighter-rouge">AppEncryptionDeclaration</code>资源代表应用程序在数据加密方面的使用声明,通过该资源,你还可以对构建版本进行声明指定。⚠️:属性<code class="language-plaintext highlighter-rouge">usesNonExemptEncryption</code>设置为<code class="language-plaintext highlighter-rouge">true</code>的构建版本,必须与某个加密声明向关联,否则不允许提交测试审核。</p>
356
357<p>具体接口能力如下:</p>
358
359<ul>
360  <li>获取指定或全部加密声明</li>
361  <li>从指定的加密声明获取相关联的应用程序信息或其资源ID</li>
362  <li>将指定的加密声明关联到构建版本</li>
363</ul>
364
365<h5 id="28-测试许可协议beta-license-agreements">2.8 测试许可协议(Beta License Agreements)</h5>
366
367<p><code class="language-plaintext highlighter-rouge">betaLicenseAgreements</code>资源包含了向测试用户提供的许可协议。每个应用程序都必须要有一份许可协议。</p>
368
369<p>具体接口能力如下:</p>
370
371<ul>
372  <li>获取指定或全部测试许可协议</li>
373  <li>获取指定测试许可协议下的应用程序或其资源ID</li>
374  <li>编辑指定的测试许可协议</li>
375</ul>
376
377<h5 id="28-构建版本builds">2.8 构建版本(Builds)</h5>
378
379<p><code class="language-plaintext highlighter-rouge">build</code>资源代表某个应用程序的一个构建版本。你可以使用Xcode或者Transporter上传构建版本,一旦App Store Connect处理完成,构建版本就会以<code class="language-plaintext highlighter-rouge">build</code>资源的形式出现。使用API可以对该资源进行提交测试审核、添加到测试人员或测试群组等操作。</p>
380
381<p>具体接口能力如下</p>
382
383<ul>
384  <li>
384获取指定或全部构建版本</li>
385  <li>编辑指定构建版本,使其过期或更改加密设置</li>
386  <li>获取与指定构建版本相关联的应用程序或其资源ID</li>
387  <li>获取与指定构件版本相关联的预发布测试版本或其资源ID集合</li>
388  <li>为构建版本添加加密声明</li>
389  <li>为测试群组添加或取消构建版本的测试权限</li>
390  <li>为构建版本添加或移除单独的测试人员</li>
391  <li>获取指定构建版本的全部独立测试人员集合或其资源ID集合</li>
392  <li>获取指定构建版本的测试提交审核状态或其资源ID</li>
393  <li>获取指定构建版本的详细测试信息(<code class="language-plaintext highlighter-rouge">BuildBetaDetail</code>)或其资源ID</li>
394  <li>获取指定构建版本的加密声明或其资源ID</li>
395  <li>获取指定构建版本的本地化配置或其资源ID</li>
396</ul>
397
398<h5 id="29-构建版本测试详细信息build-beta-details">2.9 构建版本测试详细信息(Build Beta Details)</h5>
399
400<p>每个构建版本都有一个<code class="language-plaintext highlighter-rouge">buildBetaDetails</code>资源,表示该构建版本特定于TestFlight的信息。</p>
401
402<p>具体接口能力如下:</p>
403
404<ul>
405  <li>获取指定或全部测试详细信息</li>
406  <li>获取与指定测试详细信息相关联的构建版本或其资源ID</li>
407  <li>编辑指定的测试详细信息</li>
408</ul>
409
410<h5 id="210-构建版本本地化beta-build-localizations">2.10 构建版本本地化(Beta Build Localizations)</h5>
411
412<p><code class="language-plaintext highlighter-rouge">betaBuildLocalizations</code>资源表示TestFlight中<code class="language-plaintext highlighter-rouge">What's New</code>文本中显示的内容。</p>
413
414<p>具体接口能力如下:</p>
415
416<ul>
417  <li>获取指定或全部本地化配置</li>
418  <li>获取与指定本地化配置相关联的构建版本信息或其资源ID</li>
419  <li>创建或删除或编辑一个本地化配置</li>
420</ul>
421
422<h5 id="211-应用程序测试审核详细信息beta-app-review-detail">2.11 应用程序测试审核详细信息(Beta App Review Detail)</h5>
423
424<p>应用程序进行外部测试之前必须经过Apple审核,<code class="language-plaintext highlighter-rouge">betaAppReviewDetails</code>资源包含了审核时需要的信息,比如demo账号、联系方式等。</p>
425
426<p>具体接口能力如下:</p>
427
428<ul>
429  <li>获取指定或全部应用程序的测试审核详细信息</li>
430  <li>获取与指定测试审核详细信息相关联的应用程序信息或其资源ID</li>
431  <li>修改指定的应用程序测试审核详细信息</li>
432</ul>
433
434<h5 id="212-应用程序测试审核提交beta-app-review-submissions">2.12 应用程序测试审核提交(Beta App Review Submissions)</h5>
435
436<p><code class="language-plaintext highlighter-rouge">betaAppReviewSubmissions</code>资源表示构建版本在通过TestFlight进行分发之前Apple进行的审核。在你的构建版本准备好提交时,创建一个<code class="language-plaintext highlighter-rouge">betaAppReviewSubmissions</code>,Apple会验证这个资源以确保其包含必要的信息,比如<code class="language-plaintext highlighter-rouge">appEncryptionDeclarations</code> <code class="language-plaintext highlighter-rouge">betaAppReviewDetails</code>等,包括提交该构建版本到审核团队。</p>
437
438<p>通过API可以查询该提交是否已被通过或者被拒绝。</p>
439
440<p>具体接口能力如下:</p>
441
442<ul>
443  <li>提交应用程序进行测试审核,以允许进行外部测试</li>
444  <li>获取指定或全部构建版本的测试审核提交</li>
445  <li>获取与指定测试审核提交相关联的构建版本或其资源ID</li>
446</ul>
447
448<h5 id="213-构建版本测试通知build-beta-notifications">2.13 构建版本测试通知(Build Beta Notifications)</h5>
449
450<p>使用<code class="language-plaintext highlighter-rouge">buildBetaNotifications</code>资源通知测试人员某个构建版本已为可测试状态。</p>
451
452<p>具体接口能力如下:</p>
453
454<ul>
455  <li>向关联测试人员发出通知,某个构建版本已可供测试</li>
456</ul>
457
458<h4 id="3-用户与职能users-and-roles">3 用户与职能(Users and Roles)</h4>
459
460<h5 id="31-用户users">3.1 用户(Users)</h5>
461
462<p><code class="language-plaintext highlighter-rouge">users</code>用户资源表示你App Store Connect团队中的用户。你可以删除或更改用户,但是你不能通过该资源直接添加用户,添加用户请使用<code class="language-plaintext highlighter-rouge">userInvitation</code>资源。</p>
463
464<p>具体接口能力如下:</p>
465
466<ul>
467  <li>获取团队中全部用户集合</li>
468  <li>获取或修改或删除指定用户</li>
469  <li>获取指定用户可见的全部应用程序或其资源ID</li>
470  <li>为指定用户添加或移除可见应用程序</li>
471  <li>为指定用户替换可见应用程序集合</li>
472</ul>
473
474<h5 id="32-用户邀请user-invitations">3.2 用户邀请(User Invitations)</h5>
475
476<p><code class="language-plaintext highlighter-rouge">userInvitations</code>资源表示已被发送邀请的用户,一旦用户接受邀请,<code class="language-plaintext highlighter-rouge">user</code>资源将会被创建,对应的<code class="language-plaintext highlighter-rouge">userInvitations</code>资源将会被删除。⚠️:邀请有效期只有3天。</p>
477
478<p>具体接口能力如下:</p>
479
480<ul>
481  <li>获取指定或全部的受邀状态用户</li>
482  <li>发送或取消一个用户邀请</li>
483  <li>查看处于受邀状态的用户有权查看的应用程序集合或其资源ID集合</li>
484</ul>
485
486<h4 id="4-销售和财务报告sales-and-finance-reports">4 销售和财务报告(Sales and Finance Reports)</h4>
487
488<p>
488App Store Connect提供查看销售和财务报告的能力,供你衡量应用程序的相关表现。通过API并配合使用过滤器,可以自由筛选数据进行自动化下载销售和财务数据。筛选条件有:地区、日期、报告类型等,可参看官方文档。</p>
489
490<p>具体接口能力如下:</p>
491
492<ul>
493  <li>根据指定条件下载财务报告
494    <ul>
495      <li><a href="https://developer.apple.com/documentation/appstoreconnectapi/download_sales_and_trends_reports">参看官方文档</a></li>
496    </ul>
497  </li>
498  <li>根据指定条件下载销售和趋势报告
499    <ul>
500      <li><a href="https://developer.apple.com/documentation/appstoreconnectapi/download_finance_reports">参看官方文档</a></li>
501    </ul>
502  </li>
503</ul>
504
505<h4 id="5-分页">5 分页</h4>
506
507<ul>
508  <li><strong>大型数据集</strong>
509    <ul>
510      <li>使用分页信息获取大型数据集</li>
511    </ul>
512  </li>
513</ul>
514
515<h4 id="6-错误处理">6 错误处理</h4>
516<ul>
517  <li><code class="language-plaintext highlighter-rouge">error</code> 中的 <code class="language-plaintext highlighter-rouge">id</code> 是错误的唯一标示,可反馈给Apple进行定位</li>
518  <li><code class="language-plaintext highlighter-rouge">error</code> 中的 <code class="language-plaintext highlighter-rouge">code</code> 是程序中处理错误应该使用的属性,是稳定的错误字符串
519    <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="no">GET</span> <span class="sr">/v1/</span><span class="n">betaTesters?filter</span><span class="p">[</span><span class="n">emaill</span><span class="p">]</span><span class="o">=</span><span class="n">kate</span><span class="o">-</span><span class="n">bell</span><span class="o">%</span><span class="mi">22</span><span class="n">mac</span><span class="p">.</span><span class="nf">com</span>
520<span class="no">HTTP</span><span class="o">/</span><span class="mf">1.1</span> <span class="mi">400</span> <span class="no">Bad</span> <span class="no">Request</span>
521<span class="p">{</span>
522  <span class="s2">"errors"</span><span class="p">:</span> <span class="p">[</span>
523      <span class="p">{</span>
524          <span class="s2">"status"</span><span class="p">:</span> <span class="s2">"400"</span><span class="p">,</span>
525          <span class="c1"># error的唯一标示,可反馈给Apple进行定位</span>
526          <span class="s2">"id"</span><span class="p">:</span> <span class="s2">"5becf2db-2f12-4d6a-9dc2-6ceb33c683b4"</span><span class="p">,</span>
527          <span class="s2">"title"</span><span class="p">:</span> <span class="s2">"A parameter has an invalid value"</span><span class="p">,</span>
528          <span class="s2">"detail"</span><span class="p">:</span> <span class="s2">"'emaill' is not a valid filter type"</span><span class="p">,</span>
529          <span class="c1"># 程序错误处理使用该code属性,稳定的错误字符串</span>
530          <span class="s2">"code"</span><span class="p">:</span> <span class="s2">"PARAMETER_ERROR.INVALID"</span><span class="p">,</span>
531          <span class="s2">"source"</span><span class="p">:</span> <span class="p">{</span>
532              <span class="s2">"parameter"</span><span class="p">:</span> <span class="s2">"filter[emaill]"</span>
533           <span class="p">}</span>
534      <span class="p">}</span>
535  <span class="p">]</span>
536<span class="p">}</span>
537</code></pre></div>    </div>
538  </li>
539</ul>
540
541<h2 id="调研结论">调研结论</h2>
542
543<h3 id="apple的自动化进程">Apple的自动化进程</h3>
544
545<p>Apple在WWDC18上总结的目前自动化进程:</p>
546
547<ul>
548  <li>打包及上传构建版本</li>
549  <li>下载崩溃报告</li>
550  <li>Transporter(命令行工具)
551    <ul>
552      <li>自动上传metadata.xml、构建版本(builds)</li>
553      <li>现在除了MacOS,也支持Linux平台</li>
554    </ul>
555  </li>
556  <li>Reporter(命令行工具)
557    <ul>
558      <li>下载销售和财务报告</li>
559      <li>同API的Reports部分接口能力类似</li>
560    </ul>
561  </li>
562</ul>
563
564<p>App Store Connect API的推出,可以更自由的组合自动化工作流。</p>
565
566<h3 id="第三方自动化工具">第三方自动化工具</h3>
567
568<p>业内比较有名的自动化工具:<a href="https://fastlane.tools/">Fastlane</a>,可实现如下自动化流程:</p>
569
570<ul>
571  <li>运行测试工具,生成测试报告</li>
572  <li>增加Build版本号</li>
573  <li>打包</li>
574  <li>上传(截图,元数据,IPA包)</li>
575  <li>管理TestFlight的测试用户,发布TestFlight测试</li>
576</ul>
577
578<h3 id="团队现状及演进方向">团队现状及演进方向</h3>
579
580<p><strong>团队目前的自动化进程:</strong></p>
581
582<ul>
583  <li>自动化打包</li>
584  <li>自动化上传构建版本(builds)到App Store Connect</li>
585</ul>
586
587<p><strong>通过App Store Connect API可增加/优化的自动化流程:</strong></p>
588
589<ul>
590  <li>自动化管理测试人员/测试群组
591    <ul>
592      <li>包括添加、删除,更改权限,发送测试邀请,管理TestFlight公共测试链接等</li>
593    </ul>
594  </li>
595  <li>自动化管理构建版本(builds)的配置与测试审核提交操作,<strong>配合自动打包上传,可实现TestFlight的全自动化</strong>
596    <ul>
597      <li>包括本地化、加密声明、许可协议、审核信息、提交审核、审核结果(BetaAppReviewSubmissionResponse)、发送可供测试通知(BuildBetaNotification)</li>
598      <li>⚠️并非提交App Store的审核</li>
599    </ul>
600  </li>
601  <li>自动化管理App Store Connect团队成员
602    <ul>
603      <li>包括邀请、移除、权限设置等</li>
604    </ul>
605  </li>
606  <li>自动化拉取销售和财务报告</li>
607  <li>自动化管理设备、证书、BundleID、描述文件
608    <ul>
609      <li>⚠️<em>这部分内容在WWDC18中明确提出支持,但Apple目前还未提供接口文档,后续提供后可进行相应工作。</em></li>
610    </ul>
611  </li>
612</ul>
613
614<h2 id="qa">Q&amp;A</h2>
615<p>问:是否可以使用API进行应用程序在App Store的提审操作?
616答:很不幸,不可以。但是API允许你操作TestFlight上测试版本的审核提交操作。</p>
617
618<h2 id="参考">参考</h2>
619<h4 id="官方参考">官方参考:</h4>
620
621<ol>
622  <li><a href="https://developer.apple.com/videos/play/wwdc2018/301/">WWDC18 What’s New in App Store Connect</a></li>
623  <li><a href="https://developer.apple.com/videos/play/wwdc2018/303/">WWDC18 Automating App Store Connect</a></li>
624  <li><a href="https://developer.apple.com/documentation/appstoreconnectapi">App Store Connect API Documentation</a></li>
625</ol>
626
627<h4 id="第三方参考">第三方参考:</h4>
628
629<ol>
630  <li><a href="https://www.avanderlee.com/swift/app-store-connect-api-adoption/">App Store Connect API adoption with use case examples</a></li>
631  <li><a href="https://www.xcteq.co.uk/xcblog/wwdc18-a-basic-guide-to-app-store-connect-api/">WWDC18: A Basic Guide to App Store Connect API</a></li>
632  <li><a href="https://www.xcteq.co.uk/xcblog/app-store-connect-api-is-here-how-to-prepare-for-it/">App Store Connect API is Here: How To Prepare For It</a></li>
633  <li><a href="https://fastlane.tools/">https://fastlane.tools/</a></li>
634</ol>
635
636
637                <hr>
638
639                
640
641
642                <ul class="pager">
643                    
644                    <li class="previous">
645                        <a href="/post/iOS%E5%BC%80%E5%8F%91-WWDC18-Automating-App-Store-Connect" data-toggle="tooltip" data-placement="top" title="iOS开发|WWDC18 Automating App Store Connect">&larr; Previous Post</a>
646                    </li>
647                    
648                    
649                </ul>
650
651
652                
653
654                
655                <!-- disqus 评论框 start -->
656                <div class="comment">
657                    <div id="disqus_thread" class="disqus-thread"></div>
658                </div>
659                <!-- disqus 评论框 end -->
660                
661
662            </div>
663
664    <!-- Sidebar Container -->
665            <div class="
666                col-lg-8 col-lg-offset-2
667                col-md-10 col-md-offset-1
668                sidebar-container">
669
670                <!-- Featured Tags -->
671                
672                <section>
673                    <hr class="hidden-sm hidden-xs">
674                    <h5><a href="/tags/">FEATURED TAGS</a></h5>
675                    <div class="tags">
676        				
677                            
678                				<a href="/tags/#PAT" title="PAT" rel="3">
679                                    PAT
680                                </a>
681                            
682        				
683                            
684                				<a href="/tags/#C" title="C" rel="2">
685                                    C
686                                </a>
687                            
688        				
689                            
690        				
691                            
692                				<a href="/tags/#Python" title="Python" rel="12">
693                                    Python
694                                </a>
695                            
696        				
697                            
698                				<a href="/tags/#C++" title="C++" rel="3">
699                                    C++
700                                </a>
701                            
702        				
703                            
704                				<a href="/tags/#STL" title="STL" rel="3">
705                                    STL
706                                </a>
707                            
708        				
709                            
710        				
711                            
712        				
713                            
714        				
715                            
716        				
717                            
718                				<a href="/tags/#Usage" title="Usage" rel="6">
719                                    Usage
720                                </a>
721                            
722        				
723                            
724        				
725                            
726                				<a href="/tags/#Git" title="Git" rel="2">
727                                    Git
728                                </a>
729                            
730        				
731                            
732        				
733                            
734                				<a href="/tags/#iOS" title="iOS" rel="15">
735                                    iOS
736                                </a>
737                            
738        				
739                            
740        				
741                            
742                				<a href="/tags/#Xcode" title="Xcode" rel="2">
743                                    Xcode
744                                </a>
745                            
746        				
747                            
748                				<a href="/tags/#ARC" title="ARC" rel="2">
749                                    ARC
750                                </a>
751                            
752        				
753                            
754                				<a href="/tags/#MRC" title="MRC" rel="2">
755                                    MRC
756                                </a>
757                            
758        				
759                            
760        				
761                            
762        				
763                            
764        				
765                            
766        				
767                            
768        				
769                            
770        				
771                            
772        				
773                            
774        				
775                            
776        				
777                            
778        				
779                            
780        				
781                            
782        				
783                            
784        				
785                            
786        				
787                            
788        				
789                            
790                				<a href="/tags/#WWDC2018" title="WWDC2018" rel="2">
791                                    WWDC2018
792                                </a>
793                            
794        				
795                            
796                				<a href="/tags/#App Store Connect API" title="Ap
796p Store Connect API" rel="3">
797                                    App Store Connect API
798                                </a>
799                            
800        				
801                            
802        				
803        			</div>
804                </section>
805                
806
807                <!-- Friends Blog -->
808                
809                <hr>
810                <h5>FRIENDS</h5>
811                <ul class="list-inline">
812                    
813                        <li><a href="https://www.objc.io/">ojbc.io</a></li>
814                    
815                        <li><a href="https://objccn.io/">objccn.io</a></li>
816                    
817                        <li><a href="https://onevcat.com/">OneV's Den</a></li>
818                    
819                        <li><a href="http://blog.sunnyxx.com/">sunnyxx</a></li>
820                    
821                </ul>
822                
823            </div>
824        </div>
825    </div>
826</article>
827
828
829
830
831
832<!-- disqus 公共JS代码 start (一个网页只需插入一次) -->
833<script type="text/javascript">
834    /* * * CONFIGURATION VARIABLES * * */
835    var disqus_shortname = "mushank";
836    var disqus_identifier = "/post/iOS开发|App Store Connect API 调研报告.md";
837    var disqus_url = "http://127.0.0.1:4000/post/iOS%E5%BC%80%E5%8F%91-App-Store-Connect-API-%E8%B0%83%E7%A0%94%E6%8A%A5%E5%91%8A.md";
838
839    (function() {
840        var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
841        dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
842        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
843    })();
844</script>
844
845<!-- disqus 公共JS代码 end -->
846
847
848
849
850<!-- async load function -->
851<script>
852    function async(u, c) {
853      var d = document, t = 'script',
854          o = d.createElement(t),
855          s = d.getElementsByTagName(t)[0];
856      o.src = u;
857      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
858      s.parentNode.insertBefore(o, s);
859    }
860</script>
860
861<!-- anchor-js, Doc:http://bryanbraun.github.io/anchorjs/ -->
862<script>
863    async("http://cdn.bootcss.com/anchor-js/1.1.1/anchor.min.js",function(){
864        anchors.options = {
865          visible: 'always',
866          placement: 'right',
867          icon: '#'
868        };
869        anchors.add().remove('.intro-header h1').remove('.subheading').remove('.sidebar-container h5');
870    })
871</script>
871
872<style>
873    /* place left on bigger screen */
874    @media all and (min-width: 800px) {
875        .anchorjs-link{
876            position: absolute;
877            left: -0.75em;
878            font-size: 1.1em;
879            margin-top : -0.1em;
880        }
881    }
882</style>
883
884
885
886    <!-- Footer -->
887<footer>
888    <div class="container">
889        <div class="row">
890            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
891                <ul class="list-inline text-center">
892                    
893                    
894                    <li>
895                        <a href="https://twitter.com/realmushank">
896                            <span class="fa-stack fa-lg">
897                                <i class="fa fa-circle fa-stack-2x"></i>
898                                <i class="fa fa-twitter fa-stack-1x fa-inverse"></i>
899                            </span>
900                        </a>
901                    </li>
902                    
903
904                    <!-- add Weibo, Zhihu by Hux, add target = "_blank" to <a> by Hux -->
905                    
906                    
907                    <li>
908                        <a target="_blank" href="http://weibo.com/mushank">
909                            <span class="fa-stack fa-lg">
910                                <i class="fa fa-circle fa-stack-2x"></i>
911                                <i class="fa fa-weibo fa-stack-1x fa-inverse"></i>
912                            </span>
913                        </a>
914                    </li>
915                    
916
917
918                    
919                    
920                    <li>
921                        <a target="_blank" href="https://github.com/mushank">
922                            <span class="fa-stack fa-lg">
923                                <i class="fa fa-circle fa-stack-2x"></i>
924                                <i class="fa fa-github fa-stack-1x fa-inverse"></i>
925                            </span>
926                        </a>
927                    </li>
928                    
929                </ul>
930                <p class="copyright text-muted">
931                    Copyright &copy; MUSHANK 2023
932                    <br>
933                    Theme by <a href="https://github.com/mushank">Jack</a> |
934                    <iframe
935                        style="margin-left: 2px; margin-bottom:-5px;"
936                        frameborder="0" scrolling="0" width="100px" height="20px"
937                        src="https://ghbtns.com/github-btn.html?user=mushank&repo=mushank.github.io&type=star&count=true" >
938                    </iframe>
939                </p>
940            </div>
941        </div>
942    </div>
943</footer>
944
945<!-- jQuery -->
946<script src="/js/jquery.min.js "></script>
946
947
948<!-- Bootstrap Core JavaScript -->
949<script src="/js/bootstrap.min.js "></script>
949
950
951<!-- Custom Theme JavaScript -->
952<script src="/js/hux-blog.min.js "></script>
952
953
954
955<!-- async load function -->
956<script>
957    function async(u, c) {
958      var d = document, t = 'script',
959          o = d.createElement(t),
960          s = d.getElementsByTagName(t)[0];
961      o.src = u;
962      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
963      s.parentNode.insertBefore(o, s);
964    }
965</script>
965
966
967<!-- 
968     Because of the native support for backtick-style fenced code blocks 
969     right within the Markdown is landed in Github Pages, 
970     From V1.6, There is no need for Highlight.js, 
971     so Huxblog drops it officially.
972
973     - https://github.com/blog/2100-github-pages-now-faster-and-simpler-with-jekyll-3-0  
974     - https://help.github.com/articles/creating-and-highlighting-code-blocks/    
975-->
976<!--
977    
977<script>
978        async("http://cdn.bootcss.com/highlight.js/8.6/highlight.min.js", function(){
979            hljs.initHighlightingOnLoad();
980        })
981    </script>
981
982    <link href="http://cdn.bootcss.com/highlight.js/8.6/styles/github.min.css" rel="stylesheet">
983-->
984
985
986<!-- jquery.tagcloud.js -->
987<script>
988    // only load tagcloud.js in tag.html
989    if($('#tag_cloud').length !== 0){
990        async("/js/jquery.tagcloud.js",function(){
991            $.fn.tagcloud.defaults = {
992                //size: {start: 1, end: 1, unit: 'em'},
993                color: {start: '#bbbbee', end: '#0085a1'},
994            };
995            $('#tag_cloud a').tagcloud();
996        })
997    }
998</script>
998
999
1000<!--fastClick.js -->
1001<script>
1002    async("http://cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function(){
1003        var $nav = document.querySelector("nav");
1004        if($nav) FastClick.attach($nav);
1005    })
1006</script>
1006
1007
1008
1009<!-- Google Analytics -->
1010
1011<script>
1012    // dynamic User by Hux
1013    var _gaId = 'UA-87707150-1';
1014    var _gaDomain = '';
1015
1016    // Originial
1017    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
1018    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
1019    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
1020    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
1021
1022    ga('create', _gaId, _gaDomain);
1023    ga('send', 'pageview');
1024</script>
1024
1025
1026
1027
1028<!-- Baidu Tongji -->
1029
1030
1031
1032</body>
1033
1034</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.