<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Takahiro Octopress Blog]]></title>
  <link href="http://grandbig.github.io/atom.xml" rel="self"/>
  <link href="http://grandbig.github.io/"/>
  <updated>2020-11-01T14:30:47+09:00</updated>
  <id>http://grandbig.github.io/</id>
  <author>
    <name><![CDATA[Takahiro]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Push通知の受信起因でWidgetを更新しよう！]]></title>
    <link href="http://grandbig.github.io/blog/2020/11/01/widget-and-push/"/>
    <updated>2020-11-01T13:59:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2020/11/01/widget-and-push</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>今回は、iOS14から導入された<a href="https://developer.apple.com/jp/widgets/">Widget</a>を題材として扱いたいと思います。</p>

<p>既に様々なところで <code>Widget</code> の導入について説明されていますが、筆者が気になったのは、
『プッシュ通知の受信起因で <code>Widget</code> を更新することができるかどうか 』<br/>
です。</p>

<p>簡単にサンプルを作成して試してみた結果を備忘録として載せておきたいと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>Widgetの更新方法</h3>

<p>まず、 <code>Widget</code> の更新方法ですが、これは大きく分けて2種類あります。</p>

<ul>
<li><code>Widget</code> 自体に定義した更新タイミングで更新する</li>
<li>アプリなどから任意のタイミングで更新する</li>
</ul>


<p>前者の『 <code>Widget</code> 自体に定義した更新タイミングで更新する』は、<br/>
<code>Widget</code> ソース内の <code>getTimeline</code> メソッドが該当します。</p>

<p>例えば、1時間ごとに更新したいと思った場合には、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">import</span> <span class="n">WidgetKit</span>
</span><span class='line'><span class="n">import</span> <span class="n">SwiftUI</span>
</span><span class='line'><span class="n">import</span> <span class="n">Intents</span>
</span><span class='line'>
</span><span class='line'><span class="k">struct</span> <span class="nl">Provider:</span> <span class="n">IntentTimelineProvider</span> <span class="p">{</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'>  <span class="n">func</span> <span class="n">getTimeline</span><span class="p">(</span><span class="k">for</span> <span class="nl">configuration:</span> <span class="n">ConfigurationIntent</span><span class="p">,</span> <span class="k">in</span> <span class="nl">context:</span> <span class="n">Context</span><span class="p">,</span> <span class="nl">completion:</span> <span class="err">@</span><span class="n">escaping</span> <span class="p">(</span><span class="n">Timeline</span><span class="o">&lt;</span><span class="n">Entry</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">())</span> <span class="p">{</span>
</span><span class='line'>      <span class="n">var</span> <span class="nl">entries:</span> <span class="p">[</span><span class="n">SimpleEntry</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
</span><span class='line'>
</span><span class='line'>      <span class="c1">// Generate a timeline consisting of five entries an hour apart, starting from the current date.</span>
</span><span class='line'>      <span class="n">let</span> <span class="n">currentDate</span> <span class="o">=</span> <span class="n">Date</span><span class="p">()</span>
</span><span class='line'>      <span class="k">for</span> <span class="n">hourOffset</span> <span class="k">in</span> <span class="mi">0</span> <span class="p">..</span><span class="o">&lt;</span> <span class="mi">5</span> <span class="p">{</span>
</span><span class='line'>          <span class="n">let</span> <span class="n">entryDate</span> <span class="o">=</span> <span class="n">Calendar</span><span class="p">.</span><span class="n">current</span><span class="p">.</span><span class="n">date</span><span class="p">(</span><span class="nl">byAdding:</span> <span class="p">.</span><span class="n">hour</span><span class="p">,</span> <span class="nl">value:</span> <span class="n">hourOffset</span><span class="p">,</span> <span class="nl">to:</span> <span class="n">currentDate</span><span class="p">)</span><span class="o">!</span>
</span><span class='line'>          <span class="n">let</span> <span class="n">entry</span> <span class="o">=</span> <span class="n">SimpleEntry</span><span class="p">(</span><span class="nl">date:</span> <span class="n">entryDate</span><span class="p">,</span> <span class="nl">randomNumber:</span> <span class="n">Int</span><span class="p">.</span><span class="n">random</span><span class="p">(</span><span class="k">in</span><span class="o">:</span> <span class="mi">1</span> <span class="p">...</span> <span class="mi">10</span><span class="p">),</span> <span class="nl">configuration:</span> <span class="n">configuration</span><span class="p">)</span>
</span><span class='line'>          <span class="n">entries</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">entry</span><span class="p">)</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>      <span class="n">let</span> <span class="n">timeline</span> <span class="o">=</span> <span class="n">Timeline</span><span class="p">(</span><span class="nl">entries:</span> <span class="n">entries</span><span class="p">,</span> <span class="nl">policy:</span> <span class="p">.</span><span class="n">atEnd</span><span class="p">)</span>
</span><span class='line'>      <span class="n">completion</span><span class="p">(</span><span class="n">timeline</span><span class="p">)</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">struct</span> <span class="nl">SimpleEntry:</span> <span class="n">TimelineEntry</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">let</span> <span class="nl">date:</span> <span class="n">Date</span>
</span><span class='line'>  <span class="n">let</span> <span class="nl">randomNumber:</span> <span class="n">Int</span>
</span><span class='line'>  <span class="n">let</span> <span class="nl">configuration:</span> <span class="n">ConfigurationIntent</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように定義することで実現できます。</p>

<p>続いて後者の『アプリなどから任意のタイミングで更新する』は、<br/>
任意のタイミングで、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">WidgetCenter</span><span class="p">.</span><span class="n">shared</span><span class="p">.</span><span class="n">reloadAllTimelines</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>を呼び出してやれば良いだけです。</p>

<p>では、プッシュ通知の受信起因で <code>Widget</code> を更新するためには、どうすれば良いのでしょうか？</p>

<h3>プッシュ受信契機でWidgetを更新する</h3>

<p>答えは、 <code>NotificationService Extension</code> を利用する方法です。</p>

<p><code>NotificationService Extension</code> はプッシュ通知受信時に条件次第で通知内容を変更したい場合などに利用されます。</p>

<p><code>NotificationService Extension</code> は下記のようにプッシュ通知の受信を検知してロジックを組み込むことができます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">import</span> <span class="n">UserNotifications</span>
</span><span class='line'><span class="n">import</span> <span class="n">WidgetKit</span>
</span><span class='line'>
</span><span class='line'><span class="n">class</span> <span class="nl">NotificationService:</span> <span class="n">UNNotificationServiceExtension</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">var</span> <span class="nl">contentHandler:</span> <span class="p">((</span><span class="n">UNNotificationContent</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Void</span><span class="p">)</span><span class="o">?</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">bestAttemptContent:</span> <span class="n">UNMutableNotificationContent</span><span class="o">?</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// プッシュ通知を受信したら、ココを通ります。</span>
</span><span class='line'>    <span class="n">override</span> <span class="n">func</span> <span class="n">didReceive</span><span class="p">(</span><span class="n">_</span> <span class="nl">request:</span> <span class="n">UNNotificationRequest</span><span class="p">,</span> <span class="n">withContentHandler</span> <span class="nl">contentHandler:</span> <span class="err">@</span><span class="n">escaping</span> <span class="p">(</span><span class="n">UNNotificationContent</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Void</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">self</span><span class="p">.</span><span class="n">contentHandler</span> <span class="o">=</span> <span class="n">contentHandler</span>
</span><span class='line'>        <span class="n">bestAttemptContent</span> <span class="o">=</span> <span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">content</span><span class="p">.</span><span class="n">mutableCopy</span><span class="p">()</span> <span class="n">as</span><span class="o">?</span> <span class="n">UNMutableNotificationContent</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">if</span> <span class="n">let</span> <span class="n">bestAttemptContent</span> <span class="o">=</span> <span class="n">bestAttemptContent</span> <span class="p">{</span>
</span><span class='line'>            <span class="c1">// Modify the notification content here...</span>
</span><span class='line'>            <span class="n">bestAttemptContent</span><span class="p">.</span><span class="n">title</span> <span class="o">=</span> <span class="s">&quot;\(bestAttemptContent.title) [modified]&quot;</span>
</span><span class='line'>
</span><span class='line'>            <span class="n">contentHandler</span><span class="p">(</span><span class="n">bestAttemptContent</span><span class="p">)</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">override</span> <span class="n">func</span> <span class="n">serviceExtensionTimeWillExpire</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">if</span> <span class="n">let</span> <span class="n">contentHandler</span> <span class="o">=</span> <span class="n">contentHandler</span><span class="p">,</span> <span class="n">let</span> <span class="n">bestAttemptContent</span> <span class="o">=</span>  <span class="n">bestAttemptContent</span> <span class="p">{</span>
</span><span class='line'>            <span class="n">contentHandler</span><span class="p">(</span><span class="n">bestAttemptContent</span><span class="p">)</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>この <code>didReceive(_:withContentHandler:)</code> 内で <code>WidgetCenter.shared.reloadAllTimelines()</code> を実行してやれば <code>Widget</code> が更新できます。</p>

<h3>(補足)プッシュ通知を簡単に検証する方法について</h3>

<p>今回の検証では、<a href="https://github.com/node-apn/node-apn">node-apn</a>を利用しました。<br/>
<code>node-apn</code> を使えば、プッシュ通知に必要な <code>AuthKey</code> を用意し、多少の実装をするだけ簡単にプッシュ通知を検証できるのでオススメです。</p>

<p>実装は、<a href="https://grandbig.github.io/blog/2017/09/18/node-apn/">プッシュ通知をnode-apnで送ってみよう！</a>で詳しく説明していますので、ご参照ください。<br/>
※ <code>NotificationService Extension</code> を利用するので、ペイロードに <code>"mutable-content": 1</code> が必要になることだけ注意してください。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">note</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">apn</span><span class="p">.</span><span class="nx">Notification</span><span class="p">();</span>
</span><span class='line'><span class="nx">note</span><span class="p">.</span><span class="nx">badge</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
</span><span class='line'><span class="nx">note</span><span class="p">.</span><span class="nx">sound</span> <span class="o">=</span> <span class="s2">&quot;ping.aiff&quot;</span><span class="p">;</span>
</span><span class='line'><span class="nx">note</span><span class="p">.</span><span class="nx">alert</span> <span class="o">=</span> <span class="s2">&quot;プッシュ通知が届きましたよ！&quot;</span><span class="p">;</span>
</span><span class='line'><span class="nx">note</span><span class="p">.</span><span class="nx">topic</span> <span class="o">=</span> <span class="s2">&quot;com.xxxx.WidgetSample&quot;</span><span class="p">;</span>
</span><span class='line'><span class="nx">note</span><span class="p">.</span><span class="nx">mutableContent</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1">// ココで &quot;mutable-content&quot;: 1 を指定しています。</span>
</span></code></pre></td></tr></table></div></figure>


<h3>まとめ</h3>

<p>さて如何でしたでしょうか？<br/>
<code>Widget</code> はiOS14から導入され、ホーム画面に多大な影響を与える期待の新機能ですので、<br/>
ユーザに更なる利便性を与えるためにも積極的に導入していきたいですね。</p>

<p>といったところで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[iOS14からの新機能：NearbyInteraction Framework]]></title>
    <link href="http://grandbig.github.io/blog/2020/06/27/nearby-interaction/"/>
    <updated>2020-06-27T23:39:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2020/06/27/nearby-interaction</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>先日の<a href="https://developer.apple.com/wwdc20/">WWDC2020</a>で <code>NearbyInteraction Framework</code> というものが発表されました。<br/>
これは複数端末間の距離や方角を計測するために利用できる <code>Framework</code> だそうです。</p>

<p>今年はコロナの影響で、ソーシャルディスタンスという言葉が叫ばれるようになったこともあり、意識せざるを得ないような内容に感じられたため、<br/>
WWDC2020で行われた『<a href="https://developer.apple.com/videos/play/wwdc2020/10668/">Meet Nearby Interaction</a>』セッション動画を元に勉強していきたいと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>NearbyInteraction Frameworkの機能を利用できる端末</h3>

<p>iPhone11以降でU1チップを備えている端末に限られます。</p>

<p>現段階では、</p>

<ul>
<li>iPhone11</li>
<li>iPhone11 Pro</li>
<li>iPhone11 Pro Max</li>
</ul>


<p>の3端末に限られています。</p>

<p>※シミュレータが対応しているため、上記実機が手元になくても試すことができます。</p>

<h3>NearbyInteraction Frameworkで測定できるもの</h3>

<p>まず、 <code>NearbyInteraction Framework</code> で測定可能なものですが、</p>

<ul>
<li>距離</li>
<li>方角</li>
</ul>


<p>の2つになっています。</p>

<p>上記2つは <a href="https://developer.apple.com/documentation/nearbyinteraction/ninearbyobject">NINearbyObject</a> に格納された状態で取得します。<br/>
それぞれ</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="err">@</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
</span><span class='line'><span class="n">extension</span> <span class="n">NINearbyObject</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">public</span> <span class="n">var</span> <span class="nl">distance:</span> <span class="n">Float</span><span class="o">?</span> <span class="p">{</span> <span class="n">get</span> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">public</span> <span class="n">var</span> <span class="nl">direction:</span> <span class="n">simd_float3</span><span class="o">?</span> <span class="p">{</span> <span class="n">get</span> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>と定義されています。</p>

<h3>NearbyInteraction Frameworkの利用方法</h3>

<p>ここからは詳細について見ていきましょう。</p>

<h4>discoveryTokenとは</h4>

<p>端末間の距離や方角を計測するとなると、互いの端末を検知し、ペアリングする必要がありそうな気がします。<br/>
これを可能にするために、 <code>NearbyInteraction Framework</code> では <code>NISession</code> オブジェクトを生成し、<br/>
その <code>NISession</code> が提供する <code>discoveryToken</code> を利用する必要があります。</p>

<p>下記のように <code>discoveryToken</code> が <code>NISession</code> 内に定義されています。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="err">@</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
</span><span class='line'><span class="n">open</span> <span class="n">class</span> <span class="n">NISession</span> <span class="o">:</span> <span class="n">NSObject</span> <span class="p">{</span>
</span><span class='line'>  <span class="p">....</span>
</span><span class='line'>  <span class="err">@</span><span class="n">NSCopying</span> <span class="n">open</span> <span class="n">var</span> <span class="nl">discoveryToken:</span> <span class="n">NIDiscoveryToken</span><span class="o">?</span> <span class="p">{</span> <span class="n">get</span> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>因みに、この <code>discoveryToken</code> は</p>

<ul>
<li>ランダムな識別番号として生成される</li>
<li><code>session</code> と同等の有効期限が存在する</li>
<li><code>session</code> の無効にしたり、アプリを終了すると <code>discoveryToken</code> も無効になる</li>
</ul>


<p>という性質があります。</p>

<p>この <code>discoveryToken</code> を端末間で共有し合うことで、距離や方角を計測する端末を認識することができるわけです。</p>

<h4>discoveryTokenの共有方法</h4>

<p>では、 <code>discoveryToken</code> の端末間での共有方法は何があるかというと、<br/>
WWDC2020のセッションでは、 <code>Multiple Connectivity Framework</code> が紹介されていました。</p>

<p>また、 <code>discoveryToken</code> は <code>NIDiscoveryToken</code> 型であり、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="err">@</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
</span><span class='line'><span class="n">open</span> <span class="n">class</span> <span class="n">NIDiscoveryToken</span> <span class="o">:</span> <span class="n">NSObject</span><span class="p">,</span> <span class="n">NSCopying</span><span class="p">,</span> <span class="n">NSSecureCoding</span> <span class="p">{</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように <code>NSSecureCoding</code> に準拠しているため、下記のように簡単にエンコードすることができます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">let</span> <span class="n">session</span> <span class="o">=</span> <span class="n">NISession</span><span class="p">()</span>
</span><span class='line'><span class="n">session</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span>
</span><span class='line'>
</span><span class='line'><span class="n">let</span> <span class="n">token</span> <span class="o">=</span> <span class="n">session</span><span class="p">.</span><span class="n">discoveryToken</span>
</span><span class='line'><span class="n">let</span> <span class="n">encodedData</span> <span class="o">=</span> <span class="n">try</span><span class="o">?</span> <span class="n">NSKeyedArchiver</span><span class="p">.</span><span class="n">archivedData</span><span class="p">(</span><span class="nl">withRootObject:</span> <span class="n">token</span><span class="p">,</span> <span class="nl">requiringSecureCoding:</span> <span class="n">true</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>Multiple Connectivity Framework</code> を利用する場合は、下記のように送信します。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">private</span> <span class="n">var</span> <span class="nl">mcSession:</span> <span class="n">MCSession</span><span class="o">!</span>
</span><span class='line'>
</span><span class='line'><span class="k">do</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">try</span> <span class="n">mcSession</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nl">toPeers:</span> <span class="n">mcSession</span><span class="p">.</span><span class="n">connectedPeers</span><span class="p">,</span> <span class="nl">with:</span> <span class="p">.</span><span class="n">reliable</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span> <span class="n">catch</span> <span class="n">let</span> <span class="n">error</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">NSLog</span><span class="p">(</span><span class="s">&quot;Error sending data: \(error)&quot;</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>逆に他端末から送信されたエンコードされたデータを受信したら、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">guard</span> <span class="n">let</span> <span class="n">discoveryToken</span> <span class="o">=</span> <span class="n">try</span><span class="o">?</span> <span class="n">NSKeyedUnarchiver</span><span class="p">.</span><span class="n">unarchivedObject</span><span class="p">(</span><span class="nl">ofClass:</span> <span class="n">NIDiscoveryToken</span><span class="p">.</span><span class="n">self</span><span class="p">,</span> <span class="nl">from:</span> <span class="n">data</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">fatalError</span><span class="p">(</span><span class="s">&quot;Unexpectedly failed to encode discovery token.&quot;</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のようにデコードし、 <code>discoveryToken</code> を取り出します。<br/>
これで互いに計測対象端末の <code>discoveryToken</code> が共有できました。</p>

<h4>計測値の取得方法</h4>

<p>あとは、受信した <code>discoveryToken</code> を元に生成した <code>config</code> を利用してセッションを開始します。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">let</span> <span class="n">config</span> <span class="o">=</span> <span class="n">NINearbyPeerConfiguration</span><span class="p">(</span><span class="nl">peerToken:</span> <span class="n">discoveryToken</span><span class="p">)</span>
</span><span class='line'><span class="n">session</span><span class="o">?</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>対象端末との距離や方角の値は <code>NISessionDelegate</code> を通じて取得します。<br/>
下記の <code>didUpdate</code> メソッドにて、最新の <code>NINearbyObject</code> 情報が取得できます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="err">@</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
</span><span class='line'><span class="n">public</span> <span class="n">protocol</span> <span class="n">NISessionDelegate</span> <span class="o">:</span> <span class="n">NSObjectProtocol</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// This is called when new updates about nearby objects are available.</span>
</span><span class='line'>  <span class="n">optional</span> <span class="n">func</span> <span class="n">session</span><span class="p">(</span><span class="n">_</span> <span class="nl">session:</span> <span class="n">NISession</span><span class="p">,</span> <span class="n">didUpdate</span> <span class="nl">nearbyObjects:</span> <span class="p">[</span><span class="n">NINearbyObject</span><span class="p">])</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>NISessionDelegate</code> には他にも幾つかメソッドが用意されています。</p>

<p>例えば、 <code>didRemove</code> メソッドは、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="err">@</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
</span><span class='line'><span class="n">public</span> <span class="n">protocol</span> <span class="n">NISessionDelegate</span> <span class="o">:</span> <span class="n">NSObjectProtocol</span> <span class="p">{</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'>  <span class="c1">// This is called when the system is no longer attempting to interact with some nearby objects.</span>
</span><span class='line'>  <span class="n">optional</span> <span class="n">func</span> <span class="n">session</span><span class="p">(</span><span class="n">_</span> <span class="nl">session:</span> <span class="n">NISession</span><span class="p">,</span> <span class="n">didRemove</span> <span class="nl">nearbyObjects:</span> <span class="p">[</span><span class="n">NINearbyObject</span><span class="p">],</span> <span class="nl">reason:</span> <span class="n">NINearbyObject</span><span class="p">.</span><span class="n">RemovalReason</span><span class="p">)</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>ペアリングしたはずの計測対象端末から情報が取得できない場合に呼び出されます。<br/>
これが呼び出されるパターンは主に次の2つになります。</p>

<ul>
<li>計測対象端末が遠く離れてしまい、情報取得可能な期間をタイムアウトした場合</li>
<li>計測対象端末がセッションを終了した場合</li>
</ul>


<p>上記理由も、メソッド引数の <code>reason</code> に格納されているため、識別することができます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">extension</span> <span class="n">NINearbyObject</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="err">@</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
</span><span class='line'>    <span class="n">public</span> <span class="k">enum</span> <span class="n">RemovalReason</span> <span class="o">:</span> <span class="n">Int</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">case</span> <span class="n">timeout</span> <span class="o">=</span> <span class="mi">0</span>
</span><span class='line'>        <span class="k">case</span> <span class="n">peerEnded</span> <span class="o">=</span> <span class="mi">1</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>また、アプリの状態に関わるメソッドも用意されています。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="err">@</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
</span><span class='line'><span class="n">public</span> <span class="n">protocol</span> <span class="n">NISessionDelegate</span> <span class="o">:</span> <span class="n">NSObjectProtocol</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'>    <span class="c1">// This is called when a session is suspended.</span>
</span><span class='line'>    <span class="n">optional</span> <span class="n">func</span> <span class="n">sessionWasSuspended</span><span class="p">(</span><span class="n">_</span> <span class="nl">session:</span> <span class="n">NISession</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// This is called when a session may be resumed.</span>
</span><span class='line'>    <span class="n">optional</span> <span class="n">func</span> <span class="n">sessionSuspensionEnded</span><span class="p">(</span><span class="n">_</span> <span class="nl">session:</span> <span class="n">NISession</span><span class="p">)</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>sessionWasSuspended</code> はアプリがFG起動を終了し、サスペンド状態になった際に呼び出され、<br/>
<code>sessionSuspensionEnded</code> はアプリが再びFG起動に戻し、サスペンド状態が終了した際に呼び出されます。</p>

<p>因みに、セッションは一度停止すると自動的に再開はされないため、 <code>sessionSuspensionEnded</code> 内で再度セッションを開始する必要があります。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">func</span> <span class="nf">sessionSuspensionEnded</span><span class="p">(</span><span class="n">_</span> <span class="nl">session:</span> <span class="n">NISession</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="n">let</span> <span class="n">config</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">session</span><span class="o">?</span><span class="p">.</span><span class="n">configuration</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">session</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>最後の <code>Delegate</code> メソッドとして <code>didInvalidateWith</code> が用意されています。<br/>
これはセッションが無効になった場合に呼び出されます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="err">@</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">14.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span>
</span><span class='line'><span class="n">public</span> <span class="n">protocol</span> <span class="n">NISessionDelegate</span> <span class="o">:</span> <span class="n">NSObjectProtocol</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'>    <span class="c1">// This is called when a session is invalidated.</span>
</span><span class='line'>    <span class="n">optional</span> <span class="n">func</span> <span class="n">session</span><span class="p">(</span><span class="n">_</span> <span class="nl">session:</span> <span class="n">NISession</span><span class="p">,</span> <span class="n">didInvalidateWith</span> <span class="nl">error:</span> <span class="n">Error</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>セッションが無効になると、 <code>run</code> メソッドを使っても再開することができないため、<br/>
セッションの生成からやり直す必要があります。<br/>
(セッションを生成し、 <code>discoveryToken</code> を交換し、セッションを開始する一連の流れを頭からやり直します。 )</p>

<h3>NearbyInteraction Frameworkで計測する上での制約</h3>

<p><code>NearbyInteraction Framework</code> は非常に強力な計測ができますが、<br/>
その精度を高めるためには幾つかユーザに制約を課さざるを得ないようです。</p>

<p>その制約とは、</p>

<ol>
<li>方角の測定可能フィールド上に両端末が存在していること</li>
<li>両端末ともに <code>Portrait</code> の向きで端末を持っていること</li>
<li>両端末間に壁や人などの障害物がないこと</li>
</ol>


<p>が上げられています。</p>

<p>以下、セッションの資料がわかりやすかったため、引用させて頂きます。</p>

<p><strong>方角の測定可能フィールド上に両端末が存在していること</strong></p>

<p><img src="http://grandbig.github.io/images/nearbyinteraction_1.png" alt="方角の測定可能フィールド上に両端末が存在していること" /></p>

<p><strong>両端末ともにPortraitの向きで端末を持っていること</strong><br/>
<img src="http://grandbig.github.io/images/nearbyinteraction_2.png" alt="両端末ともにPortraitの向きで端末を持っていること" /></p>

<p><strong>両端末間に壁や人などの障害物がないこと</strong><br/>
<img src="http://grandbig.github.io/images/nearbyinteraction_3.png" alt="両端末間に壁や人などの障害物がないこと" /></p>

<h3>まとめ</h3>

<p>さて、如何でしたでしょうか。<br/>
利用上の制約があることで万能ではないと感じる方もいるかもしれませんが、<br/>
cm単位の精度で端末間の距離や方角がわかるという機能は、<br/>
これからの新生活で大活躍する可能性も秘めているかもしれません。</p>

<p>個人的には、<br/>
何か世の中の役に立つようなサービスを考えるきっかけになったかなと思いました。</p>

<p>最後に、今回説明させて頂いた内容は、冒頭に書きました通り<a href="https://developer.apple.com/videos/play/wwdc2020/10668/">Meet Nearby Interaction</a>動画で確認できますし、<br/>
<a href="https://developer.apple.com/documentation/nearbyinteraction/implementing_interactions_between_users_in_close_proximity">Implementing Interactions Between Users in Close Proximit</a>にてサンプルコードも公開されています。</p>

<p>より詳細を見る場合は当然ですが、上記を見ることをオススメいたします。</p>

<p>といったところで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[jpegDataメソッドによるJPEG画像圧縮テスト]]></title>
    <link href="http://grandbig.github.io/blog/2020/05/30/jpeg-compression/"/>
    <updated>2020-05-30T11:30:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2020/05/30/jpeg-compression</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>最近は、iPhone端末も昔に比べて飛躍的にカメラの性能も向上し、<br/>
高品質な写真を撮ることができます。<br/>
一消費者としては喜ばしい限りなのですが、エンジニアとしては喜んでばかりもいられません。</p>

<p>というのも、画像をクラウド上で保存する機能があった場合に、<br/>
容量と品質を天秤にかけざるを得ないことも、まだまだあるからです。</p>

<p>そこで今回は、画像をJPEG圧縮することで、どの程度の品質で容量が変わるのか実験してみたいと思います。<br/>
利用するメソッドは下記になります。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">func</span> <span class="n">jpegData</span><span class="p">(</span><span class="nl">compressionQuality:</span> <span class="n">CGFloat</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Data</span><span class="o">?</span>
</span></code></pre></td></tr></table></div></figure>




<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>実験</h3>

<p>早速、実験方法ですが、<br/>
<code>jpegData</code> の引数である <code>compressionQuality</code> に0.1刻みで0.0〜1.0の間を指定して、<br/>
結果容量を比較してみました。</p>

<h4>実験①：イラスト画像</h4>

<p>まずは、区切り線の明瞭なイラスト画像を用いて実験をしてみました。</p>

<p><img src="http://grandbig.github.io/images/jpeg_compression_sample1_result.png" alt="実験結果①" /></p>

<p>1.0から0.9が極端に下がっていることがわかります。<br/>
また、0.1と0.0とでは画像容量に差分がない結果となりました。</p>

<p>実際に、画像を比較してみると、</p>

<p><strong>compressionQuality: 1.0 の画像</strong><br/>
<img src="http://grandbig.github.io/images/jpeg_compression_sample1_quality_1.JPG" alt="compressionQualityに1.0を指定したイラスト画像" /></p>

<p><strong>compressionQuality: 0.5 の画像</strong><br/>
<img src="http://grandbig.github.io/images/jpeg_compression_sample1_quality_0_5.JPG" alt="compressionQualityに0.5を指定したイラスト画像" /></p>

<p><strong>compressionQuality: 0.0 の画像</strong><br/>
<img src="http://grandbig.github.io/images/jpeg_compression_sample1_quality_0.JPG" alt="compressionQualityに0.0を指定したイラスト画像" /></p>

<p>のようになります。<br/>
当然ですが、1.0の方が全体的に滑らかで、0.0はかなり品質が落ちていることがわかります。</p>

<p>※画像は<a href="https://illustrain.com/?p=9259">イラストレイン</a>からお借りしました。</p>

<h4>実験②：写真</h4>

<p>続いて写真で実験してみました。</p>

<p><img src="http://grandbig.github.io/images/jpeg_compression_sample2_result.png" alt="実験結果②" /></p>

<p>イラストと同じく、1.0から0.9が極端に容量が下がっている一方で、<br/>
0.1から0.0は同じ容量となりました。</p>

<p>こちらも画像を比較してみると、</p>

<p><strong>compressionQuality: 1.0 の画像</strong><br/>
<img src="http://grandbig.github.io/images/jpeg_compression_sample2_quality_1.JPG" alt="compressionQualityに1.0を指定したイラスト画像" /></p>

<p><strong>compressionQuality: 0.5 の画像</strong><br/>
<img src="http://grandbig.github.io/images/jpeg_compression_sample2_quality_0_5.JPG" alt="compressionQualityに0.5を指定したイラスト画像" /></p>

<p><strong>compressionQuality: 0.0 の画像</strong><br/>
<img src="http://grandbig.github.io/images/jpeg_compression_sample2_quality_0.JPG" alt="compressionQualityに0.0を指定したイラスト画像" /></p>

<p>のようになります。
やはり0.0にまでなると品質劣化が肉眼でもわかりますね。</p>

<p>※画像は、<a href="https://www.pakutaso.com/20160121025post-6686.html">ぱくたそ</a>からお借りしました。</p>

<h3>因みに</h3>

<p>JPEG圧縮のアルゴリズムは当然ながら、Swiftのメソッドを利用したからといって変わるわけではありません。<br/>
なので、Mac上で画像を書き出す際に品質を指定することで確認できます笑</p>

<p><img src="http://grandbig.github.io/images/jpeg_compression_mac.png" alt="Mac上で画像をJPEG圧縮" /></p>

<p>JPEGのアルゴリズムに関しては、</p>

<ul>
<li><a href="http://funini.com/kei/math/jpeg.shtml">JPEGの仕組み</a></li>
<li><a href="https://www.marguerite.jp/Nihongo/Labo/Image/JPEG.html">JPEG画像形式の概要(圧縮アルゴリズム)。</a></li>
</ul>


<p>などが参考になるかと思います。</p>

<h3>まとめ</h3>

<p>以上の実験からわかる通り、圧縮具合を変えることで容量に大きな変化が生まれますし、<br/>
当然ですが、それに伴い品質も劣化していきます。</p>

<p>サービス上で許容となる品質レベルは異なるでしょうし、慎重な意思決定が必要となることでしょう。<br/>
個人的には <code>0.5</code> でも全然気にならなかったりしますが、許容範囲広すぎでしょうか。。。笑</p>

<p>と言ったところで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Color AssetとコードでiOS13の色のダークモード対応]]></title>
    <link href="http://grandbig.github.io/blog/2020/04/18/color/"/>
    <updated>2020-04-18T10:38:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2020/04/18/color</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>iOS13からダークモードがiPhoneにも追加され、ユーザニーズからアプリ側でもダークモード対応を必要とされる場面が増えてきました。<br/>
一方でiOS12以前のOSをサポートする必要がある場合がほとんどかと思います。</p>

<p>「iOS12ではダークモードが利用できないので、ダークモードが導入できないのでは？」</p>

<p>と思う方がいる可能性を踏まえて、<br/>
本日は、今更ではありますが、旧OSをサポートしながらダークモード対応するための <code>Color</code> 定義について見ていきたいと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>Color Assetでダークモード時の色を定義</h3>

<p>Xcode9より <code>Color Asset</code> 機能が追加されています。<br/>
エンジニアだけでなく、デザイナーさんがXcodeを触って色を確認する可能性も踏まえると、<br/>
<code>Color Asset</code> を使わない手はないでしょう。<br/>
( <code>Color Asset</code> が利用できる状況なら、恐らく最も手軽にダークモード対応が可能です。 )</p>

<p>Xcode11より、 <code>Color Asset</code> でダークモード対応できる機能が追加されました。<br/>
方法は簡単で下図のように、右メニュー > <code>Appearances</code> の設定を <code>Any,Dark</code> に変更するだけです。<br/>
( <code>Light</code> , <code>Dark</code> 以外にモードを設ける場合は <code>Any,Light,Dark</code> を選択してください。 )</p>

<p><img src="http://grandbig.github.io/images/darkmode_1.png" alt="Color Assetの定義" /></p>

<p>これでダークモードの対応は完了です。<br/>
下記のように、 <code>Color Asset</code> で設定した名称で呼び出せば、端末のモードに従って色を設定できます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">view</span><span class="p">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="n">UIColor</span><span class="p">(</span><span class="nl">named:</span> <span class="s">&quot;mainBackgroundColor&quot;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>iOS12以前の旧OSではダークモードがありませんので、<br/>
<code>UIColor(named: "mainBackgroundColor")</code> を利用すると自動的に <code>Any</code> が採用されます。</p>

<h3>コードでダークモード時の色を定義</h3>

<p>さて、 <code>Color Asset</code> が利用できない場合は、どうすれば良いでしょうか。<br/>
この場合、ゴリゴリにコードで記述するしかないでしょう。</p>

<p>iOS13以上のアプリであれば、 <code>UIColor</code> に、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">init</span><span class="p">(</span><span class="nl">dynamicProvider:</span> <span class="err">@</span><span class="n">escaping</span> <span class="p">(</span><span class="n">UITraitCollection</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">UIColor</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>というイニシャライザーが追加されているため、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">extension</span> <span class="n">UIColor</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">class</span> <span class="n">var</span> <span class="nl">mainBackgroundColor:</span> <span class="n">UIColor</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="p">.</span><span class="n">init</span><span class="p">(</span><span class="nl">dynamicProvider:</span> <span class="p">{</span> <span class="n">traitCollection</span> <span class="o">-&gt;</span> <span class="n">UIColor</span> <span class="k">in</span>
</span><span class='line'>      <span class="k">switch</span> <span class="n">traitCollection</span><span class="p">.</span><span class="n">userInterfaceStyle</span> <span class="p">{</span>
</span><span class='line'>      <span class="k">case</span> <span class="p">.</span><span class="nl">dark:</span>
</span><span class='line'>        <span class="k">return</span> <span class="p">.</span><span class="n">black</span>
</span><span class='line'>      <span class="k">case</span> <span class="p">.</span><span class="n">unspecified</span><span class="p">,</span> <span class="p">.</span><span class="nl">light:</span>
</span><span class='line'>        <span class="k">return</span> <span class="p">.</span><span class="n">white</span>
</span><span class='line'>      <span class="err">@</span><span class="n">unknown</span> <span class="k">default</span><span class="o">:</span>
</span><span class='line'>        <span class="k">return</span> <span class="p">.</span><span class="n">white</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>    <span class="p">})</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>といった形で定義ができるでしょう。</p>

<p>これがiOS12以前も対応するとなると、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">extension</span> <span class="n">UIColor</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">class</span> <span class="n">var</span> <span class="nl">mainBackgroundColor:</span> <span class="n">UIColor</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="err">#</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">13.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="k">return</span> <span class="n">UIColor</span> <span class="p">{</span> <span class="n">traitCollection</span> <span class="o">-&gt;</span> <span class="n">UIColor</span> <span class="k">in</span>
</span><span class='line'>        <span class="k">switch</span> <span class="n">traitCollection</span><span class="p">.</span><span class="n">userInterfaceStyle</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">case</span> <span class="p">.</span><span class="nl">dark:</span>
</span><span class='line'>          <span class="k">return</span> <span class="p">.</span><span class="n">black</span>
</span><span class='line'>        <span class="k">case</span> <span class="p">.</span><span class="n">unspecified</span><span class="p">,</span> <span class="p">.</span><span class="nl">light:</span>
</span><span class='line'>          <span class="k">return</span> <span class="p">.</span><span class="n">white</span>
</span><span class='line'>        <span class="err">@</span><span class="n">unknown</span> <span class="k">default</span><span class="o">:</span>
</span><span class='line'>          <span class="k">return</span> <span class="p">.</span><span class="n">white</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>      <span class="k">return</span> <span class="p">.</span><span class="n">white</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように条件分岐が必要になります。</p>

<h3>まとめ</h3>

<p>さて、如何でしたでしょうか？<br/>
案外、簡単にダークモード対応ができることがわかります。</p>

<p>ただし、コードで定義する場合は、色定義ファイルが膨大になることが予想されるため、<br/>
もう少し工夫が必要になることでしょう。</p>

<p>と言ったところで本日はここまで。</p>

<p>参考URL:</p>

<ul>
<li><a href="https://developer.apple.com/documentation/uikit/uicolor/3238041-init">Apple Document &ndash; init(dynamicProvider:)</a></li>
</ul>


<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Xcode11.4でやっと修正されたドキュメントコメントの挿入ショートカット]]></title>
    <link href="http://grandbig.github.io/blog/2020/03/28/xcode11-4/"/>
    <updated>2020-03-28T23:26:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2020/03/28/xcode11-4</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>XcodeでiOSアプリを開発する際に、ほとんどの開発者はドキュメントコメントを書く機会があると思います。</p>

<p>Xcodeでは、メニューバーから<br/>
<code>Editor &gt; Structure &gt; Add Documentation</code>
を選択すると、最適な形式のドキュメントコメントを差し込んでくれます。<br/>
( または、<code>command + option + /</code> でショートカットできます。 )</p>

<p>実はこの機能が困ったことにXcode11当初でバグがありました。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>Xcode11で発生していたバグと、その解消</h3>

<p>例えば、下記のようなメソッドがある場合、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">func</span> <span class="n">connect</span><span class="p">(</span><span class="nl">word1:</span> <span class="n">String</span><span class="p">,</span> <span class="nl">word2:</span> <span class="n">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">String</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">text1</span> <span class="o">+</span> <span class="n">text2</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>ドキュメントコメントは、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">/// 引数に指定した2つのワードを連結した結果を返却します</span>
</span><span class='line'><span class="c1">///</span>
</span><span class='line'><span class="c1">/// - Parameters:</span>
</span><span class='line'><span class="c1">///   - word1: 1つ目のワード</span>
</span><span class='line'><span class="c1">///   - word2: 2つ目のワード</span>
</span><span class='line'><span class="c1">/// - Returns: 2つのワードを連結した結果</span>
</span><span class='line'><span class="n">func</span> <span class="n">connect</span><span class="p">(</span><span class="nl">word1:</span> <span class="n">String</span><span class="p">,</span> <span class="nl">word2:</span> <span class="n">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">String</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">text1</span> <span class="o">+</span> <span class="n">text2</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のようになります。</p>

<p>Xccode10.xまでは上記期待値の元、ドキュメントコメントを追加していたのですが、<br/>
これがXcode11にアップデートしてみると&hellip;</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">/// 引数に指定した2つのワードを連結した結果を返却します</span>
</span><span class='line'><span class="c1">///</span>
</span><span class='line'><span class="c1">/// - Parameters:</span>
</span><span class='line'><span class="c1">///   - word1: 1つ目のワード</span>
</span><span class='line'><span class="c1">///   - word2: 2つ目のワード</span>
</span><span class='line'><span class="n">func</span> <span class="n">connect</span><span class="p">(</span><span class="nl">word1:</span> <span class="n">String</span><span class="p">,</span> <span class="nl">word2:</span> <span class="n">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">String</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">text1</span> <span class="o">+</span> <span class="n">text2</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように戻り値のあるメソッドだったとしても <code>Returns</code> を挿入してくれなくなってしまっていました。</p>

<p>また、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">protocol</span> <span class="n">sample</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">func</span> <span class="n">connect</span><span class="p">(</span><span class="nl">word1:</span> <span class="n">String</span><span class="p">,</span> <span class="nl">word2:</span> <span class="n">String</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>という <code>protocol</code> メソッドを定義した場合は逆に、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">protocol</span> <span class="n">sample</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">/// &lt;#Description#&gt;</span>
</span><span class='line'>  <span class="c1">/// - Parameters:</span>
</span><span class='line'>  <span class="c1">///   - word1: &lt;#word1 description#&gt;</span>
</span><span class='line'>  <span class="c1">///   - word2: &lt;#word2 description#&gt;</span>
</span><span class='line'>  <span class="c1">/// - Returns: &lt;#description#&gt;</span>
</span><span class='line'>  <span class="n">func</span> <span class="n">connect</span><span class="p">(</span><span class="nl">word1:</span> <span class="n">String</span><span class="p">,</span> <span class="nl">word2:</span> <span class="n">String</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように不必要な <code>Returns</code> が挿入されてしまっていました。</p>

<p>上記、Xcode11.4にて、やっと正しく挿入されるように修正されたようです。</p>

<p>因みに、筆者としては、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// Xcode11以降の挿入されるドキュメントコメント</span>
</span><span class='line'>
</span><span class='line'><span class="c1">/// &lt;#Description#&gt;</span>
</span><span class='line'><span class="c1">/// - Parameters:</span>
</span><span class='line'><span class="c1">///   - word1: &lt;#word1 description#&gt;</span>
</span><span class='line'><span class="c1">///   - word2: &lt;#word2 description#&gt;</span>
</span><span class='line'><span class="c1">/// - Returns: &lt;#description#&gt;</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Xcode10.xで挿入されていたドキュメントコメント</span>
</span><span class='line'>
</span><span class='line'><span class="c1">/// &lt;#Description#&gt;</span>
</span><span class='line'><span class="c1">///</span>
</span><span class='line'><span class="c1">/// - Parameters:</span>
</span><span class='line'><span class="c1">///   - word1: &lt;#word1 description#&gt;</span>
</span><span class='line'><span class="c1">///   - word2: &lt;#word2 description#&gt;</span>
</span><span class='line'><span class="c1">/// - Returns: &lt;#description#&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>のようにXcode10.xまでのメソッド概要と <code>Parameters</code> の間に1行あける感覚に慣れてしまっていたため、<br/>
Xcode11以降の書き方に違和感があり、手動で1行挿入してしまっていたりします笑。</p>

<h3>まとめ</h3>

<p>何はともあれ、地味に面倒だと感じていた部分がやっと修正されて何よりです。<br/>
Xcode11系は当初から幾つかの致命的なバグを抱えていましたが、<br/>
ひとまず細かなところも修正されて安定的に利用できるようになりましたかね〜</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SwiftUIの基礎を理解しよう！(1)〜簡単な装飾とボタンアクション〜]]></title>
    <link href="http://grandbig.github.io/blog/2020/02/22/swiftui-1/"/>
    <updated>2020-02-22T17:59:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2020/02/22/swiftui-1</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>さて、本日はXcode11から新規似追加された <code>SwiftUI</code> によるiOSアプリ開発について勉強していきたいと思います。</p>

<p>筆者がぱっと見る限り、 <code>SwiftUI</code> は、</p>

<ul>
<li>ホットリロードによる手動ビルドの手間の改善</li>
<li><code>xib</code> や <code>storyboard</code> によるUI実装のレビューの難しさの解消</li>
</ul>


<p>に一役買っていると感じました。</p>

<p>また、 <code>SwiftUI</code> での書き方自体は、<br/>
これまでのiOSアプリの書き方に慣れている人にとっては、やはりそれなりの準備期間は必要になりそうです。<br/>
( <code>RxSwift</code> 使いの方々にとっては比較的とっかかりやすいという話もあります。 )</p>

<p>ということもあり、<br/>
実際の業務にて <code>SwiftUI</code> を利用するシーンはまだそこまで増えることはないかもしれませんが、<br/>
iOSアプリ開発の実装方法の幅を増やすに越したことはないでしょう。</p>

<p>では、少しずつ <code>SwiftUI</code> について勉強していきたいと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>Hello World!</h3>

<p>さて、まずは新規に <code>SwiftUI</code> プロジェクトを作成した場合、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// ContentView.swift</span>
</span><span class='line'><span class="n">import</span> <span class="n">SwiftUI</span>
</span><span class='line'>
</span><span class='line'><span class="k">struct</span> <span class="nl">ContentView:</span> <span class="n">View</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">body:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">Text</span><span class="p">(</span><span class="s">&quot;Hello, World!&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">struct</span> <span class="nl">ContentView_Previews:</span> <span class="n">PreviewProvider</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">static</span> <span class="n">var</span> <span class="nl">previews:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">ContentView</span><span class="p">()</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>といった形で <code>Hello World!</code> プロジェクトが作成されます。</p>

<p>シミュレータを起動することなく、ホットリロードしながら開発するには、<br/>
<code>Resume</code> ボタンをクリックします。</p>

<p><img src="http://grandbig.github.io/images/swiftui-1_1.png" alt="ホットリロードによる開発のためにResumeを実行する" /></p>

<p>その結果、以下のように、あたかもシミュレータが起動したかのような状態で開発を進めることができます。</p>

<p><img src="http://grandbig.github.io/images/swiftui-1_2.png" alt="ホットリロード状態での開発" /></p>

<h3>UIの作成方法</h3>

<p>具体的に <code>SwiftUI</code> でどのようにUIを作成するのでしょうか。</p>

<p>慣れれば、コードベースで全て書き上げることができると思いますが、<br/>
慣れないうちは、GUIと併用して実装するのが良いかと思います。</p>

<h4>bodyプロパティ</h4>

<p><code>SwiftUI</code> で実装する上で最も基本的かつ重要なことは、 <code>body</code> プロパティです。<br/>
<code>body</code> プロパティは 計算型の <code>View</code> 型プロパティです。<br/>
この <code>body</code> に各種パーツを設定した <code>View</code> をセットすることでUIを表現します。</p>

<p>言い換えると、『 <code>body</code> には様々なパーツを包括した1つの <code>View</code> をセットする』ということです。</p>

<p>例えば、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">struct</span> <span class="nl">ContentView:</span> <span class="n">View</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">body:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">Text</span><span class="p">(</span><span class="s">&quot;hoge&quot;</span><span class="p">)</span>
</span><span class='line'>        <span class="n">Text</span><span class="p">(</span><span class="s">&quot;fuga&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>といったことはできず、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">struct</span> <span class="nl">ContentView:</span> <span class="n">View</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">body:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">VStack</span> <span class="p">{</span>
</span><span class='line'>          <span class="n">Text</span><span class="p">(</span><span class="s">&quot;hoge&quot;</span><span class="p">)</span>
</span><span class='line'>          <span class="n">Text</span><span class="p">(</span><span class="s">&quot;fuga&quot;</span><span class="p">)</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように <code>VStack</code> , <code>HStack</code> , <code>Group</code> など <code>View</code> を包括するパーツを利用してまとめる必要があります。</p>

<p>先程記載した通り、コードベースで直接 <code>VStack</code> で囲っても良いですし、<br/>
慣れないうちは <code>Command</code> キーを押しながら <code>Text</code> をクリックしメニューを表示させます。</p>

<p><img src="http://grandbig.github.io/images/swiftui-1_3.png" alt="VStackなどを追加するためのメニュー" /></p>

<p>ここから <code>Embed in VStack</code> を選択すれば、コードに <code>VStack</code> を挿入してくれます。</p>

<h4>Textの装飾</h4>

<p>続いて、パーツの装飾方法について見ていきます。<br/>
今回は最もシンプルな例として、 <code>Text</code> を扱います。</p>

<p>GUIから編集する方法はシンプルで、</p>

<ol>
<li>コード上で対象パーツにカーソルをあわせる</li>
<li>プレビュー上で対象パーツを選択する</li>
</ol>


<p>のどちらかを実行することで、右メニューで編集が可能になります。</p>

<p><img src="http://grandbig.github.io/images/swiftui-1_4.png" alt="右メニューでTextの装飾" /></p>

<p>右メニューから、</p>

<ul>
<li>Textの文言</li>
<li>フォントの各種設定</li>
<li>Padding</li>
<li>Frame (Width / Height)</li>
</ul>


<p>がデフォルトで用意されており変更が可能です。</p>

<p>また、 <code>Add Modifier</code> から多種多様なプロパティ設定を追加することができます。<br/>
(単純な装飾に限らないようですが。。。)</p>

<p><img src="http://grandbig.github.io/images/swiftui-1_5.png" alt="Textに各種プロパティを設定" /></p>

<p>因みに、GUIから各種設定を追加すると、<br/>
<code>.foregroundColor(Color.blue)</code> のように省略なしの表記でコードに挿入される場合がありますが、<br/>
<code>.foregroundColor(.blue)</code> とすることも、もちろん可能です。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// こんな感じで省略表記は可能です。</span>
</span><span class='line'><span class="n">Text</span><span class="p">(</span><span class="s">&quot;Hello, World!&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">.</span><span class="n">font</span><span class="p">(.</span><span class="n">largeTitle</span><span class="p">)</span>
</span><span class='line'>    <span class="p">.</span><span class="n">fontWeight</span><span class="p">(.</span><span class="n">heavy</span><span class="p">)</span>
</span><span class='line'>    <span class="p">.</span><span class="n">foregroundColor</span><span class="p">(.</span><span class="n">blue</span><span class="p">)</span>
</span><span class='line'>    <span class="p">.</span><span class="n">padding</span><span class="p">(.</span><span class="n">all</span><span class="p">,</span> <span class="mf">20.0</span><span class="p">)</span>
</span><span class='line'>    <span class="p">.</span><span class="n">background</span><span class="p">(.</span><span class="n">black</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Buttonアクション</h4>

<p>最後にボタンをタップシた際のアクションの実装について簡単に触れておきましょう。<br/>
今回は、サンプルとして、「ボタンをタップした際にアラートを表示する」ことを試してみます。</p>

<p>実装は次の通りです。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">struct</span> <span class="nl">ContentView:</span> <span class="n">View</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">/// アラートの表示フラグ</span>
</span><span class='line'>    <span class="err">@</span><span class="n">State</span> <span class="n">private</span> <span class="n">var</span> <span class="n">isShown</span> <span class="o">=</span> <span class="n">true</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">var</span> <span class="nl">body:</span> <span class="n">some</span> <span class="n">View</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">VStack</span><span class="p">(</span><span class="nl">spacing:</span> <span class="mf">20.0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>            <span class="n">Text</span><span class="p">(</span><span class="s">&quot;Hello, World!&quot;</span><span class="p">)</span>
</span><span class='line'>            <span class="n">Button</span><span class="p">(</span><span class="nl">action:</span> <span class="p">{</span>
</span><span class='line'>                <span class="c1">// ボタンをタップすると、表示に切り替え</span>
</span><span class='line'>                <span class="n">self</span><span class="p">.</span><span class="n">isShown</span> <span class="o">=</span> <span class="n">true</span>
</span><span class='line'>            <span class="p">})</span> <span class="p">{</span>
</span><span class='line'>                <span class="n">Text</span><span class="p">(</span><span class="s">&quot;Button&quot;</span><span class="p">)</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>            <span class="c1">// isPresentedがtrueになった場合にアラートを表示する</span>
</span><span class='line'>            <span class="p">.</span><span class="n">alert</span><span class="p">(</span><span class="nl">isPresented:</span> <span class="n">$isShown</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>                <span class="n">Alert</span><span class="p">(</span><span class="nl">title:</span> <span class="n">Text</span><span class="p">(</span><span class="s">&quot;Tapped Button&quot;</span><span class="p">))</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>冒頭で <code>@State</code> 属性を用いて <code>isShown</code> フラグを定義することで、<br/>
<code>View</code> 内で扱うことが可能となり、そのプロパティの状態に従って描画を実行できるようになります。</p>

<p>ここでは、ボタンタップ時に発火される <code>Button(action: { ... })</code> で、<br/>
<code>isShown</code> フラグを切り替えます。<br/>
そして後続の <code>alert</code> で <code>isPresented</code> に <code>isShown</code> フラグを判定として利用することでアラートを表示します。</p>

<h3>まとめ</h3>

<p>さて、まずは、ほんの少しの表面だけ <code>SwiftUI</code> に触れてみました。<br/>
まだまだ複雑なアプリを開発するには練習が全然足りないため、継続して学びつつブログに書き起こせていければと思います。</p>

<p>と言ったところで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SwiftでiOS&Androidアプリを開発！？〜Scadeチュートリアル〜]]></title>
    <link href="http://grandbig.github.io/blog/2020/01/19/scade-hello-world/"/>
    <updated>2020-01-19T01:07:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2020/01/19/scade-hello-world</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>昨今、 <code>Flutter</code> や <code>React Native</code> などによるiOSアプリとAndroidアプリの同時作成が少しずつ現実的に実践されるようになってきており、<br/>
筆者的には、2010年代前半以来のリブームのように感じられる今日此頃です。</p>

<p>2010年代前半は、Facebookを筆頭に、最終的にはフルネイティブに舵を切り直すプロダクトが多かったイメージがあるのですが、<br/>
各種OSの浸透および安定化に伴い、今回の流れはある程度続く可能性があるのではと思わずにはいられません。</p>

<p>しかしながら、 <code>Flutter</code> は <code>Dart</code> というGoogle製の言語を利用し、<br/>
<code>React Native</code> は <code>JavaScript</code> および <code>React</code> の知識が必要になります。</p>

<p> 既に <code>Swift</code> でのiOSアプリの開発や <code>Kotlin</code> によるAndroidアプリの開発に慣れているエンジニアであれば、<br/>
 言語書式が比較的似ていることから、OSやIDEの違いさえ把握できれば学習コストは大幅に抑えられる可能性があります。</p>

<p> 一方で、それでは両OSアプリの同時作成の恩恵に預かることができないため、<br/>
 何か良いものがないかな〜と思っていたところ、<br/>
『 <a href="https://www.infoq.com/jp/news/2019/08/scade-swift-android-development/">Scadeは、Swiftを使用してAndroidアプリ開発を可能にすることを目指す</a>』という記事を見つけました。</p>

<p>これは面白そうだなということで、今回は<a href="https://www.scade.io/">Scade</a>について見てみたいと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>Scadeとは</h3>

<p>公式ホームページによると、 <code>Scade</code> とは『次世代のモバイルアプリの開発基盤』と言われています。</p>

<p>具体的にできることとしては、</p>

<ul>
<li><code>Swift</code> 言語でiOS&amp;Androidの両OSのアプリを開発できる</li>
<li><code>SCADE Simulator</code> という <code>Scade</code> アプリ内で提供されているシミュレータが利用できる</li>
<li><code>Xcode</code> や <code>Android Studio</code> で利用している各々専用のシミュレータも利用できる</li>
<li>GUI上でアプリをデザインすることができる</li>
</ul>


<p>などがあります。</p>

<p>Swift5にてABIの安定化を達成したこともあってか <code>Scade</code> 自体のバージョンも <code>v1.0</code> に到達しています。</p>

<p>しかしながら、現在、最新の <code>Scade</code> は <strong>Xcode11</strong> ( つまり <strong>Swift5.0</strong> )までしか対応しておらず、<br/>
お使いのMacで <code>Xcode11.3</code> が入っている場合は <code>Scade IDE</code> 上でのコンパイルに失敗してしまうため注意が必要です。</p>

<h3>チュートリアル &ndash; Hello World</h3>

<p>何はともあれ、 <code>Scade IDE</code> を使ってチュートリアルを進めてみましょう。<br/>
<code>Scade</code> のドキュメントは非常に充実しており、チュートリアルとして <code>Hello World</code> が用意されています。</p>

<p>参考：<a href="https://docs.scade.io/docshttps://docs.scade.io/docs">Scade: チュートリアル &ndash; Hello World</a></p>

<p>上記リンク先に動画付きで説明がなされているので、大筋で困ることはないと思います。<br/>
ただ、ドキュメントが古いのか、全く同じように作成することはできなかったため、紹介がてら使い方を見ていきたいと思います。</p>

<p>まず、ソフトウェアのDLページですが、<a href="https://www.scade.io/download/">こちら</a>になります。<br/>
※無料で利用できます。</p>

<h4>プロジェクトの作成</h4>

<p>続いて、プロジェクトの作成方法を説明します。</p>

<p>① ナビゲーションバーから、Scade Projectを選択する</p>

<p><img src="http://grandbig.github.io/images/scade_1.png" alt="Scadeプロジェクトを作成する" /></p>

<p>② プロジェクト名を決める</p>

<p><img src="http://grandbig.github.io/images/scade_2.png" alt="プロジェクト名を決める" /></p>

<p>これだけで、IDE上に②で指定した名前のプロジェクト名が作成されていることを確認できると思います。</p>

<p><img src="http://grandbig.github.io/images/scade_3.png" alt="IDE上に作成されたプロジェクト" /></p>

<h4>レイアウトの作成</h4>

<p><code>Scade</code> の特徴でも説明したように、 <code>Xcode</code> や <code>Android Studio</code> 同様にGUIからレイアウトを決めることが可能になっています。<br/>
今回は、『チュートリアルのHello World』ということで下図のようなレイアウトを作成します。</p>

<p><img src="http://grandbig.github.io/images/scade_4.png" alt="チュートリアルHello Worldのレイアウト" /></p>

<p>では説明していきましょう。</p>

<p>① main.pageファイルを開く</p>

<p><img src="http://grandbig.github.io/images/scade_5.png" alt="main.pageファイルを開く" /></p>

<p>② 右メニューのPaletteのWidgetsからLabelとButtonをドラッグ＆ドロップする</p>

<p><img src="http://grandbig.github.io/images/scade_6.png" alt="右メニューのPaletteのWidgetsからLabelとButtonをドラッグ＆ドロップする" /></p>

<p>③ 右メニューのPaletteのLayoutsからGrid1つとVertical2つをドラッグ＆ドロップする</p>

<p><img src="http://grandbig.github.io/images/scade_7.png" alt="右メニューのPaletteのLayoutsからGrid1つとVertical2つをドラッグ＆ドロップする" /></p>

<p>④ Grid内にVerticalを2つ子要素として追加し、それぞれのVerticalの子要素としてLabelとButtonを追加する</p>

<p><img src="http://grandbig.github.io/images/scade_8.png" alt="Grid内にVerticalを2つ子要素として追加し、それぞれのVerticalの子要素としてLabelとButtonを追加する" /></p>

<p>⑤ Gridの上下左右にレイアウトを設定する</p>

<p><img src="http://grandbig.github.io/images/scade_9.png" alt="Gridの上下左右にレイアウトを設定する " /></p>

<p>⑥ Label側のVerticalのレイアウトを設定する</p>

<p><img src="http://grandbig.github.io/images/scade_10.png" alt="Label側のVerticalのレイアウトを設定する" /></p>

<p>⑦ Button側のVerticalのレイアウトを設定する</p>

<p><img src="http://grandbig.github.io/images/scade_11.png" alt="Button側のVerticalのレイアウトを設定する" /></p>

<p>⑧ Labelのレイアウトと色を設定する</p>

<p><img src="http://grandbig.github.io/images/scade_12.png" alt="Labelのレイアウトと色を設定する" /></p>

<p>⑨ Buttonのレイアウトと色を設定する</p>

<p><img src="http://grandbig.github.io/images/scade_13.png" alt="Buttonのレイアウトと色を設定する" /></p>

<h4>実行処理の実装</h4>

<p>今回のチュートリアルでは、下記機能を持たせます。</p>

<ul>
<li>ボタンをタップしたときにラベルの文字列を変更する</li>
</ul>


<p>これを実現するために、 <code>main.page.swift</code> ファイルを開き、実行処理を書きます。</p>

<p><img src="http://grandbig.github.io/images/scade_14.png" alt="main.page.swiftファイルに実行処理を書く" /></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// main.page.swift</span>
</span><span class='line'><span class="n">import</span> <span class="n">ScadeKit</span>
</span><span class='line'>
</span><span class='line'><span class="n">class</span> <span class="nl">MainPageAdapter:</span> <span class="n">SCDLatticePageAdapter</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// page adapter initialization</span>
</span><span class='line'>  <span class="n">override</span> <span class="n">func</span> <span class="n">load</span><span class="p">(</span><span class="n">_</span> <span class="nl">path:</span> <span class="n">String</span><span class="p">)</span> <span class="p">{</span>        
</span><span class='line'>      <span class="n">super</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// ①</span>
</span><span class='line'>      <span class="n">let</span> <span class="n">button1</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">page</span><span class="o">!</span><span class="p">.</span><span class="n">getWidgetByName</span><span class="p">(</span><span class="s">&quot;button1&quot;</span><span class="p">)</span> <span class="n">as</span><span class="o">!</span> <span class="n">SCDWidgetsButton</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// ②</span>
</span><span class='line'>      <span class="n">button1</span><span class="p">.</span><span class="n">onClick</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">SCDWidgetsEventHandler</span><span class="p">{</span> <span class="n">_</span> <span class="k">in</span>
</span><span class='line'>      <span class="c1">// ③</span>
</span><span class='line'>          <span class="n">let</span> <span class="n">label</span> <span class="o">=</span>  <span class="n">self</span><span class="p">.</span><span class="n">page</span><span class="o">!</span><span class="p">.</span><span class="n">getWidgetByName</span><span class="p">(</span><span class="s">&quot;label1&quot;</span><span class="p">)</span> <span class="n">as</span><span class="o">!</span> <span class="n">SCDWidgetsLabel</span>
</span><span class='line'>          <span class="n">label</span><span class="p">.</span><span class="n">text</span> <span class="o">=</span> <span class="s">&quot;Swift on Android rocks&quot;</span>
</span><span class='line'>      <span class="p">})</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>処理内容を説明すると、下記の通りです。</p>

<p>① <code>getWidgetByName</code> を使ってボタン要素を取得しています<br/>
② <code>onClick</code> でボタンのタップを補足し、 <code>append</code> で処理を追加しています<br/>
③ ラベルの文字列変更のために <code>getWidgetByName</code> で取得したラベルの <code>text</code> に変更したい文字列を代入しています</p>

<h4>シミュレータでの確認方法</h4>

<p>左上からターゲットとなるプロジェクトを選択すると、実行するシミュレータを選択できます。</p>

<p><img src="http://grandbig.github.io/images/scade_15.png" alt="シミュレータでの確認方法" /></p>

<p>シミュレータを選択した後で、三角の実行ボタンを押下することでシミュレータを実行できます。</p>

<p><img src="http://grandbig.github.io/images/scade_16.png" alt="実行したシミュレータ" /></p>

<p>因みに、下図のように簡単にシミュレータの端末を変更する機能も備わっています。</p>

<p><img src="http://grandbig.github.io/images/scade_17.png" alt="シミュレータの端末を変更" /></p>

<h3>まとめ</h3>

<p>さて、如何でしたでしょうか？<br/>
SwiftでiOS/Androidアプリを同時に開発できるなんて夢のような企画ですよね！</p>

<p>ただ、</p>

<ul>
<li>Swiftバージョンへの追従ができていない</li>
<li><code>Scade</code> のIDEが <code>Eclipse</code> ベースでもっさりしている</li>
<li><code>Xcode</code> と同じ名称の部品があるので、同じように使えると思うと案外うまくいかない</li>
</ul>


<p>といった課題も感じました。</p>

<p>しかしながら、 <code>Scade</code> が実用レベルに到達するようになれば、 <code>Flutter</code> や <code>React Native</code> と同じく1つの選択肢として普及するようになる可能性もあることでしょう&hellip;</p>

<p>まあ、今回は単純に発想が面白かったということで紹介にとどめたいと思います。<br/>
と言ったところで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[iOS13におけるCoreLocationの変更点〜常に許可/使用中のみ許可/一時的に許可〜]]></title>
    <link href="http://grandbig.github.io/blog/2019/12/22/ios13-core-location/"/>
    <updated>2019-12-22T00:00:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/12/22/ios13-core-location</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>「<a href="https://qiita.com/advent-calendar/2019/ios-2">iOS #2 Advent Calendar 2019</a>」の22日目の記事です。</p>

<p>筆者がiOSアプリの開発を始めたのは、約8年ほど前でしょうか。<br/>
iOS史上でも初期から強力な機能だったこともあってか、当時はiOSアプリに位置情報の機能を載せることが流行っており、<br/>
筆者も漏れなく位置情報に関する調査や機能実装を永遠とこなしていた気がします。</p>

<p>今年でiOSもバージョン <code>13</code> となり、昔はなかった機能がたくさん登場しています。<br/>
新しい機能は当然、開発者の意欲を掻き立て、未知の世界をユーザに届けることに寄与することでしょう。<br/>
ただ、位置情報に人一倍強い思いがあることを自負している筆者ですから、<br/>
iOS13が出た今でも位置情報に関する仕様変更があることには感動もひとしおです。</p>

<p>今日は、iOS13からの位置情報に関する仕様変更を『<a href="https://developer.apple.com/videos/play/wwdc2019/705/">WWDC2019 &ndash; What&rsquo;s New in Core Location</a>』を元に紹介して1年を締めくくりたいと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>位置情報とプライバシーについて</h3>

<p>約8年前は位置情報に関する様々な国内での取り決め事がまだまだ調整途中だったかと思います。<br/>
ただし、当時から「電池の消耗が激しいこと」と同じくらい「自身の位置情報を知られたくない」というプライバシーに対する強い意識があった気がします。<br/>
(もちろん現在の方がプライバシー意識は劇的に高まっていると思います。)</p>

<p>一方で、企業側としては、位置情報を利用した新しい便利な体験をユーザに提供するために、<br/>
位置情報を積極的に許可してほしい気持ちがあったかと思います。</p>

<p>これまでiOSはそんなユーザとアプリ提供者側の双方の立場を考慮して改善を進めてきました。<br/>
例えば、初め「常に許可」「許可しない」の2つの選択肢しかなかったところに、<br/>
「使用中のみ許可」が加わったりといった取り組みです。</p>

<p>iOS13では、この取り組みが更に一歩進んだ形になります。</p>

<h3>位置情報の利用確認ダイアログに「常に許可」の選択肢がない</h3>

<p>iOS12までは、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">var</span> <span class="n">locationManager</span> <span class="o">=</span> <span class="n">CLLocationManager</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'><span class="n">locationManager</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span>
</span><span class='line'>
</span><span class='line'><span class="n">locationManager</span><span class="p">.</span><span class="n">requestAlwaysAuthorization</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>とした際に、</p>

<ul>
<li>「常に許可」</li>
<li>「使用中のみ許可」</li>
<li>「許可しない」</li>
</ul>


<p>の3つの選択肢を持つ確認ダイアログが表示されていました。</p>

<p>これがiOS13では、</p>

<ul>
<li>「使用中のみ許可」</li>
<li>「一時的に許可」</li>
<li>「許可しない」</li>
</ul>


<p>の3つの選択肢に変更となりました。<br/>
一見すると、アプリ開発者にとって辛い仕様に思えるのですが、「常に許可」がなくなったわけではありません。</p>

<p><strong>「常に許可」を求めるタイミングが変更になった</strong></p>

<p>というのが正しい解釈となります。</p>

<p>因みに、ここで「使用中のみ許可」を選択した場合、<br/>
<code>CLAuthorizationStatus</code> は実は <code>.authorizedAlways</code> になります。</p>

<h3>「常に許可」に変更するタイミング</h3>

<p>どういうことなのか詳しく説明しましょう。</p>

<p>先程の「使用中のみ許可 / 一時的に許可 / 許可しない」の3つの選択肢から <strong>「使用中のみ許可」</strong> を選択した場合、OSがユーザが忙しくない時を自動的に狙って、</p>

<ul>
<li>「使用中のみ許可のままにする」</li>
<li>「常に許可に変更する」</li>
</ul>


<p>かを問いかけてくれます。</p>

<p><img src="http://grandbig.github.io/images/ios13-core-location_1.jpg" alt="常に許可に変更する確認ダイアログ" /></p>

<p>ここで「常に許可に変更する」を選択して初めて、「常に許可」状態に設定することができます。</p>

<p>この仕様がアプリ提供元の企業やアプリ開発者に対して、何を問うているのでしょうか。</p>

<p>筆者は、</p>

<ul>
<li>ユーザに「アプリの利用価値を伝え、位置情報を利用することの有用性を体感してもらう」ことが必要</li>
<li>ユーザに「アプリで利用する位置情報は、プライバシーが守られていることを伝える」ことが必要</li>
</ul>


<p>という大前提に加えて、</p>

<ul>
<li>アプリが最大価値を発揮するのは、Foreground起動中であるべき</li>
<li>ユーザがアプリを初めて利用する場面は、必ずForeground起動であるはず</li>
</ul>


<p>という釘を差しているようにも捉えています。</p>

<p>後者は、OSや純正アプリによって担保されていますが、前者はAppleの審査やそもそもの規約があるものの、作り手の仕組みにも依存する側面は完璧には拭えないでしょう。</p>

<p>因みに、<br/>
「使用中のみ許可のままにする / 常に許可に変更する」かの確認は1度のみであるため、<br/>
これを逃すと、ユーザが自主的に設定画面から設定を変更しない限り「常に許可」に変更する導線がなくなります。</p>

<h3>「使用中のみ許可」の変更点</h3>

<p>iOS12ではユーザが「使用中のみ許可」を選択した場合、下記のようにできることが限られていました。</p>

<ul>
<li>位置情報を取得すること</li>
<li>iBeaconを監視すること</li>
<li>Backgroundで位置情報を継続して取得すること</li>
</ul>


<p>もし、ユーザが「常に許可」を選択すれば、上記に加えて下記も利用することができるようになります。</p>

<ul>
<li>位置情報の取得の「開始」</li>
<li>大幅変更位置情報サービスを利用すること</li>
<li>領域監視を利用すること</li>
<li>滞在情報監視を利用すること</li>
</ul>


<p><img src="http://grandbig.github.io/images/ios13-core-location_2.png" alt="iOS12: 常に許可/使用中のみ許可の違い表" /><br/>
※WWDC2019の資料から該当箇所を抜粋して紹介させて頂いています。
<a href="https://developer.apple.com/videos/play/wwdc2019/705/">https://developer.apple.com/videos/play/wwdc2019/705/</a></p>

<p>このため、アプリ開発者は、『どんな機能をアプリに持たせたいか』次第で「常に許可」「使用中のみ許可」のどちらをユーザに求めるべきかを決めていました。</p>

<p>これがiOS13では非常にシンプルになりました。</p>

<p><img src="http://grandbig.github.io/images/ios13-core-location_3.png" alt="iOS13: 常に許可/使用中のみ許可の違い表" /><br/>
※WWDC2019の資料から該当箇所を抜粋して紹介させて頂いています。
<a href="https://developer.apple.com/videos/play/wwdc2019/705/">https://developer.apple.com/videos/play/wwdc2019/705/</a></p>

<p>つまり、機能間の差異をなくして、『ユーザに求めた許可状態に即したタイミングで機能を提供する』ことが可能になったということです。</p>

<p>「常に許可」な状態はアプリがForeground/Background起動に関わらず、常に機能を利用できる状態であることがわかるかと思いますが、<br/>
「使用中のみ許可」な状態とは具体的にどんな状態を指すのでしょうか。</p>

<p>ここで簡単なサンプルアプリを作成して説明してみます。</p>

<h3>「使用中のみ許可」の「使用中」の状態とは</h3>

<p>下記のような簡単なサンプルアプリを作成してみました。</p>

<h4>機能</h4>

<p>実験のためのアプリなので、機能は下記のみです。</p>

<ul>
<li>「常に許可」設定をユーザに求める</li>
<li>「使用中のみ許可」設定をユーザに求める</li>
<li>BackgroundモードはOFFで位置情報取得を開始する</li>
<li>BackgroundモードはOFFで位置情報取得を終了する</li>
<li>BackgroundモードはONで位置情報取得を開始する</li>
<li>BackgroundモードはONで位置情報取得を終了する</li>
</ul>


<h4>画面キャプチャ</h4>

<p>実際の画面は下記の通りです。</p>

<p><img src="http://grandbig.github.io/images/ios13-core-location_1.jpg" alt="サンプルアプリの画面" /></p>

<h4>ソースコード</h4>

<p>実際のソースコードは下記の通りです。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">import</span> <span class="n">UIKit</span>
</span><span class='line'><span class="n">import</span> <span class="n">CoreLocation</span>
</span><span class='line'>
</span><span class='line'><span class="n">class</span> <span class="nl">ViewController:</span> <span class="n">UIViewController</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// MARK: - Properties</span>
</span><span class='line'>  <span class="n">var</span> <span class="n">locationManager</span> <span class="o">=</span> <span class="n">CLLocationManager</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// MARK: - Lifecycle</span>
</span><span class='line'>  <span class="n">override</span> <span class="n">func</span> <span class="n">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">super</span><span class="p">.</span><span class="n">viewDidLoad</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">configureLocationManager</span><span class="p">()</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// MARK: - Configure</span>
</span><span class='line'>  <span class="n">private</span> <span class="n">func</span> <span class="n">configureLocationManager</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">desiredAccuracy</span> <span class="o">=</span> <span class="n">kCLLocationAccuracyBestForNavigation</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">activityType</span> <span class="o">=</span> <span class="n">CLActivityType</span><span class="p">.</span><span class="n">fitness</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">pausesLocationUpdatesAutomatically</span> <span class="o">=</span> <span class="n">false</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// MARK: - IBActions</span>
</span><span class='line'>  <span class="err">@</span><span class="kt">IBAction</span> <span class="n">private</span> <span class="n">func</span> <span class="n">didTapRequestAlwaysAuthorizationButton</span><span class="p">(</span><span class="n">_</span> <span class="nl">sender:</span> <span class="n">Any</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">requestAlwaysAuthorization</span><span class="p">()</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="err">@</span><span class="kt">IBAction</span> <span class="n">private</span> <span class="n">func</span> <span class="n">didTapRequestWhenInUseAuthorizationButton</span><span class="p">(</span><span class="n">_</span> <span class="nl">sender:</span> <span class="n">Any</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">requestWhenInUseAuthorization</span><span class="p">()</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="err">@</span><span class="kt">IBAction</span> <span class="n">private</span> <span class="n">func</span> <span class="n">didTapStartUpdatingLocationButton</span><span class="p">(</span><span class="n">_</span> <span class="nl">sender:</span> <span class="n">Any</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">startUpdatingLocation</span><span class="p">()</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="err">@</span><span class="kt">IBAction</span> <span class="n">private</span> <span class="n">func</span> <span class="n">didTapStopUpdatingLocationButton</span><span class="p">(</span><span class="n">_</span> <span class="nl">sender:</span> <span class="n">Any</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">stopUpdatingLocation</span><span class="p">()</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="err">@</span><span class="kt">IBAction</span> <span class="n">private</span> <span class="n">func</span> <span class="n">didTapStartUpdatingLocationAllowsBackgroundButton</span><span class="p">(</span><span class="n">_</span> <span class="nl">sender:</span> <span class="n">Any</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">allowsBackgroundLocationUpdates</span> <span class="o">=</span> <span class="n">true</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">startUpdatingLocation</span><span class="p">()</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="err">@</span><span class="kt">IBAction</span> <span class="n">private</span> <span class="n">func</span> <span class="n">didTapStopUpdatingLocationAllowsBackgroundButton</span><span class="p">(</span><span class="n">_</span> <span class="nl">sender:</span> <span class="n">Any</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">allowsBackgroundLocationUpdates</span> <span class="o">=</span> <span class="n">false</span>
</span><span class='line'>    <span class="n">locationManager</span><span class="p">.</span><span class="n">stopUpdatingLocation</span><span class="p">()</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// MARK: - CLLocationManagerDelegate</span>
</span><span class='line'><span class="n">extension</span> <span class="nl">ViewController:</span> <span class="n">CLLocationManagerDelegate</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">func</span> <span class="n">locationManager</span><span class="p">(</span><span class="n">_</span> <span class="nl">manager:</span> <span class="n">CLLocationManager</span><span class="p">,</span> <span class="n">didChangeAuthorization</span> <span class="nl">status:</span> <span class="n">CLAuthorizationStatus</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">switch</span> <span class="n">status</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="nl">notDetermined:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;未決定の場合&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="nl">authorizedAlways:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;常に許可した場合&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="nl">authorizedWhenInUse:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;使用中のみ許可した場合&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="nl">denied:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;許可しない場合&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="k">restricted</span><span class="o">:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;位置情報を利用できない制限がある場合&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="err">@</span><span class="n">unknown</span> <span class="k">default</span><span class="o">:</span>
</span><span class='line'>      <span class="n">fatalError</span><span class="p">(</span><span class="s">&quot;CLAuthorizationStatusの種類が増えているので、条件を見直す必要があります。&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">func</span> <span class="n">locationManager</span><span class="p">(</span><span class="n">_</span> <span class="nl">manager:</span> <span class="n">CLLocationManager</span><span class="p">,</span> <span class="n">didUpdateLocations</span> <span class="nl">locations:</span> <span class="p">[</span><span class="n">CLLocation</span><span class="p">])</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">guard</span> <span class="n">let</span> <span class="n">location</span> <span class="o">=</span> <span class="n">locations</span><span class="p">.</span><span class="n">first</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
</span><span class='line'>    <span class="n">print</span><span class="p">(</span><span class="s">&quot;location: \(location.coordinate)&quot;</span><span class="p">)</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h4>実験</h4>

<p>では、具体的に「使用中」状態を見るための実験をしてきましょう。</p>

<h5>実験１：BackgroundモードOFFの位置情報取得</h5>

<p>まずは、「使用中のみ許可」設定をしてから、 <strong>BackgroundモードOFFの位置情報取得開始</strong> を実行してみましょう。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// 使用中のみ許可を求める</span>
</span><span class='line'><span class="n">locationManager</span><span class="p">.</span><span class="n">requestWhenInUseAuthorization</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// 位置情報の取得を開始する</span>
</span><span class='line'><span class="n">locationManager</span><span class="p">.</span><span class="n">startUpdatingLocation</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>この結果、アプリがForeground起動の場合、 <code>didUpdateLocations</code> が呼び出され、最新の位置情報を取得できます。<br/>
しかし、アプリをBackground起動にした場合、 <code>didUpdateLocations</code> が呼び出されることはなくなりました。</p>

<p>※タイミングによっては呼び出されることがありますが、それはアプリはBackgroundで数秒Foregroundと同じ扱いになるOS仕様のためです。</p>

<p>上記の例では、この <strong>Foreground起動中が「使用中」</strong> に当たります。</p>

<p>そのため、再び、Foreground起動にアプリを戻すと「使用中」状態に戻るため、 <code>didUpdateLocations</code> が呼び出されるようになります。</p>

<h5>実験２：BackgroundモードONの位置情報取得</h5>

<p>続いて、 <strong>BackgroundモードONの位置情報取得開始</strong> を実行してみましょう。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// 使用中のみ許可を求める</span>
</span><span class='line'><span class="n">locationManager</span><span class="p">.</span><span class="n">requestWhenInUseAuthorization</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Backgroundでの位置情報の更新を許可する</span>
</span><span class='line'><span class="n">locationManager</span><span class="p">.</span><span class="n">allowsBackgroundLocationUpdates</span> <span class="o">=</span> <span class="n">true</span>
</span><span class='line'><span class="c1">// 位置情報の取得を開始する</span>
</span><span class='line'><span class="n">locationManager</span><span class="p">.</span><span class="n">startUpdatingLocation</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>このとき、アプリがForeground起動の場合、 <code>didUpdateLocations</code> が呼び出され、最新の位置情報を取得できます。<br/>
それに加えて、アプリをBackground起動にした場合でも、 <code>didUpdateLocations</code> が呼び出され続けます。</p>

<p>この場合の例では、 <strong>ForegroundおよびBackgroundともに「使用中」</strong> に当たります。</p>

<p>Backgroundで位置情報を利用していることが、ユーザにも伝わるように、ステータスバーの左側に青色で囲まれた矢印マークが表示されます。</p>

<p><img src="http://grandbig.github.io/images/ios13-core-location_5.jpg" alt="Backgroundで位置情報を利用している状態" /></p>

<h3>「使用中のみ許可」ユーザが使用中でない場合へのアプローチ</h3>

<p>これで <strong>使用中</strong> とはどういった状態を指すのか、おわかり頂けたかと思います。</p>

<p>基本的にはアプリはユーザが利用したい時に利用することになると思いますが、<br/>
アプリ提供元がアプリを利用する最適な場面を知らせたいといったこともあるでしょう。</p>

<p>そういった時には、 <code>UNLocationNotificationTrigger</code> を使ったジオフェンスによるローカルプッシュが役に立つかもしれません。</p>

<p>特定の場所にジオフェンスを仕掛けておくことで、<br/>
領域侵入 or 領域退出時にローカルプッシュでユーザを気づかせ、<br/>
アプリを起動してもらうことで <strong>使用中</strong> 状態に誘導し、更なる価値提供を狙えることでしょう。</p>

<p>参考までに <code>UNLocationNotificationTrigger</code> の利用方法も記載しておきます。</p>

<p>まずは、 <code>AppDelegate.swift</code> で最低限の準備をしておきましょう。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// AppDelegate.swift</span>
</span><span class='line'><span class="n">class</span> <span class="nl">AppDelegate:</span> <span class="n">UIResponder</span><span class="p">,</span> <span class="n">UIApplicationDelegate</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">func</span> <span class="n">application</span><span class="p">(</span><span class="n">_</span> <span class="nl">application:</span> <span class="n">UIApplication</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span> <span class="nl">launchOptions:</span> <span class="p">[</span><span class="n">UIApplication</span><span class="p">.</span><span class="nl">LaunchOptionsKey:</span> <span class="n">Any</span><span class="p">]</span><span class="o">?</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">let</span> <span class="n">center</span> <span class="o">=</span> <span class="n">UNUserNotificationCenter</span><span class="p">.</span><span class="n">current</span><span class="p">()</span>
</span><span class='line'>    <span class="n">center</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// 通知の許可を求める</span>
</span><span class='line'>    <span class="n">center</span><span class="p">.</span><span class="n">requestAuthorization</span><span class="p">(</span><span class="nl">options:</span> <span class="p">[.</span><span class="n">sound</span><span class="p">,</span> <span class="p">.</span><span class="n">alert</span><span class="p">,</span> <span class="p">.</span><span class="n">badge</span><span class="p">])</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span> <span class="k">in</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">true</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// MARK: - UNUserNotificationCenterDelegate</span>
</span><span class='line'><span class="n">extension</span> <span class="nl">AppDelegate:</span> <span class="n">UNUserNotificationCenterDelegate</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// 未起動 or Background起動時にローカルプッシュを受信した場合</span>
</span><span class='line'>  <span class="n">func</span> <span class="n">userNotificationCenter</span><span class="p">(</span><span class="n">_</span> <span class="nl">center:</span> <span class="n">UNUserNotificationCenter</span><span class="p">,</span> <span class="n">didReceive</span> <span class="nl">response:</span> <span class="n">UNNotificationResponse</span><span class="p">,</span> <span class="n">withCompletionHandler</span> <span class="nl">completionHandler:</span> <span class="err">@</span><span class="n">escaping</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="n">Void</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">completionHandler</span><span class="p">()</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// Foreground起動時にローカルプッシュを受信した場合</span>
</span><span class='line'>  <span class="n">func</span> <span class="n">userNotificationCenter</span><span class="p">(</span><span class="n">_</span> <span class="nl">center:</span> <span class="n">UNUserNotificationCenter</span><span class="p">,</span> <span class="n">willPresent</span> <span class="nl">notification:</span> <span class="n">UNNotification</span><span class="p">,</span> <span class="n">withCompletionHandler</span> <span class="nl">completionHandler:</span> <span class="err">@</span><span class="n">escaping</span> <span class="p">(</span><span class="n">UNNotificationPresentationOptions</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Void</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">completionHandler</span><span class="p">(.</span><span class="n">alert</span><span class="p">)</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>続いて、ジオフェンスを設定します。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// ViewController.swift</span>
</span><span class='line'><span class="n">class</span> <span class="nl">ViewController:</span> <span class="n">UIViewController</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'>    <span class="n">private</span> <span class="n">func</span> <span class="n">configureLocationNotificationTrigger</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">content</span> <span class="o">=</span> <span class="n">UNMutableNotificationContent</span><span class="p">()</span>
</span><span class='line'>        <span class="n">content</span><span class="p">.</span><span class="n">title</span> <span class="o">=</span> <span class="s">&quot;お知らせ！&quot;</span>
</span><span class='line'>        <span class="n">content</span><span class="p">.</span><span class="n">body</span> <span class="o">=</span> <span class="s">&quot;今、アプリを利用するとお得な情報をGETできます！&quot;</span>
</span><span class='line'>        <span class="n">content</span><span class="p">.</span><span class="n">sound</span> <span class="o">=</span> <span class="n">UNNotificationSound</span><span class="p">.</span><span class="k">default</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">let</span> <span class="n">center</span> <span class="o">=</span> <span class="n">CLLocationCoordinate2D</span><span class="p">(</span><span class="nl">latitude:</span> <span class="mf">35.0</span><span class="p">,</span> <span class="nl">longitude:</span> <span class="mf">139.7</span><span class="p">)</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">region</span> <span class="o">=</span> <span class="n">CLCircularRegion</span><span class="p">(</span><span class="nl">center:</span> <span class="n">center</span><span class="p">,</span> <span class="nl">radius:</span> <span class="mi">200</span><span class="p">,</span> <span class="nl">identifier:</span> <span class="s">&quot;特定の場所&quot;</span><span class="p">)</span>
</span><span class='line'>        <span class="n">region</span><span class="p">.</span><span class="n">notifyOnEntry</span> <span class="o">=</span> <span class="n">true</span>
</span><span class='line'>        <span class="n">region</span><span class="p">.</span><span class="n">notifyOnExit</span> <span class="o">=</span> <span class="n">true</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">trigger</span> <span class="o">=</span> <span class="n">UNLocationNotificationTrigger</span><span class="p">(</span><span class="nl">region:</span> <span class="n">region</span><span class="p">,</span> <span class="nl">repeats:</span> <span class="n">true</span><span class="p">)</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">request</span> <span class="o">=</span> <span class="n">UNNotificationRequest</span><span class="p">(</span><span class="nl">identifier:</span> <span class="s">&quot;ジオフェンス&quot;</span><span class="p">,</span> <span class="nl">content:</span> <span class="n">content</span><span class="p">,</span> <span class="nl">trigger:</span> <span class="n">trigger</span><span class="p">)</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">notificationCenter</span> <span class="o">=</span> <span class="n">UNUserNotificationCenter</span><span class="p">.</span><span class="n">current</span><span class="p">()</span>
</span><span class='line'>        <span class="n">notificationCenter</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// MARK: - CLLocationManagerDelegate</span>
</span><span class='line'><span class="n">extension</span> <span class="nl">ViewController:</span> <span class="n">CLLocationManagerDelegate</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">func</span> <span class="n">locationManager</span><span class="p">(</span><span class="n">_</span> <span class="nl">manager:</span> <span class="n">CLLocationManager</span><span class="p">,</span> <span class="n">didChangeAuthorization</span> <span class="nl">status:</span> <span class="n">CLAuthorizationStatus</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">switch</span> <span class="n">status</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="nl">notDetermined:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;未決定の場合&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="nl">authorizedAlways:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;常に許可した場合&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="nl">authorizedWhenInUse:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;使用中のみ許可した場合&quot;</span><span class="p">)</span>
</span><span class='line'>      <span class="n">configureLocationNotificationTrigger</span><span class="p">()</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="nl">denied:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;許可しない場合&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="k">case</span> <span class="p">.</span><span class="k">restricted</span><span class="o">:</span>
</span><span class='line'>      <span class="n">print</span><span class="p">(</span><span class="s">&quot;位置情報を利用できない制限がある場合&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="err">@</span><span class="n">unknown</span> <span class="k">default</span><span class="o">:</span>
</span><span class='line'>      <span class="n">fatalError</span><span class="p">(</span><span class="s">&quot;CLAuthorizationStatusの種類が増えているので、条件を見直す必要があります。&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>上記のように、「使用中のみ許可」の場合にのみ <code>UNLocationNotificationTrigger</code> を仕掛けておくことも一つの手です。</p>

<p>「常に許可」は既にユーザから最大限の承諾を得ているため、更なる価値を届けるためのプッシュ通知という意味では特に意味を持たないためです。</p>

<h3>「一時的に許可」とは</h3>

<p>さて、ここまで「常に許可」「使用中のみ許可」について説明してきましたが、<br/>
iOS13からは「一時的に許可」という選択肢が新たに追加になりました。</p>

<p>「一時的に許可」とは文字通り、 <strong>その時の使用中の間のみ許可をする</strong> ということです。<br/>
よって、 <code>CLAuthorizationStatus</code> も <code>.authorizedWhenInUse</code> になります。</p>

<p>また、 <strong>その時の使用中の間のみ許可する</strong> の「使用中のみ」は、「使用中のみ許可」と同じ定義になります。</p>

<p>つまり、</p>

<ul>
<li><code>allowsBackgroundLocationUpdates=false</code> で <code>locationManager.startUpdatingLocation()</code> するとForeground起動中が「使用中」に当たります</li>
<li><code>allowsBackgroundLocationUpdates=true</code> で <code>locationManager.startUpdatingLocation()</code> するとBackground起動中も「使用中」に当たります</li>
</ul>


<p>ということです。</p>

<p><strong>その時の使用中の間のみ許可する</strong> ため、「使用中」状態が終了したタイミングで <strong>「未設定」状態に戻ります</strong> 。<br/>
実際に、 <code>CLAuthorizationStatus</code> も <code>.notDetermined</code> に戻ります。</p>

<p>一度「未設定」状態に戻ると、改めて位置情報の利用許可を求めなければ、位置情報サービスを利用することができません。</p>

<p>この「改めて位置情報の利用許可を求めるタイミング」ですが、アプリのフローに密接に結びつくため、非常に重要です。<br/>
全く関係のないタイミングで位置情報の利用許可を求めると、ユーザに不信感を持たれたり、煩わしさから離れていってしまう可能性もあるでしょう。</p>

<p>では、最適なタイミングとはいつになるのでしょうか？<br/>
それは、ユーザが再び「位置情報を利用する機能を使いたい」と思ったタイミングになります。</p>

<p>例えば、レストランの検索アプリの場合、「検索開始ボタンをタップしたタイミング」となるでしょう。</p>

<p>大事なことは、<br/>
<strong>『適切なタイミングで、ユーザが位置情報サービスを承諾するまで問いかける導線を用意しておく』</strong><br/>
ということになります。</p>

<p>因みに、「一時的に許可」を選択した後で、『設定アプリ > プライバシー > 位置情報サービス』を見ると、 <strong>『次回確認』</strong> が設定されています。</p>

<p><img src="http://grandbig.github.io/images/ios13-core-location_6.jpg" alt="設定 &gt; プライバシー &gt; 位置情報サービスの設定状態" /></p>

<h3>まとめ</h3>

<p>さて如何でしたでしょうか。<br/>
iOS13で仕様がシンプルになったからこそ、「位置情報サービスの利用許可を求める最適なタイミング」をしっかりと考える必要が出てきました。<br/>
また、この仕様変更は、アプリ提供元だけでなくユーザにとっても、わかりやすい仕様になったと言えるのではないでしょうか。</p>

<p>昔からある機能で慣れ親しみがあるものの、その機能全てがユーザに受け入れられているわけではありません。<br/>
少なからず、デメリットを感じてしまう側面もあることでしょう。<br/>
でも、だからこそ、継続して安心安全な機能提供をブラッシュアップしていくことは非常に大切なのでしょう。</p>

<p>我々、アプリ開発者もそういったことを十分に理解した上で、より良い形でユーザに価値を提供し続けていけると良いですね。</p>

<p>といったところで本日はここまで。</p>

<h3>参考</h3>

<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2019/705/">WWDC2019 &ndash; What&rsquo;s New in Core Location</a></li>
</ul>


<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Xcode10以降からは、CocoaPods v1.7.0以上を使ってInput/Output File Listsを活用しよう！]]></title>
    <link href="http://grandbig.github.io/blog/2019/11/23/cocoapods-1-7/"/>
    <updated>2019-11-23T23:05:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/11/23/cocoapods-1-7</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p><code>CocoaPods</code> はiOSアプリエンジニアであれば誰もが知るライブラリ管理ツールだと思いますが、<br/>
<code>v1.7.0</code> 以降では <code>xcfilelist</code> を <code>Input/Output File Lists</code> で指定できるようになりました。<br/>
( <a href="http://blog.cocoapods.org/CocoaPods-1.7.0-beta/#.xcfilelist-support">CocoaPods Blog &ndash; CocoaPods 1.7.0 Beta!</a> )</p>

<p><code>Input/Output File Lists</code> とはXcode10で追加された <code>Input / Output Files</code> に代わる、<br/>
<code>xcfilelist</code> をパスとして指定できる新たな枠組みです。</p>

<p>既に <code>v1.8.4</code> までリリースされており、Xcodeも11まで上がっているので今更感はありますが、気になったので少し具体的に差分を見てみました。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>CocoaPodsのバージョン比較のためにPodfileを用意</h3>

<p><code>CocoaPods</code> の『 <code>v1.7.0</code> 未満のバージョン』と『 <code>v1.7.0</code> 以上のバージョン』で比較するため、 <code>Podfile</code> を用意します。<br/>
因みに、今回の比較で利用したそれぞれのバージョンは下記の通りです。</p>

<ul>
<li><code>v1.7.0</code> 未満のバージョン： <code>v1.6.1</code></li>
<li><code>v1.7.0</code> 以上のバージョン： <code>v1.8.4</code></li>
</ul>


<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="cp"># Podfile</span>
</span><span class='line'><span class="n">use_frameworks</span><span class="o">!</span>
</span><span class='line'><span class="n">platform</span> <span class="o">:</span><span class="n">ios</span><span class="p">,</span> <span class="err">&#39;</span><span class="mf">13.0</span><span class="err">&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="n">target</span> <span class="s">&quot;HogeHogeSample&quot;</span> <span class="k">do</span>
</span><span class='line'>  <span class="err">#</span> <span class="n">Normal</span> <span class="n">libraries</span>
</span><span class='line'>  <span class="n">pod</span> <span class="err">&#39;</span><span class="n">Moya</span><span class="err">&#39;</span>
</span><span class='line'>  <span class="n">pod</span> <span class="err">&#39;</span><span class="n">ReSwift</span><span class="err">&#39;</span>
</span><span class='line'>  <span class="n">pod</span> <span class="err">&#39;</span><span class="n">ReSwiftThunk</span><span class="err">&#39;</span>
</span><span class='line'>  <span class="n">pod</span> <span class="err">&#39;</span><span class="n">PromiseKit</span><span class="err">&#39;</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">abstract_target</span> <span class="err">&#39;</span><span class="n">Tests</span><span class="err">&#39;</span> <span class="k">do</span>
</span><span class='line'>    <span class="n">target</span> <span class="s">&quot;HogeHogeSampleTests&quot;</span>
</span><span class='line'>    <span class="n">target</span> <span class="s">&quot;HogeHogeSampleUITests&quot;</span>
</span><span class='line'>  <span class="n">end</span>
</span><span class='line'><span class="n">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>それでは早速比較をしてみましょう。</p>

<h3>v1.6.1の場合</h3>

<p>まずは <code>v1.6.1</code> の方で <code>pod install</code> を実行してみます。<br/>
インストール完了後、 <code>Xcode</code> の対象ターゲットから <code>Build Phases</code> > <code>[CP] Embed Pods Frameworks</code> を開いてみましょう。</p>

<p><img src="http://grandbig.github.io/images/cocoapods_1.png" alt="対象ターゲットのBuild Phases(v1.6.1)" /></p>

<p>設定状態は、</p>

<ul>
<li>実行シェルスクリプトとして、 <code>"${PODS_ROOT}/Target Support Files/Pods-HogeHogeSample/Pods-HogeHogeSample-frameworks.sh"</code> が指定されている</li>
<li><code>Input Files</code> と <code>Output Files</code> にそれぞれパスが指定されている</li>
</ul>


<p>となっています。</p>

<h3>v1.8.4の場合</h3>

<p>次に <code>v1.8.4</code> の方で <code>pod install</code> を実行してみます。<br/>
同じく <code>Build Phases</code> > <code>[CP] Embed Pods Frameworks</code> を開いてみましょう。</p>

<p><img src="http://grandbig.github.io/images/cocoapods_2.png" alt="対象ターゲットのBuild Phases(v1.8.4)" /></p>

<p>設定状態は、</p>

<ul>
<li>実行シェルスクリプトとして、 <code>"${PODS_ROOT}/Target Support Files/Pods-HogeHogeSample/Pods-HogeHogeSample-frameworks.sh"</code> が指定されている</li>
<li><code>Input File Lists</code> と <code>Output File Lists</code> にそれぞれパスが指定されている</li>
</ul>


<p>となっています。</p>

<h3>比較結果</h3>

<p>比較してみると、<br/>
<code>v1.6.1</code> で利用していた <code>Input / Output Files</code> から <code>v1.8.4</code> では <code>Input / Output File Lists</code> に変わっていることがわかると思います。</p>

<p>この <code>Input / Output File Lists</code> で指定されているファイルの中身を見てみましょう。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// Input File Listsに指定されたファイルの中身</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">PODS_ROOT</span><span class="p">}</span><span class="o">/</span><span class="n">Target</span> <span class="n">Support</span> <span class="n">Files</span><span class="o">/</span><span class="n">Pods</span><span class="o">-</span><span class="n">HogeHogeSample</span><span class="o">/</span><span class="n">Pods</span><span class="o">-</span><span class="n">HogeHogeSample</span><span class="o">-</span><span class="n">frameworks</span><span class="p">.</span><span class="n">sh</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">BUILT_PRODUCTS_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">Alamofire</span><span class="o">/</span><span class="n">Alamofire</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">BUILT_PRODUCTS_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">Moya</span><span class="o">/</span><span class="n">Moya</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">BUILT_PRODUCTS_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">PromiseKit</span><span class="o">/</span><span class="n">PromiseKit</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">BUILT_PRODUCTS_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">ReSwift</span><span class="o">/</span><span class="n">ReSwift</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">BUILT_PRODUCTS_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">ReSwiftThunk</span><span class="o">/</span><span class="n">ReSwiftThunk</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">BUILT_PRODUCTS_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">Result</span><span class="o">/</span><span class="n">Result</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Output File Listsに指定されたファイルの中身</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">TARGET_BUILD_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">$</span><span class="p">{</span><span class="n">FRAMEWORKS_FOLDER_PATH</span><span class="p">}</span><span class="o">/</span><span class="n">Alamofire</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">TARGET_BUILD_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">$</span><span class="p">{</span><span class="n">FRAMEWORKS_FOLDER_PATH</span><span class="p">}</span><span class="o">/</span><span class="n">Moya</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">TARGET_BUILD_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">$</span><span class="p">{</span><span class="n">FRAMEWORKS_FOLDER_PATH</span><span class="p">}</span><span class="o">/</span><span class="n">PromiseKit</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">TARGET_BUILD_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">$</span><span class="p">{</span><span class="n">FRAMEWORKS_FOLDER_PATH</span><span class="p">}</span><span class="o">/</span><span class="n">ReSwift</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">TARGET_BUILD_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">$</span><span class="p">{</span><span class="n">FRAMEWORKS_FOLDER_PATH</span><span class="p">}</span><span class="o">/</span><span class="n">ReSwiftThunk</span><span class="p">.</span><span class="n">framework</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">TARGET_BUILD_DIR</span><span class="p">}</span><span class="o">/</span><span class="n">$</span><span class="p">{</span><span class="n">FRAMEWORKS_FOLDER_PATH</span><span class="p">}</span><span class="o">/</span><span class="n">Result</span><span class="p">.</span><span class="n">framework</span>
</span></code></pre></td></tr></table></div></figure>


<p>結果、これまで <code>Input / Output Files</code> にベタッと指定していたパスと同じであることがわかると思います。<br/>
因みに、上記中身が記載された <code>xcfilelist</code> はプロジェクト自体には組み込まれていません。<br/>
ファイルからパスが読み取れさえすれば問題ないので、そのままなのでしょう。</p>

<p>ただし、 <code>Git</code> で <code>xcworkspace</code> も管理する場合は、<br/>
この <code>xcfilelist</code> も <code>Git</code> にアップロードする必要がありますので注意しましょう。<br/>
(プロジェクト内でファイル指定されているので、存在しないとビルドが通らなくなります。)</p>

<h3>まとめ</h3>

<p><code>CocoaPods</code> 自体が優秀なため、バージョンアップして <code>pod install</code> を実行することで、<br/>
自動的に整合性を取ってくれるはずなので、意識する必要はそんなにないかもしれません。</p>

<p>しかし、こうした変更を理解しておくことで、開発中の想定外の差分に気づけたりすることもあります。<br/>
知っておいて損はないことなので、細かいところも注視していけると良いな〜というところで本日はここまで。</p>

<p>参考： <a href="http://blog.cocoapods.org/CocoaPods-1.7.0-beta/">CocoaPods Blog &ndash; CocoaPods 1.7.0 Beta!</a></p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[iOS13で密かに追加されたUserDefaultsへの保存制限]]></title>
    <link href="http://grandbig.github.io/blog/2019/10/19/ios13-userdefaults/"/>
    <updated>2019-10-19T16:49:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/10/19/ios13-userdefaults</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>先日ふと調べ物をしていたところ、<br/>
<a href="https://forums.developer.apple.com/thread/121527">iOS 13 &ndash; Attempting to store >= 4194304 bytes of data in CFPreferences/NSUserDefaults on this platform is invalid</a>
という何やら気になる話を見つけました。</p>

<p>どうも <code>iOS13</code> からは、 <code>UserDefaults</code> に保存できる容量が <code>4194304 bytes</code> と制限が追加されたようだと言うのです。<br/>
これは実際にやってみるっきゃない！ということで実験をしてみました。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>実験</h3>

<p>実際に筆者が実行した実験内容は下記の通りです。</p>

<ul>
<li><code>iOS13.1.3</code> の端末を用いて、 <code>UserDefaults</code> への保存の合計容量が <code>4194304 bytes</code> を超過すると怒られるのか</li>
</ul>


<p>それでは実際に見ていきましょう。</p>

<h4>実験に利用したソースコード</h4>

<p>制限が <code>4194304 bytes</code> であるため、文字列を保存するのは手間がかかりそうだったので、<br/>
<code>Assets.xcassets</code> に予め用意した画像を <code>Data</code> 型に変換後、 <code>UserDefaults</code> に保存することにしました。</p>

<p>用意した画像は下記になります。</p>

<ul>
<li>サイズが <code>262KB</code> の画像</li>
</ul>


<p>実験に利用したソースコードは下記になります。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// ViewController.swift</span>
</span><span class='line'><span class="n">import</span> <span class="n">UIKit</span>
</span><span class='line'>
</span><span class='line'><span class="n">class</span> <span class="nl">ViewController:</span> <span class="n">UIViewController</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">override</span> <span class="n">func</span> <span class="n">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">super</span><span class="p">.</span><span class="n">viewDidLoad</span><span class="p">()</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="err">@</span><span class="kt">IBAction</span> <span class="n">func</span> <span class="n">saveImage</span><span class="p">(</span><span class="n">_</span> <span class="nl">sender:</span> <span class="n">Any</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">image</span> <span class="o">=</span> <span class="n">UIImage</span><span class="p">(</span><span class="nl">named:</span> <span class="s">&quot;test1&quot;</span><span class="p">)</span>
</span><span class='line'>        <span class="k">if</span> <span class="n">let</span> <span class="n">data</span> <span class="o">=</span> <span class="n">image</span><span class="o">?</span><span class="p">.</span><span class="n">pngData</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>            <span class="n">let</span> <span class="n">keyName</span> <span class="o">=</span> <span class="s">&quot;test\(Int.random(in: 1 ... 100))&quot;</span>
</span><span class='line'>            <span class="n">print</span><span class="p">(</span><span class="s">&quot;\(data) , keyName: \(keyName)&quot;</span><span class="p">)</span>
</span><span class='line'>            <span class="n">UserDefaults</span><span class="p">.</span><span class="n">standard</span><span class="p">.</span><span class="n">set</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nl">forKey:</span> <span class="n">keyName</span><span class="p">)</span>
</span><span class='line'>            <span class="n">UserDefaults</span><span class="p">.</span><span class="n">standard</span><span class="p">.</span><span class="n">synchronize</span><span class="p">()</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h4>実験結果</h4>

<p>早速、実験結果について見てみましょう。</p>

<p>1回の保存サイズが <code>350129 bytes</code> だったため、12回で <code>4201548 bytes</code> となり、<br/>
宣言である <code>4194304 bytes</code> を超えることになります。</p>

<p>今回の実験では、 <code>print</code> 文で『ランダムなキー名とバイト数』を出力するように組み込み、<br/>
端末の該当アプリに対して <code>Download Container...</code> から <code>UserDefaults</code> の中身を取得し比較してみました。</p>

<p>出力結果は、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test59</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test50</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test51</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test7</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test58</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test7</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test91</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test83</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test54</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test90</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test80</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test71</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test25</span>
</span><span class='line'><span class="mi">2019</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">19</span> <span class="mi">16</span><span class="o">:</span><span class="mi">47</span><span class="o">:</span><span class="mf">19.645111</span><span class="o">+</span><span class="mi">0900</span> <span class="n">AppSample</span><span class="p">[</span><span class="mi">1048</span><span class="o">:</span><span class="mi">193781</span><span class="p">]</span> <span class="p">[</span><span class="n">User</span> <span class="n">Defaults</span><span class="p">]</span> <span class="n">CFPrefsPlistSource</span><span class="o">&lt;</span><span class="mh">0x2821a5600</span><span class="o">&gt;</span> <span class="p">(</span><span class="nl">Domain:</span> <span class="n">com</span><span class="p">.</span><span class="n">xxx</span><span class="p">.</span><span class="n">AppSample</span><span class="p">,</span> <span class="nl">User:</span> <span class="n">kCFPreferencesCurrentUser</span><span class="p">,</span> <span class="nl">ByHost:</span> <span class="n">No</span><span class="p">,</span> <span class="nl">Container:</span> <span class="p">(</span><span class="n">null</span><span class="p">),</span> <span class="n">Contents</span> <span class="n">Need</span> <span class="nl">Refresh:</span> <span class="n">Yes</span><span class="p">)</span><span class="o">:</span> <span class="n">Attempting</span> <span class="n">to</span> <span class="n">store</span> <span class="o">&gt;=</span> <span class="mi">4194304</span> <span class="n">bytes</span> <span class="n">of</span> <span class="n">data</span> <span class="k">in</span> <span class="n">CFPreferences</span><span class="o">/</span><span class="n">NSUserDefaults</span> <span class="n">on</span> <span class="n">this</span> <span class="n">platform</span> <span class="n">is</span> <span class="n">invalid</span><span class="p">.</span> <span class="n">This</span> <span class="n">is</span> <span class="n">a</span> <span class="n">bug</span> <span class="k">in</span> <span class="n">NotificationSample</span> <span class="n">or</span> <span class="n">a</span> <span class="n">library</span> <span class="n">it</span> <span class="n">uses</span>
</span><span class='line'><span class="mi">2019</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mi">19</span> <span class="mi">16</span><span class="o">:</span><span class="mi">47</span><span class="o">:</span><span class="mf">19.645273</span><span class="o">+</span><span class="mi">0900</span> <span class="n">AppSample</span><span class="p">[</span><span class="mi">1048</span><span class="o">:</span><span class="mi">193781</span><span class="p">]</span> <span class="p">[</span><span class="n">User</span> <span class="n">Defaults</span><span class="p">]</span> <span class="n">CFPrefsPlistSource</span><span class="o">&lt;</span><span class="mh">0x2821a5600</span><span class="o">&gt;</span> <span class="p">(</span><span class="nl">Domain:</span> <span class="n">com</span><span class="p">.</span><span class="n">xxx</span><span class="p">.</span><span class="n">AppSample</span><span class="p">,</span> <span class="nl">User:</span> <span class="n">kCFPreferencesCurrentUser</span><span class="p">,</span> <span class="nl">ByHost:</span> <span class="n">No</span><span class="p">,</span> <span class="nl">Container:</span> <span class="p">(</span><span class="n">null</span><span class="p">),</span> <span class="n">Contents</span> <span class="n">Need</span> <span class="nl">Refresh:</span> <span class="n">No</span><span class="p">)</span><span class="o">:</span> <span class="n">Transitioning</span> <span class="n">into</span> <span class="n">direct</span> <span class="n">mode</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test21</span>
</span><span class='line'><span class="mi">350129</span> <span class="n">bytes</span> <span class="p">,</span> <span class="nl">keyName:</span> <span class="n">test32</span>
</span></code></pre></td></tr></table></div></figure>


<p>となり、 <strong>13個目</strong> の保存時にエラーが出力されました。
(途中キー名が被っており、上書きとなるため、その回数はスキップしています。)</p>

<p><code>Download Container...</code> で取得した <code>UserDefaults</code> の中身はどうなっているかと言うと、</p>

<p><img src="http://grandbig.github.io/images/ios13-userdefaults.png" alt="UserDefaultsに保存された内容" /></p>

<p>となっており、12個目までは保存されているようでした。</p>

<p>厳密には少々超える分には、許容されているように見えましたが、少なくとも、はっきりと超えた場合は保存されないことがわかりました。</p>

<h3>まとめ</h3>

<p>さて如何でしたでしょうか？<br/>
<code>UserDefaults</code> は大量の重要データを保存するのには不向きであることは、これまで通り自明であったと思いますが、<br/>
それがより厳密化されたと言えるのではないでしょうか。</p>

<p>今後に備えて、データの保存方法を今一度見直すにはいい機会かなと思います。<br/>
と言ったところで本日はここまで。</p>

<p>参考URL：</p>

<ul>
<li><a href="https://forums.developer.apple.com/thread/121527">Apple Developer Forums: iOS 13 &ndash; Attempting to store >= 4194304 bytes of data in CFPreferences/NSUserDefaults on this platform is invalid</a></li>
</ul>


<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[iOS13から対応する『Sign In with Apple』〜サードパーティアカウントでOAuth認証によるログインを備えたアプリで迷わないように。〜]]></title>
    <link href="http://grandbig.github.io/blog/2019/10/06/sign-in-with-apple/"/>
    <updated>2019-10-06T17:05:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/10/06/sign-in-with-apple</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>今回はiOS13から導入された <code>Sign In with Apple</code> について見ていきたいと思います。</p>

<p>もし、 <code>Facebook / Google / Twitter</code> などのアカウントを用いたログインを可能とする機能をアプリが持っている場合、<br/>
Appleが新たに提唱した <code>Sign In with Apple</code> の機能を実装する必要性が出てきたようです。</p>

<p>一方で <code>Facebook / Google / Twitter</code> などの本家のアプリでは恐らく実装する必要はないと思われます。<br/>
(ログイン機能を実装するのであれば、 <code>Sign In with Apple</code> が必須という話ではなく、サードパーティ製のログイン機能を持つアプリに限るようです。 )</p>

<p>とは言え、「急にそんなことを言われても対応工数がかかるし、他に実装したい機能もあるし&hellip;」と困るエンジニアもいるかもしれません。</p>

<p>ですが、 <code>Apple</code> が要求する以上、 iOSアプリを開発し続ける上で避けては通れない問題ですので、簡単に対応方法を紹介したいと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>Apple Developer Programでの対応</h3>

<p>まずは、コードを書く前に、 <code>Sign In with Apple</code> の機能を有効にした <code>App ID</code> を作成し、 <code>Provisioning Profile</code> に紐付ける必要があります。</p>

<h4>App IDの作成</h4>

<p>下記手順で <code>Sign In with Apple</code> 対応の <code>App ID</code> を作成します。</p>

<p>まずは、 <a href="https://developer.apple.com/account/resources/identifiers/list">Apple Developer Program > Certificates, Identifiers &amp; Profiles > Identifiers</a> にアクセスします。</p>

<p>追加するための「＋」ボタンを選択後に表示される下記の画面で <code>App IDs</code> を選択します。<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_1.png" alt="App IDsを選択" /></p>

<p>必要事項を入力します。<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_2.png" alt="DescriptionとBundle IDを入力" /></p>

<p>入力例)</p>

<ul>
<li>Description: <code>SignInWithAppleSample</code></li>
<li>Bundle ID: <code>com.xxx.SignInWithAppleSample</code></li>
</ul>


<p>下にスクロールすると <code>Sign In with Apple</code> の項目が追加されているので、チェックをつけます。<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_3.png" alt="Sign In with Appleにチェック" /></p>

<p>上記状態で次に進み、確認画面で問題ないことを確認すれば、 <code>Register</code> ボタンを選択するだけで作成完了です。<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_4.png" alt="確認完了後、Registerボタンを選択" /></p>

<h4>Provisioning Profileへの紐付け</h4>

<p>下記手順で <code>App ID</code> を紐付けて <code>Provisioning Profile</code> を作成します。</p>

<p><a href="https://developer.apple.com/account/resources/profiles/list">Apple Developer Program > Certificates, Identifiers &amp; Profiles > Profiles</a>にアクセスします。</p>

<p>追加するための「＋」ボタンを選択後に表示される下記の画面で <code>iOS App Development</code> を選択します。<br/>
(開発時を想定しているため、リリース時であれば、 <code>App Store</code> など適切なものを選択してください。)<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_5.png" alt="iOS App Developmentを選択" /></p>

<p>先程作成した <code>App ID</code> を選択します。<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_6.png" alt="App IDを選択" /></p>

<p>必要な <code>Certificate</code> を選択します。<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_7.png" alt="Certificateを選択" /></p>

<p>利用したいデバイスを選択します。<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_8.png" alt="Deviceを選択" /></p>

<p><code>Provisioning Profile</code> の名前を決めます。<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_9.png" alt="Provisioning Profileの名前を入力" /></p>

<p><code>Generate</code> ボタンを選択すれば作成完了です。</p>

<h3>Xcode11の設定</h3>

<p><code>Apple Developer Program</code> 上での準備が整ったら、 <code>Xcode11</code> で <code>Sign In with Apple</code> の <code>Capability</code> を追加しましょう。</p>

<p>プロジェクトを選択し、 <code>Signing &amp; Capabilities</code> タブから追加します。<br/>
<img src="http://grandbig.github.io/images/sign-in-with-apple_10.png" alt="Sign In with AppleのCapabilityを追加" /></p>

<h3>ソースコードの実装</h3>

<p>ソースコードを書いていきましょう。<br/>
これは <code>Apple</code> が公開しているサンプルを一部抜粋して説明します。<br/>
サンプルは<a href="https://developer.apple.com/documentation/authenticationservices/adding_the_sign_in_with_apple_flow_to_your_app">こちら</a>からダウンロードできます。</p>

<p><code>Sign In with Apple</code> は <code>AuthenticationServices</code> の各種メソッドを利用するため、<br/>
まずは <code>AuthenticationServices</code> をインポートします。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">import</span> <span class="n">UIKit</span>
</span><span class='line'><span class="n">import</span> <span class="n">AuthenticationServices</span>
</span><span class='line'>
</span><span class='line'><span class="n">class</span> <span class="nl">LoginViewController:</span> <span class="n">UIViewController</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>続いて、ログイン画面に <code>Sign In with Apple</code> のボタンを配置します。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">class</span> <span class="nl">LoginViewController:</span> <span class="n">UIViewController</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">/// Sign In with Appleボタンを追加するためのUIStackView</span>
</span><span class='line'>    <span class="err">@</span><span class="kt">IBOutlet</span> <span class="n">weak</span> <span class="n">var</span> <span class="nl">loginProviderStackView:</span> <span class="n">UIStackView</span><span class="o">!</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">override</span> <span class="n">func</span> <span class="n">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">super</span><span class="p">.</span><span class="n">viewDidLoad</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'>        <span class="c1">// Sign In with Appleボタンを配置します</span>
</span><span class='line'>        <span class="n">setupProviderLoginView</span><span class="p">()</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">/// Sign In with Appleのボタンを配置</span>
</span><span class='line'>    <span class="n">func</span> <span class="n">setupProviderLoginView</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">authorizationButton</span> <span class="o">=</span> <span class="n">ASAuthorizationAppleIDButton</span><span class="p">()</span>
</span><span class='line'>        <span class="n">authorizationButton</span><span class="p">.</span><span class="n">addTarget</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="nl">action:</span> <span class="err">#</span><span class="n">selector</span><span class="p">(</span><span class="n">handleAuthorizationAppleIDButtonPress</span><span class="p">),</span> <span class="k">for</span><span class="o">:</span> <span class="p">.</span><span class="n">touchUpInside</span><span class="p">)</span>
</span><span class='line'>        <span class="n">self</span><span class="p">.</span><span class="n">loginProviderStackView</span><span class="p">.</span><span class="n">addArrangedSubview</span><span class="p">(</span><span class="n">authorizationButton</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">/// Sign In with Appleボタンタップ後のアクション</span>
</span><span class='line'>    <span class="err">@</span><span class="n">objc</span>
</span><span class='line'>    <span class="n">func</span> <span class="n">handleAuthorizationAppleIDButtonPress</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">appleIDProvider</span> <span class="o">=</span> <span class="n">ASAuthorizationAppleIDProvider</span><span class="p">()</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">request</span> <span class="o">=</span> <span class="n">appleIDProvider</span><span class="p">.</span><span class="n">createRequest</span><span class="p">()</span>
</span><span class='line'>        <span class="n">request</span><span class="p">.</span><span class="n">requestedScopes</span> <span class="o">=</span> <span class="p">[.</span><span class="n">fullName</span><span class="p">,</span> <span class="p">.</span><span class="n">email</span><span class="p">]</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">let</span> <span class="n">authorizationController</span> <span class="o">=</span> <span class="n">ASAuthorizationController</span><span class="p">(</span><span class="nl">authorizationRequests:</span> <span class="p">[</span><span class="n">request</span><span class="p">])</span>
</span><span class='line'>        <span class="n">authorizationController</span><span class="p">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="n">self</span>
</span><span class='line'>        <span class="n">authorizationController</span><span class="p">.</span><span class="n">presentationContextProvider</span> <span class="o">=</span> <span class="n">self</span>
</span><span class='line'>        <span class="n">authorizationController</span><span class="p">.</span><span class="n">performRequests</span><span class="p">()</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>上記を見てわかるように、タップ時に呼び出すメソッドを定義して、その中で必要なリクエストを生成し、実行しています。</p>

<p><code>requestedScopes</code> は必要なアクセス情報の範囲を定義するようですが、<br/>
今のところは <code>.fullName</code> と <code>.email</code> しか用意されていないようです。</p>

<p>また、 <code>delegate</code> と <code>presentationContextProvider</code> を呼び出す必要があります。<br/>
これは、</p>

<ul>
<li><code>ASAuthorizationControllerDelegate</code> の <code>authorizationController:didCompleteWithAuthorization:</code> で認証完了をキャッチする</li>
<li><code>ASAuthorizationControllerPresentationContextProviding</code> の <code>presentationAnchorForAuthorizationController:</code> で認証画面に必要な画面の提示場所を設定する</li>
</ul>


<p>ためです。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">extension</span> <span class="nl">LoginViewController:</span> <span class="n">ASAuthorizationControllerDelegate</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">func</span> <span class="n">authorizationController</span><span class="p">(</span><span class="nl">controller:</span> <span class="n">ASAuthorizationController</span><span class="p">,</span> <span class="n">didCompleteWithAuthorization</span> <span class="nl">authorization:</span> <span class="n">ASAuthorization</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="p">...</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">extension</span> <span class="nl">LoginViewController:</span> <span class="n">ASAuthorizationControllerPresentationContextProviding</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">func</span> <span class="n">presentationAnchor</span><span class="p">(</span><span class="k">for</span> <span class="nl">controller:</span> <span class="n">ASAuthorizationController</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ASPresentationAnchor</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">view</span><span class="p">.</span><span class="n">window</span><span class="o">!</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>動作確認</h3>

<p>ここまで実装した内容で、どんな動きになるのかを見てみます。</p>

<h4>iPhone上でAppleIDでサインインしていない場合</h4>

<p>あまりケースとして多くはないかもしれませんが、普通にありえるので実装時に考慮が必要です。<br/>
この場合、 <code>performRequests()</code> を実行した時点で『設定アプリでAppleIDアカウントでサインインするかどうか』を聞かれます。</p>

<p><img src="http://grandbig.github.io/images/sign-in-with-apple_11.png" alt="設定アプリでAppleIDアカウントでサインインするかどうか聞かれます" /></p>

<p>サインイン後に、改めてアプリに戻りログインを実行することで先に進めるようになります。</p>

<h4>iPhone上でAppleIDでサインイン済みの場合</h4>

<p>改めて <code>Sign In with Apple</code> のボタンをタップすると、以下の画面が表示されます。</p>

<p><img src="http://grandbig.github.io/images/sign-in-with-apple_12.png" alt="Sign In with Appleの画面" /></p>

<p><code>Continue</code> ボタンのタップで先に進むと、以下の画面が表示されます。</p>

<p><img src="http://grandbig.github.io/images/sign-in-with-apple_13.png" alt="Share My Email / Hide My Email選択画面" /></p>

<p>ここで <code>Share My Email</code> または <code>Hide My Email</code> のどちらかを選択することになります。<br/>
<code>Hide My Email</code> はよりユーザにとってセキュアな状態を保つために用意されており、以下の特徴があります。</p>

<ul>
<li>実際のメールアドレスとは異なるメールアドレスが発行される</li>
<li>そのメールアドレスは、認証時に利用したAppleIDのメールアドレスとリンクしている</li>
<li>アプリに実際のメールアドレスを渡さないで済む</li>
</ul>


<p>上記選択した上で <code>Continuer with Password</code> ボタンをタップして次に進むと、以下の画面が表示されます。</p>

<p><img src="http://grandbig.github.io/images/sign-in-with-apple_14.png" alt="パスワード入力画面" /></p>

<p>パスワード入力して次に進むと、二段階認証を求められます。</p>

<p><img src="http://grandbig.github.io/images/sign-in-with-apple_15.png" alt="二段階認証画面" /></p>

<p>二段階認証してやっと認証が完了となります。</p>

<h3>まとめ</h3>

<p>さて如何でしたでしょうか。<br/>
アプリの機能次第で対応必須な内容ですので、今からでも知っておいて損はないかなと思います。<br/>
<code>SDK</code> 自体は非常に簡単に扱えますので、アプリ側の実装は早めに目処をつけておいて、データ構成など課題がある部分にフォーカスした方が良いでしょう。</p>

<p>と言ったところで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[iOS13におけるプッシュ通知に必要なデバイストークンの取得方法]]></title>
    <link href="http://grandbig.github.io/blog/2019/09/28/ios-devicetoken-2/"/>
    <updated>2019-09-28T16:12:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/09/28/ios-devicetoken-2</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>今回はiOS13のプッシュ通知用デバイストークンについて見ていきたいと思います。<br/>
<code>Swift</code> に関して言えば、歴史的変遷から、問題のない現場が多いと思うのですが、<br/>
<code>Objective-C</code> を中心に活用している現場では注意が必要かもしれません。</p>

<p>具体的には後述しますが、<br/>
<code>description</code> を利用してデバイストークンを取得する方式は <code>iOS13</code> から見直す必要がありそうです。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>プッシュ通知の受信処理の実装</h3>

<p>折角なので、基本的な設定についても説明していきます。</p>

<h4>Xcode11の設定</h4>

<p>まずは、Xcode11上で下図の状態まで設定します。</p>

<p><strong>▼確認観点</strong><br/>
* <code>Team ID</code> が利用想定のものであること<br/>
* <code>Bundle Identifier</code> が利用想定のものであること<br/>
* <code>Signing Certificate</code> が利用想定のものであること<br/>
* <code>Provisioning Profile</code> の <code>Capabilities</code> に <code>Push Notifications</code> が含まれている<br/>
* <code>Capability</code> として <code>Push Notifications</code> が追加すること</p>

<p><img src="http://grandbig.github.io/images/ios-devicetoken-2_1.png" alt="Signing &amp; Capabilities" /></p>

<h4>AppDelegateの実装</h4>

<p>続いて、 <code>AppDelegate.swift</code> 上の実装です。</p>

<p>基本的なステップは4つです。</p>

<p>① プッシュ通知の利用許可のリクエストを送信します<br/>
② 利用許可を得た場合に、プッシュ通知の利用登録を実行します<br/>
③ プッシュ通知の利用登録が成功したことをキャッチする <code>Delegate</code> メソッドを書きます<br/>
④ プッシュ通知の利用登録が失敗したことをキャッチする <code>Delegate</code> メソッドを書きます</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">import</span> <span class="n">UIKit</span>
</span><span class='line'><span class="n">import</span> <span class="n">UserNotifications</span>
</span><span class='line'>
</span><span class='line'><span class="err">@</span><span class="n">UIApplicationMain</span>
</span><span class='line'><span class="n">class</span> <span class="nl">AppDelegate:</span> <span class="n">UIResponder</span><span class="p">,</span> <span class="n">UIApplicationDelegate</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">func</span> <span class="n">application</span><span class="p">(</span><span class="n">_</span> <span class="nl">application:</span> <span class="n">UIApplication</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span> <span class="nl">launchOptions:</span> <span class="p">[</span><span class="n">UIApplication</span><span class="p">.</span><span class="nl">LaunchOptionsKey:</span> <span class="n">Any</span><span class="p">]</span><span class="o">?</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// Override point for customization after application launch.</span>
</span><span class='line'>
</span><span class='line'>        <span class="c1">// ① プッシュ通知の利用許可のリクエスト送信</span>
</span><span class='line'>        <span class="n">UNUserNotificationCenter</span><span class="p">.</span><span class="n">current</span><span class="p">().</span><span class="n">requestAuthorization</span><span class="p">(</span><span class="nl">options:</span> <span class="p">[.</span><span class="n">alert</span><span class="p">,</span> <span class="p">.</span><span class="n">sound</span><span class="p">,</span> <span class="p">.</span><span class="n">badge</span><span class="p">])</span> <span class="p">{</span> <span class="n">granted</span><span class="p">,</span> <span class="n">error</span> <span class="k">in</span>
</span><span class='line'>            <span class="n">guard</span> <span class="n">granted</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>            <span class="n">DispatchQueue</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">async</span> <span class="p">{</span>
</span><span class='line'>                <span class="c1">// ② プッシュ通知利用の登録</span>
</span><span class='line'>                <span class="n">UIApplication</span><span class="p">.</span><span class="n">shared</span><span class="p">.</span><span class="n">registerForRemoteNotifications</span><span class="p">()</span>
</span><span class='line'>            <span class="p">}</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="k">return</span> <span class="n">true</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">extension</span> <span class="n">AppDelegate</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// ③ プッシュ通知の利用登録が成功した場合</span>
</span><span class='line'>    <span class="n">func</span> <span class="n">application</span><span class="p">(</span><span class="n">_</span> <span class="nl">application:</span> <span class="n">UIApplication</span><span class="p">,</span>
</span><span class='line'>                     <span class="n">didRegisterForRemoteNotificationsWithDeviceToken</span> <span class="nl">deviceToken:</span> <span class="n">Data</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">token</span> <span class="o">=</span> <span class="n">deviceToken</span><span class="p">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">String</span><span class="p">(</span><span class="nl">format:</span> <span class="s">&quot;%.2hhx&quot;</span><span class="p">,</span> <span class="n">$0</span><span class="p">)</span> <span class="p">}.</span><span class="n">joined</span><span class="p">()</span>
</span><span class='line'>        <span class="n">print</span><span class="p">(</span><span class="s">&quot;Device token: \(token)&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// ④ プッシュ通知の利用登録が失敗した場合</span>
</span><span class='line'>    <span class="n">func</span> <span class="n">application</span><span class="p">(</span><span class="n">_</span> <span class="nl">application:</span> <span class="n">UIApplication</span><span class="p">,</span>
</span><span class='line'>                     <span class="n">didFailToRegisterForRemoteNotificationsWithError</span> <span class="nl">error:</span> <span class="n">Error</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">print</span><span class="p">(</span><span class="s">&quot;Failed to register to APNs: \(error)&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>旧来のデバイストークンの取得方法</h3>

<p>さて本題に入ります。<br/>
<code>Objective-C</code> 時代に主流と言われていたデバイストークンの取得方法は、以下のような <code>description</code> を利用した手法でした。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">NSString</span> <span class="o">*</span><span class="n">token</span> <span class="o">=</span> <span class="p">[[[[</span><span class="n">deviceToken</span> <span class="n">description</span><span class="p">]</span> <span class="nl">stringByReplacingOccurrencesOfString:</span><span class="s">@&quot;&lt;&quot;</span><span class="nl">withString:</span><span class="s">@&quot;&quot;</span><span class="p">]</span>
</span><span class='line'>                   <span class="nl">stringByReplacingOccurrencesOfString:</span><span class="s">@&quot;&gt;&quot;</span> <span class="nl">withString:</span><span class="s">@&quot;&quot;</span><span class="p">]</span>
</span><span class='line'>                   <span class="nl">stringByReplacingOccurrencesOfString:</span> <span class="s">@&quot; &quot;</span> <span class="nl">withString:</span> <span class="s">@&quot;&quot;</span><span class="p">];</span>
</span></code></pre></td></tr></table></div></figure>


<p>これは、 <code>[deviceToken description]</code> で取得される値が、<br/>
<code>&lt;7097978d 1e438923 ... 6fdbf111&gt;</code> というような先頭と末尾を <code>&lt;&gt;</code> で括られていたためです。<br/>
そして、1文で書けるというのも当時から魅力的だったのかもしれません。</p>

<p><code>Swift</code> に言語が変わってからも、上記手法が多くの場面で使われてきたことと思います。<br/>
しかし、 <code>Swift3</code> になったタイミングで、状況が変わりました。</p>

<p>プッシュ通知の利用登録成功時の <code>Delegate</code> メソッドの <code>deviceToken</code> が <code>NSData</code> 型から <code>Data</code> 型に変わったことで、この手法が利用できなくなったのです。</p>

<p>この時点で</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">let</span> <span class="n">token</span> <span class="o">=</span> <span class="n">deviceToken</span><span class="p">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">String</span><span class="p">(</span><span class="nl">format:</span> <span class="s">&quot;%.2hhx&quot;</span><span class="p">,</span> <span class="n">$0</span><span class="p">)</span> <span class="p">}.</span><span class="n">joined</span><span class="p">()</span>
</span></code></pre></td></tr></table></div></figure>


<p>という書き方がデバイストークン取得の主流に完全に取って代わったと思います。</p>

<p>もしかしたら、 <code>Data</code> 型を <code>NSData</code> 型に変換することで、<br/>
引き続き旧来の手法を利用している現場があるかもしれませんが、<br/>
その場合は、即刻デバイストークンの取得方法を見直しましょう。</p>

<p>これはあくまでも <code>Swift</code> におけるデバイストークンの取得の変遷であって、<br/>
<code>Objective-C</code> は何ら変わることがなかったため、<br/>
旧来の書き方が続いている現場がまだまだ多い気がしています。</p>

<p>しかし、 <code>iOS13</code> からはどうやら見直しが必須になったようです。<br/>
というのも、 <code>description</code> を利用すると</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">{</span> <span class="n">length</span> <span class="o">=</span> <span class="mi">32</span><span class="p">,</span> <span class="n">bytes</span> <span class="o">=</span> <span class="mi">7097978</span><span class="n">d</span> <span class="mf">1e438923</span> <span class="p">...</span> <span class="mf">6f</span><span class="n">dbf111</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のような形で返ってきてしまうため、<br/>
<code>&lt;&gt;</code> を除去することでデバイストークンを取得することができなくなったようなんです。</p>

<ul>
<li><a href="https://forums.developer.apple.com/thread/117545">Apple Developer Forums: iOS13 PKPushCredentials broken</a></li>
<li><a href="https://forums.developer.apple.com/thread/119111">Apple Developer Forums: NSData description and NSString stringWithFormat have different return results when compiled with Xcode 11 versus Xcode 10</a></li>
</ul>


<p>では、 <code>Swift</code> は先程書いた通り1行で書けますが、<br/>
<code>Objective-C</code> ではどう書いていくべきなのでしょうか。<br/>
安心してください。 <code>Facebook</code> が1つの解を示してくれています。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// ↓ dataにDelegateメソッドに渡ってきたdeviceTokenを渡します</span>
</span><span class='line'><span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nl">hexadecimalStringFromData:</span><span class="p">(</span><span class="n">NSData</span> <span class="o">*</span><span class="p">)</span><span class="n">data</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="n">NSUInteger</span> <span class="n">dataLength</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">length</span><span class="p">;</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">dataLength</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">return</span> <span class="nb">nil</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">dataBuffer</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">bytes</span><span class="p">;</span>
</span><span class='line'>    <span class="n">NSMutableString</span> <span class="o">*</span><span class="n">hexString</span>  <span class="o">=</span> <span class="p">[</span><span class="n">NSMutableString</span> <span class="nl">stringWithCapacity:</span><span class="p">(</span><span class="n">dataLength</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)];</span>
</span><span class='line'>    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">dataLength</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="p">[</span><span class="n">hexString</span> <span class="nl">appendFormat:</span><span class="s">@&quot;%02x&quot;</span><span class="p">,</span> <span class="n">dataBuffer</span><span class="p">[</span><span class="n">i</span><span class="p">]];</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="p">[</span><span class="n">hexString</span> <span class="n">copy</span><span class="p">];</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>流石に1行で済ませることは難しいですが、<br/>
これで <code>Objective-C</code> でも正しくデバイストークンを取得できるようになりました。</p>

<h3>まとめ</h3>

<p>さて如何でしたでしょうか。<br/>
iOS13は細かいところも含めて様々な変更が入っているため、日々最新情報をキャッチしつつ、自身でも積極的に使い倒していく必要がありますね。</p>

<p>と言ったところで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[iOS13から利用できるBackgroundTasksを使ってみよう！]]></title>
    <link href="http://grandbig.github.io/blog/2019/09/22/backgroundtasks/"/>
    <updated>2019-09-22T17:09:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/09/22/backgroundtasks</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>今回はiOS13で新たに追加された <code>BackgroundTasks Framework</code> について見ていきたいと思います。<br/>
基本的には、 <a href="https://developer.apple.com/videos/play/wwdc2019/707/">WWDC2019動画の『Advances in App Background Execution』</a>を見ながら実践してみました。</p>

<p>ですが、微妙に躓くところもあったのでメモとして残しておきたいと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>BackgroundTasksとは</h3>

<p>まず、 <code>BackgroundTasks</code> の説明です。<br/>
<code>BackgroundTasks</code> はiOS13から利用できる新しいFrameworkになります。<br/>
<code>BackgroundTasks</code> には大きく分けて下記2つのAPIが存在します。</p>

<ol>
<li>Background Processing Tasks</li>
<li>Background App Refresh Tasks</li>
</ol>


<h4>Background Processing Tasks</h4>

<p><code>Background Processing Tasks</code> は以下シーンでの利用が想定されています。</p>

<ul>
<li>(急を要しない)後々の実行で良いメンテナンス処理</li>
<li>Core MLを利用した機械学習のトレーニング処理</li>
</ul>


<p>そのため、 <strong>数分間</strong> の処理実行が許されています。<br/>
また、 <code>requiresExternalPower</code> というフラグを <code>true</code> にすることで、CPU消耗によるプロセスキルをさせないよう制御することができます。<br/>
(iOSの世界でこれって結構スゴイ気がしますね。)</p>

<h4>Background App Refresh Tasks</h4>

<p><code>Background App Refresh Tasks</code> は以下シーンでの利用が想定されています。</p>

<ul>
<li>30秒以内で完了できる処理</li>
<li>1日を通してアプリを最新に保つために必要な処理</li>
</ul>


<p>これまで上記のような対応をする際には <code>Background Fetch</code> を利用していたことと思いますが、<br/>
今回の新APIの発表により、旧APIはdeprecatedになったそうです。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// deprecated対象</span>
</span><span class='line'><span class="n">UIApplication</span><span class="p">.</span><span class="n">setMinimumBackgroundFetchInterval</span><span class="p">(</span><span class="nl">_:</span><span class="p">)</span>
</span><span class='line'><span class="n">UIApplicationDelegate</span><span class="p">.</span><span class="n">application</span><span class="p">(</span><span class="nl">_:performFetchWithCompletionHandler:</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<h3>BackgroundTasksの使い方</h3>

<p>続いて具体的な使い方を見ていきます。</p>

<h4>ソースコードを書き始めるまでの下準備</h4>

<p><strong>① Xccode上でCapabilityを追加します</strong><br/>
バックグラウンド処理を利用する場合はこれまで通り <code>Capability</code> の <code>Background Modes</code> が必要になります。<br/>
Xcode11で追加する方法が少々変わっているので気をつけましょう。</p>

<p><img src="http://grandbig.github.io/images/backgroundtasks_1.png" alt="Background Modesを追加します" /></p>

<p><strong>② Background Modesにチェックを入れます</strong><br/>
今回は、 <code>Background Processing Tasks</code> と <code>Background App Refresh Tasks</code> なので下図の通りです。</p>

<p><img src="http://grandbig.github.io/images/backgroundtasks_2.png" alt="必要なBackground Modesにチェックを入れます" /></p>

<p><strong>③ Info.plistにIdentifierを登録します</strong><br/>
<code>Info.plist</code> に <code>Permitted background task scheduler identifiers</code> を追加します。<br/>
また、 <code>Background Processing Tasks</code> と <code>Background App Refresh Tasks</code> 用にそれぞれ <code>Identifier</code> を定義します。</p>

<p>因みに、この <code>Identifier</code> は例によってユニークであることが求められるので、<br/>
<code>com.xxxx.XXXXSample.process</code> , <code>com.xxxx.XXXXSample.refresh</code> といったDNSの逆書きが推奨されています。</p>

<p><img src="http://grandbig.github.io/images/backgroundtasks_3.png" alt="Info.plistに必要なIdentifierを登録します" /></p>

<h4>ソースコードの実装</h4>

<p>ここまででソースコード以外の準備は完了です。<br/>
続いて、ソースコードを書いていきましょう。</p>

<p><code>Background Processing Tasks</code> と <code>Background App Refresh Tasks</code> それぞれ記載します。</p>

<h5>Background Processing Tasks</h5>

<p><strong>① BackgroundTasksをimportします</strong></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// AppDelegate.swift</span>
</span><span class='line'><span class="n">import</span> <span class="n">UIKit</span>
</span><span class='line'><span class="n">import</span> <span class="n">BackgroundTasks</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>② didFinishLaunchWithOptions内にバックグラウンドタスクを登録します</strong></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// AppDelegate.swift</span>
</span><span class='line'><span class="err">@</span><span class="n">UIApplicationMain</span>
</span><span class='line'><span class="n">class</span> <span class="nl">AppDelegate:</span> <span class="n">UIResponder</span><span class="p">,</span> <span class="n">UIApplicationDelegate</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'>    <span class="n">func</span> <span class="n">application</span><span class="p">(</span><span class="n">_</span> <span class="nl">application:</span> <span class="n">UIApplication</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span> <span class="nl">launchOptions:</span> <span class="p">[</span><span class="n">UIApplication</span><span class="p">.</span><span class="nl">LaunchOptionsKey:</span> <span class="n">Any</span><span class="p">]</span><span class="o">?</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// Override point for customization after application launch.</span>
</span><span class='line'>
</span><span class='line'>        <span class="c1">// 第一引数: Info.plistで定義したIdentifierを指定</span>
</span><span class='line'>        <span class="c1">// 第二引数: タスクを実行するキューを指定。nilの場合は、デフォルトのバックグラウンドキューが利用されます。</span>
</span><span class='line'>        <span class="c1">// 第三引数: 実行する処理</span>
</span><span class='line'>        <span class="n">BGTaskScheduler</span><span class="p">.</span><span class="n">shared</span><span class="p">.</span><span class="k">register</span><span class="p">(</span><span class="nl">forTaskWithIdentifier:</span> <span class="s">&quot;com.MeasurementSample.refresh&quot;</span><span class="p">,</span> <span class="nl">using:</span> <span class="nb">nil</span><span class="p">)</span> <span class="p">{</span> <span class="n">task</span> <span class="k">in</span>
</span><span class='line'>            <span class="c1">// バックグラウンド処理したい内容 ※後述します</span>
</span><span class='line'>            <span class="n">self</span><span class="p">.</span><span class="n">handleAppProcessing</span><span class="p">(</span><span class="nl">task:</span> <span class="n">task</span> <span class="n">as</span><span class="o">!</span> <span class="n">BGProcessingTask</span><span class="p">)</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">true</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>③ バックグラウンドタスクにスケジューリングします</strong></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">private</span> <span class="n">func</span> <span class="nf">scheduleAppProcessing</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// Info.plistで定義したIdentifierを指定</span>
</span><span class='line'>    <span class="n">let</span> <span class="n">request</span> <span class="o">=</span> <span class="n">BGProcessingTaskRequest</span><span class="p">(</span><span class="nl">identifier:</span> <span class="s">&quot;com.MeasurementSample.refresh&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="c1">// 通信が発生するか否かを指定</span>
</span><span class='line'>    <span class="n">request</span><span class="p">.</span><span class="n">requiresNetworkConnectivity</span> <span class="o">=</span> <span class="n">false</span>
</span><span class='line'>    <span class="c1">// CPU監視の必要可否を設定</span>
</span><span class='line'>    <span class="n">request</span><span class="p">.</span><span class="n">requiresExternalPower</span> <span class="o">=</span> <span class="n">true</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">do</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// スケジューラーに実行リクエストを登録</span>
</span><span class='line'>        <span class="n">try</span> <span class="n">BGTaskScheduler</span><span class="p">.</span><span class="n">shared</span><span class="p">.</span><span class="n">submit</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span> <span class="n">catch</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">print</span><span class="p">(</span><span class="s">&quot;Could not schedule app processing: \(error)&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">func</span> <span class="nf">applicationDidEnterBackground</span><span class="p">(</span><span class="n">_</span> <span class="nl">application:</span> <span class="n">UIApplication</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// バックグラウンド起動に移ったときにルケジューリング登録</span>
</span><span class='line'>    <span class="n">scheduleAppProcessing</span><span class="p">()</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>④ 実際に実行する処理を定義します</strong></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// AppDelegate.swift</span>
</span><span class='line'><span class="c1">// サンプル用のOperation</span>
</span><span class='line'><span class="n">class</span> <span class="nl">PrintOperation:</span> <span class="n">Operation</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">let</span> <span class="kt">id</span><span class="o">:</span> <span class="n">Int</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">init</span><span class="p">(</span><span class="kt">id</span><span class="o">:</span> <span class="n">Int</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">self</span><span class="p">.</span><span class="kt">id</span> <span class="o">=</span> <span class="kt">id</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">override</span> <span class="n">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">print</span><span class="p">(</span><span class="s">&quot;this operation id is \(self.id)&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="err">@</span><span class="n">UIApplicationMain</span>
</span><span class='line'><span class="n">class</span> <span class="nl">AppDelegate:</span> <span class="n">UIResponder</span><span class="p">,</span> <span class="n">UIApplicationDelegate</span> <span class="p">{</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'>  <span class="n">private</span> <span class="n">func</span> <span class="n">handleAppProcessing</span><span class="p">(</span><span class="nl">task:</span> <span class="n">BGProcessingTask</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="c1">// 1日の間、何度も実行したい場合は、1回実行するごとに新たにスケジューリングに登録します</span>
</span><span class='line'>      <span class="n">scheduleAppRefresh</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'>      <span class="n">let</span> <span class="n">queue</span> <span class="o">=</span> <span class="n">OperationQueue</span><span class="p">()</span>
</span><span class='line'>      <span class="n">queue</span><span class="p">.</span><span class="n">maxConcurrentOperationCount</span> <span class="o">=</span> <span class="mi">1</span>
</span><span class='line'>
</span><span class='line'>      <span class="c1">// 時間内に実行完了しなかった場合は、処理を解放します</span>
</span><span class='line'>      <span class="c1">// バックグラウンドで実行する処理は、次回に回しても問題ない処理のはずなので、これでOK</span>
</span><span class='line'>      <span class="n">task</span><span class="p">.</span><span class="n">expirationHandler</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>          <span class="n">queue</span><span class="p">.</span><span class="n">cancelAllOperations</span><span class="p">()</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>      <span class="c1">// サンプルの処理をキューに詰めます</span>
</span><span class='line'>      <span class="n">let</span> <span class="n">array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
</span><span class='line'>      <span class="n">array</span><span class="p">.</span><span class="n">enumerated</span><span class="p">().</span><span class="n">forEach</span> <span class="p">{</span> <span class="n">arg</span> <span class="k">in</span>
</span><span class='line'>          <span class="n">let</span> <span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="o">=</span> <span class="n">arg</span>
</span><span class='line'>          <span class="n">let</span> <span class="n">operation</span> <span class="o">=</span> <span class="n">PrintOperation</span><span class="p">(</span><span class="kt">id</span><span class="o">:</span> <span class="n">value</span><span class="p">)</span>
</span><span class='line'>          <span class="k">if</span> <span class="n">offset</span> <span class="o">==</span> <span class="n">array</span><span class="p">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">{</span>
</span><span class='line'>              <span class="n">operation</span><span class="p">.</span><span class="n">completionBlock</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>                  <span class="c1">// 最後の処理が完了したら、必ず完了したことを伝える必要があります</span>
</span><span class='line'>                  <span class="n">task</span><span class="p">.</span><span class="n">setTaskCompleted</span><span class="p">(</span><span class="nl">success:</span> <span class="n">operation</span><span class="p">.</span><span class="n">isFinished</span><span class="p">)</span>
</span><span class='line'>              <span class="p">}</span>
</span><span class='line'>          <span class="p">}</span>
</span><span class='line'>          <span class="n">queue</span><span class="p">.</span><span class="n">addOperation</span><span class="p">(</span><span class="n">operation</span><span class="p">)</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h5>Background App Refresh Tasks</h5>

<p><strong>① BackgroundTasksをimportします</strong></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// AppDelegate.swift</span>
</span><span class='line'><span class="n">import</span> <span class="n">UIKit</span>
</span><span class='line'><span class="n">import</span> <span class="n">BackgroundTasks</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>② didFinishLaunchWithOptions内にバックグラウンドタスクを登録します</strong></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// AppDelegate.swift</span>
</span><span class='line'><span class="err">@</span><span class="n">UIApplicationMain</span>
</span><span class='line'><span class="n">class</span> <span class="nl">AppDelegate:</span> <span class="n">UIResponder</span><span class="p">,</span> <span class="n">UIApplicationDelegate</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'>    <span class="n">func</span> <span class="n">application</span><span class="p">(</span><span class="n">_</span> <span class="nl">application:</span> <span class="n">UIApplication</span><span class="p">,</span> <span class="n">didFinishLaunchingWithOptions</span> <span class="nl">launchOptions:</span> <span class="p">[</span><span class="n">UIApplication</span><span class="p">.</span><span class="nl">LaunchOptionsKey:</span> <span class="n">Any</span><span class="p">]</span><span class="o">?</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// Override point for customization after application launch.</span>
</span><span class='line'>
</span><span class='line'>        <span class="c1">// 第一引数: Info.plistで定義したIdentifierを指定</span>
</span><span class='line'>        <span class="c1">// 第二引数: タスクを実行するキューを指定。nilの場合は、デフォルトのバックグラウンドキューが利用されます。</span>
</span><span class='line'>        <span class="c1">// 第三引数: 実行する処理</span>
</span><span class='line'>        <span class="n">BGTaskScheduler</span><span class="p">.</span><span class="n">shared</span><span class="p">.</span><span class="k">register</span><span class="p">(</span><span class="nl">forTaskWithIdentifier:</span> <span class="s">&quot;com.MeasurementSample.refresh&quot;</span><span class="p">,</span> <span class="nl">using:</span> <span class="nb">nil</span><span class="p">)</span> <span class="p">{</span> <span class="n">task</span> <span class="k">in</span>
</span><span class='line'>            <span class="c1">// バックグラウンド処理したい内容 ※後述します</span>
</span><span class='line'>            <span class="n">self</span><span class="p">.</span><span class="n">handleAppRefresh</span><span class="p">(</span><span class="nl">task:</span> <span class="n">task</span> <span class="n">as</span><span class="o">!</span> <span class="n">BGAppRefreshTask</span><span class="p">)</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">true</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>③ バックグラウンドタスクにスケジューリングします</strong></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">private</span> <span class="n">func</span> <span class="nf">scheduleAppRefresh</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// Info.plistで定義したIdentifierを指定</span>
</span><span class='line'>    <span class="n">let</span> <span class="n">request</span> <span class="o">=</span> <span class="n">BGAppRefreshTaskRequest</span><span class="p">(</span><span class="nl">identifier:</span> <span class="s">&quot;com.MeasurementSample.refresh&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="c1">// 最低で、どの程度の期間を置いてから実行するか指定</span>
</span><span class='line'>    <span class="n">request</span><span class="p">.</span><span class="n">earliestBeginDate</span> <span class="o">=</span> <span class="n">Date</span><span class="p">(</span><span class="nl">timeIntervalSinceNow:</span> <span class="mi">15</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">do</span> <span class="p">{</span>
</span><span class='line'>        <span class="c1">// スケジューラーに実行リクエストを登録</span>
</span><span class='line'>        <span class="n">try</span> <span class="n">BGTaskScheduler</span><span class="p">.</span><span class="n">shared</span><span class="p">.</span><span class="n">submit</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span> <span class="n">catch</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">print</span><span class="p">(</span><span class="s">&quot;Could not schedule app refresh: \(error)&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">func</span> <span class="nf">applicationDidEnterBackground</span><span class="p">(</span><span class="n">_</span> <span class="nl">application:</span> <span class="n">UIApplication</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// バックグラウンド起動に移ったときにルケジューリング登録</span>
</span><span class='line'>    <span class="n">scheduleAppRefresh</span><span class="p">()</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><strong>④ 実際に実行する処理を定義します</strong></p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// AppDelegate.swift</span>
</span><span class='line'><span class="c1">// サンプル用のOperation</span>
</span><span class='line'><span class="n">class</span> <span class="nl">PrintOperation:</span> <span class="n">Operation</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">let</span> <span class="kt">id</span><span class="o">:</span> <span class="n">Int</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">init</span><span class="p">(</span><span class="kt">id</span><span class="o">:</span> <span class="n">Int</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">self</span><span class="p">.</span><span class="kt">id</span> <span class="o">=</span> <span class="kt">id</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">override</span> <span class="n">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">print</span><span class="p">(</span><span class="s">&quot;this operation id is \(self.id)&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="err">@</span><span class="n">UIApplicationMain</span>
</span><span class='line'><span class="n">class</span> <span class="nl">AppDelegate:</span> <span class="n">UIResponder</span><span class="p">,</span> <span class="n">UIApplicationDelegate</span> <span class="p">{</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'>  <span class="n">private</span> <span class="n">func</span> <span class="n">handleAppRefresh</span><span class="p">(</span><span class="nl">task:</span> <span class="n">BGAppRefreshTask</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="c1">// 1日の間、何度も実行したい場合は、1回実行するごとに新たにスケジューリングに登録します</span>
</span><span class='line'>      <span class="n">scheduleAppRefresh</span><span class="p">()</span>
</span><span class='line'>
</span><span class='line'>      <span class="n">let</span> <span class="n">queue</span> <span class="o">=</span> <span class="n">OperationQueue</span><span class="p">()</span>
</span><span class='line'>      <span class="n">queue</span><span class="p">.</span><span class="n">maxConcurrentOperationCount</span> <span class="o">=</span> <span class="mi">1</span>
</span><span class='line'>
</span><span class='line'>      <span class="c1">// 時間内に実行完了しなかった場合は、処理を解放します</span>
</span><span class='line'>      <span class="c1">// バックグラウンドで実行する処理は、次回に回しても問題ない処理のはずなので、これでOK</span>
</span><span class='line'>      <span class="n">task</span><span class="p">.</span><span class="n">expirationHandler</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>          <span class="n">queue</span><span class="p">.</span><span class="n">cancelAllOperations</span><span class="p">()</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>      <span class="c1">// サンプルの処理をキューに詰めます</span>
</span><span class='line'>      <span class="n">let</span> <span class="n">array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
</span><span class='line'>      <span class="n">array</span><span class="p">.</span><span class="n">enumerated</span><span class="p">().</span><span class="n">forEach</span> <span class="p">{</span> <span class="n">arg</span> <span class="k">in</span>
</span><span class='line'>          <span class="n">let</span> <span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="o">=</span> <span class="n">arg</span>
</span><span class='line'>          <span class="n">let</span> <span class="n">operation</span> <span class="o">=</span> <span class="n">PrintOperation</span><span class="p">(</span><span class="kt">id</span><span class="o">:</span> <span class="n">value</span><span class="p">)</span>
</span><span class='line'>          <span class="k">if</span> <span class="n">offset</span> <span class="o">==</span> <span class="n">array</span><span class="p">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">{</span>
</span><span class='line'>              <span class="n">operation</span><span class="p">.</span><span class="n">completionBlock</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>                  <span class="c1">// 最後の処理が完了したら、必ず完了したことを伝える必要があります</span>
</span><span class='line'>                  <span class="n">task</span><span class="p">.</span><span class="n">setTaskCompleted</span><span class="p">(</span><span class="nl">success:</span> <span class="n">operation</span><span class="p">.</span><span class="n">isFinished</span><span class="p">)</span>
</span><span class='line'>              <span class="p">}</span>
</span><span class='line'>          <span class="p">}</span>
</span><span class='line'>          <span class="n">queue</span><span class="p">.</span><span class="n">addOperation</span><span class="p">(</span><span class="n">operation</span><span class="p">)</span>
</span><span class='line'>      <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>BackgroundTasksのデバッグ方法</h3>

<p>上記で実装が完了しました。<br/>
実際に挙動を試すためには、特別な手順が必要になります。</p>

<p>① アプリを起動します<br/>
② アプリをバックグラウンドに移します(登録トリガーのためです)<br/>
③ 再度アプリを起動します<br/>
④ Xcodeで <code>Pause Program Execution</code> をタップします</p>

<p><img src="http://grandbig.github.io/images/backgroundtasks_4.png" alt="Pause Program Executionをタップします" /></p>

<p>⑤ LLDBに以下コマンドを入力して実行します</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// Background Processing Tasksの場合</span>
</span><span class='line'><span class="n">e</span> <span class="o">-</span><span class="n">l</span> <span class="n">objc</span> <span class="o">--</span> <span class="p">(</span><span class="kt">void</span><span class="p">)[[</span><span class="n">BGTaskScheduler</span> <span class="n">sharedScheduler</span><span class="p">]</span> <span class="nl">_simulateLaunchForTaskWithIdentifier:</span><span class="s">@&quot;com.MeasurementSample.process&quot;</span><span class="p">]</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Background App Refresh Tasksの場合</span>
</span><span class='line'><span class="n">e</span> <span class="o">-</span><span class="n">l</span> <span class="n">objc</span> <span class="o">--</span> <span class="p">(</span><span class="kt">void</span><span class="p">)[[</span><span class="n">BGTaskScheduler</span> <span class="n">sharedScheduler</span><span class="p">]</span> <span class="nl">_simulateLaunchForTaskWithIdentifier:</span><span class="s">@&quot;com.MeasurementSample.refresh&quot;</span><span class="p">]</span>
</span></code></pre></td></tr></table></div></figure>


<p>⑥ 再度、Xcodeで <code>Pause Program Execution</code> をタップします</p>

<p>以上で実際の実行処理が見れるようになるはずです。</p>

<p>因みに、実行処理の期限切れを試したい場合は、以下のように⑤のLLDBで以下を入力して実行します</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// Background Processing Tasksの場合</span>
</span><span class='line'><span class="n">e</span> <span class="o">-</span><span class="n">l</span> <span class="n">objc</span> <span class="o">--</span> <span class="p">(</span><span class="kt">void</span><span class="p">)[[</span><span class="n">BGTaskScheduler</span> <span class="n">sharedScheduler</span><span class="p">]</span> <span class="nl">_simulateExpirationForTaskWithIdentifier:</span><span class="s">@&quot;com.MeasurementSample.process&quot;</span><span class="p">]</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Background App Refresh Tasksの場合</span>
</span><span class='line'><span class="n">e</span> <span class="o">-</span><span class="n">l</span> <span class="n">objc</span> <span class="o">--</span> <span class="p">(</span><span class="kt">void</span><span class="p">)[[</span><span class="n">BGTaskScheduler</span> <span class="n">sharedScheduler</span><span class="p">]</span> <span class="nl">_simulateExpirationForTaskWithIdentifier:</span><span class="s">@&quot;com.MeasurementSample.refresh&quot;</span><span class="p">]</span>
</span></code></pre></td></tr></table></div></figure>


<h3>ハマったところ</h3>

<p>筆者が試しに実装してハマったところを参考までに載せておこうと思います。<br/>
筆者の場合、なぜかシミュレータで実行しようとすると、以下のエラーが発生してしまいました。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">BGTaskSchedulerErrorDomain</span> <span class="n">Code</span><span class="o">=</span><span class="mi">1</span> <span class="s">&quot;(null)&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>これは、<a href="https://developer.apple.com/documentation/backgroundtasks/refreshing_and_maintaining_your_app_using_background_tasks">サンプルコード</a>で試しても同様でした。<br/>
ただ、実機で試したところ問題なく通ったんですよね&hellip;</p>

<h3>まとめ</h3>

<p>さて如何でしたでしょうか。<br/>
iOS13で追加された新APIは旧バージョンサポートのため、
すぐには利用されないかもしれませんが、iOS13の普及に伴い、利用シーンは確実に増えていくことでしょう。<br/>
そのため、サンプル程度の実装でも、使い方を学んでおくことは今後の役に立つと思っています。</p>

<p>と言ったところで本日はここまで。</p>

<ul>
<li>参考URL:

<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2019/707/">WWDC2019 &ndash; Advances in App Background Execution</a></li>
<li><a href="https://developer.apple.com/documentation/backgroundtasks">Apple Documentation &ndash; BackgroundTasks</a></li>
</ul>
</li>
</ul>


<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[iOSアプリ開発に便利なLLDBのp/po/ｖコマンドを覚えよう！]]></title>
    <link href="http://grandbig.github.io/blog/2019/09/07/po-v-command/"/>
    <updated>2019-09-07T20:02:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/09/07/po-v-command</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>筆者はこれまでLLDBコマンドでは <code>po</code> を利用することが多かったのですが、<br/>
WWDC2019のビデオである<a href="https://developer.apple.com/videos/play/wwdc2019/429/">LLDB: Beyond &ldquo;po&rdquo;</a>を視聴して改めて <code>v</code> コマンドの使いやすさを勉強しました。</p>

<p>今回はその <code>v</code> コマンドについて実例を交えながら見ていきたいと思います。
( <code>p</code> や <code>po</code> コマンドでなぜか変数の中身が見れない&hellip;と思っていた方、必見です。 )</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>p/po/vコマンドについて</h3>

<p>まず、各種コマンドについて簡単に見ていきます。</p>

<h4>poコマンドについて</h4>

<p><code>po</code> は <code>print object</code> の略だそうです。 <br/>
<code>po</code> は <code>expression</code> コマンドのエイリアス(ショートカット機能)であり、簡略化して書くことができます。</p>

<p>具体的には、以下の通りです。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">expression</span> <span class="o">--</span><span class="n">object</span><span class="o">-</span><span class="n">description</span> <span class="o">--</span> <span class="n">engineer</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">po</span> <span class="n">engineer</span>
</span></code></pre></td></tr></table></div></figure>


<p>また、 <code>po</code> は式を評価した結果のオブジェクトを返すため、非常に見やすい形式で出力されます。<br/>
早速例を見てみましょう。</p>

<p>下記のような構造体が定義されているとします。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">struct</span> <span class="n">iOSEngineer</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">var</span> <span class="nl">name:</span> <span class="n">string</span>
</span><span class='line'>  <span class="n">var</span> <span class="nl">age:</span> <span class="n">Int</span>
</span><span class='line'>  <span class="n">var</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="n">String</span><span class="p">]</span>
</span><span class='line'>  <span class="n">var</span> <span class="nl">swiftLevel:</span> <span class="n">Int</span>
</span><span class='line'>  <span class="n">var</span> <span class="nl">obcLevel:</span> <span class="n">Int</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>これを初期化した直後にブレークポイントで止めているとしましょう。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">let</span> <span class="n">engineer</span> <span class="o">=</span> <span class="n">iOSEngineer</span><span class="p">(</span><span class="nl">name:</span> <span class="s">&quot;Ichiro&quot;</span><span class="p">,</span> <span class="nl">age:</span> <span class="mi">33</span><span class="p">,</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="s">&quot;gps&quot;</span><span class="p">,</span> <span class="s">&quot;bluetooth&quot;</span><span class="p">,</span> <span class="s">&quot;animation&quot;</span><span class="p">],</span> <span class="nl">swiftLevel:</span> <span class="mi">3</span><span class="p">,</span> <span class="nl">obcLevel:</span> <span class="mi">2</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>ブレークポイントで止めた状態で <code>po</code> コマンドを打ち出すと、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">po</span> <span class="n">engineer</span>
</span><span class='line'><span class="err">▿</span> <span class="n">iOSEngineer</span>
</span><span class='line'>  <span class="o">-</span> <span class="n">name</span> <span class="o">:</span> <span class="s">&quot;Ichiro&quot;</span>
</span><span class='line'>  <span class="o">-</span> <span class="n">age</span> <span class="o">:</span> <span class="mi">33</span>
</span><span class='line'>  <span class="err">▿</span> <span class="n">specialty</span> <span class="o">:</span> <span class="mi">3</span> <span class="n">elements</span>
</span><span class='line'>    <span class="o">-</span> <span class="mi">0</span> <span class="o">:</span> <span class="s">&quot;gps&quot;</span>
</span><span class='line'>    <span class="o">-</span> <span class="mi">1</span> <span class="o">:</span> <span class="s">&quot;bluetooth&quot;</span>
</span><span class='line'>    <span class="o">-</span> <span class="mi">2</span> <span class="o">:</span> <span class="s">&quot;animation&quot;</span>
</span><span class='line'>  <span class="o">-</span> <span class="n">swiftLevel</span> <span class="o">:</span> <span class="mi">3</span>
</span><span class='line'>  <span class="o">-</span> <span class="n">obcLevel</span> <span class="o">:</span> <span class="mi">2</span>
</span></code></pre></td></tr></table></div></figure>


<p>という出力結果が得られます。</p>

<h4>pコマンドについて</h4>

<p><code>p</code> は <code>print</code> の略だそうで、<code>po</code> と同じくコンパイルして式を評価します。<br/>
<code>po</code> と異なるのは、出力結果の形式です。<br/>
以下はその例です。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">p</span> <span class="n">engineer</span>
</span><span class='line'><span class="p">(</span><span class="n">Sample</span><span class="p">.</span><span class="n">iOSEngineer</span><span class="p">)</span> <span class="n">$R4</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">name</span> <span class="o">=</span> <span class="s">&quot;Ichiro&quot;</span>
</span><span class='line'>  <span class="n">age</span> <span class="o">=</span> <span class="mi">33</span>
</span><span class='line'>  <span class="n">specialty</span> <span class="o">=</span> <span class="mi">3</span> <span class="n">values</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;gps&quot;</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;bluetooth&quot;</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;animation&quot;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="n">swiftLevel</span> <span class="o">=</span> <span class="mi">3</span>
</span><span class='line'>  <span class="n">obcLevel</span> <span class="o">=</span> <span class="mi">2</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>po</code> と違って <code>$R4</code> のような名前がつけられていることがわかります。<br/>
この <code>$R4</code> のような名前を引数に指定して <code>po</code> や <code>p</code> コマンドを実行することができます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">p</span> <span class="n">$R4</span><span class="p">.</span><span class="n">name</span>
</span><span class='line'><span class="p">(</span><span class="n">String</span><span class="p">)</span> <span class="n">$R10</span> <span class="o">=</span> <span class="s">&quot;Ichiro&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">po</span> <span class="n">$R4</span><span class="p">.</span><span class="n">name</span>
</span><span class='line'><span class="s">&quot;Ichiro&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>このように以前の結果から更に値を取得するような場面で使い勝手が非常に良いです。</p>

<p>もちろん <code>p</code> も <code>expression</code> コマンドのエイリアスになっています。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">expression</span> <span class="o">--</span> <span class="n">engineer</span>
</span><span class='line'><span class="p">(</span><span class="n">MeasurementSample</span><span class="p">.</span><span class="n">iOSEngineer</span><span class="p">)</span> <span class="n">$R16</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">name</span> <span class="o">=</span> <span class="s">&quot;Ichiro&quot;</span>
</span><span class='line'>  <span class="n">age</span> <span class="o">=</span> <span class="mi">33</span>
</span><span class='line'>  <span class="n">specialty</span> <span class="o">=</span> <span class="mi">3</span> <span class="n">values</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;gps&quot;</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;bluetooth&quot;</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;animation&quot;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="n">swiftLevel</span> <span class="o">=</span> <span class="mi">3</span>
</span><span class='line'>  <span class="n">obcLevel</span> <span class="o">=</span> <span class="mi">2</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h4>vコマンドについて</h4>

<p><code>v</code> は <code>variable</code> の略のようです。<br/>
出力形式自体は <code>p</code> コマンドと同じになっています。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">v</span> <span class="n">engineer</span>
</span><span class='line'><span class="p">(</span><span class="n">MeasurementSample</span><span class="p">.</span><span class="n">iOSEngineer</span><span class="p">)</span> <span class="n">engineer</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">name</span> <span class="o">=</span> <span class="s">&quot;Ichiro&quot;</span>
</span><span class='line'>  <span class="n">age</span> <span class="o">=</span> <span class="mi">33</span>
</span><span class='line'>  <span class="n">specialty</span> <span class="o">=</span> <span class="mi">3</span> <span class="n">values</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;gps&quot;</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;bluetooth&quot;</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;animation&quot;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="n">swiftLevel</span> <span class="o">=</span> <span class="mi">3</span>
</span><span class='line'>  <span class="n">obcLevel</span> <span class="o">=</span> <span class="mi">2</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>po</code> や <code>p</code> と同じくエイリアスなのですが、 <code>expression</code> ではなく <code>frame variable</code> コマンドのエイリアスです。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">frame</span> <span class="n">variable</span> <span class="n">engineer</span>
</span><span class='line'><span class="p">(</span><span class="n">MeasurementSample</span><span class="p">.</span><span class="n">iOSEngineer</span><span class="p">)</span> <span class="n">engineer</span> <span class="o">=</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">name</span> <span class="o">=</span> <span class="s">&quot;Ichiro&quot;</span>
</span><span class='line'>  <span class="n">age</span> <span class="o">=</span> <span class="mi">33</span>
</span><span class='line'>  <span class="n">specialty</span> <span class="o">=</span> <span class="mi">3</span> <span class="n">values</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;gps&quot;</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;bluetooth&quot;</span>
</span><span class='line'>    <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s">&quot;animation&quot;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="n">swiftLevel</span> <span class="o">=</span> <span class="mi">3</span>
</span><span class='line'>  <span class="n">obcLevel</span> <span class="o">=</span> <span class="mi">2</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>一見、 <code>p</code> コマンドを使えば良いのでは？と思われそうですが、 <code>v</code> コマンドは以下の特徴があります。</p>

<ul>
<li>コンパイルしないため、結果の出力が速い</li>
<li>メモリから変数を読み込み、動的解決を複数回繰り返すことで、サブフィールドの読み込みが可能</li>
</ul>


<p>上記のどちらにも言えることですが、代わりに式の評価ができないので使い分けが必要です。</p>

<h3>vコマンドの便利機能</h3>

<p>もう少し具体的に <code>v</code> コマンドの良さを見ていきましょう。</p>

<p>次のように定義された構造体があったとします。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">protocol</span> <span class="n">Engineer</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">name:</span> <span class="n">String</span> <span class="p">{</span> <span class="n">get</span> <span class="n">set</span> <span class="p">}</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">age:</span> <span class="n">Int</span> <span class="p">{</span> <span class="n">get</span> <span class="n">set</span> <span class="p">}</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="n">String</span><span class="p">]</span> <span class="p">{</span> <span class="n">get</span> <span class="n">set</span> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">struct</span> <span class="nl">iOSEngineer:</span> <span class="n">Engineer</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">name:</span> <span class="n">String</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">age:</span> <span class="n">Int</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="n">String</span><span class="p">]</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">swiftLevel:</span> <span class="n">Int</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">obcLevel:</span><span class="n">Int</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>上記の通り、 <code>iOSEngineer</code> はプロトコル <code>Engineer</code> に準拠しています。<br/>
加えて独自に <code>swiftLevel</code> と <code>obcLevel</code> という変数を持ちます。</p>

<p>この構造体を下記のように初期化している場面があったとします。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">let</span> <span class="nl">engineer:</span> <span class="n">Engineer</span> <span class="o">=</span> <span class="n">iOSEngineer</span><span class="p">(</span><span class="nl">name:</span> <span class="s">&quot;Ichiro&quot;</span><span class="p">,</span> <span class="nl">age:</span> <span class="mi">33</span><span class="p">,</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="s">&quot;gps&quot;</span><span class="p">,</span> <span class="s">&quot;bluetooth&quot;</span><span class="p">,</span> <span class="s">&quot;animation&quot;</span><span class="p">],</span> <span class="nl">swiftLevel:</span> <span class="mi">3</span><span class="p">,</span> <span class="nl">obcLevel:</span> <span class="mi">2</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>これを <code>p</code> や <code>po</code> コマンドで <code>iOSEngineer</code> 独自のフィールドである <code>swiftLevel</code> を出力させようとすると、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">p</span> <span class="n">engineer</span><span class="p">.</span><span class="n">swiftLevel</span>
</span><span class='line'><span class="nl">error:</span> <span class="o">&lt;</span><span class="n">EXPR</span><span class="o">&gt;:</span><span class="mi">3</span><span class="o">:</span><span class="mi">1</span><span class="o">:</span> <span class="nl">error:</span> <span class="n">value</span> <span class="n">of</span> <span class="n">type</span> <span class="err">&#39;</span><span class="n">Engineer</span><span class="err">&#39;</span> <span class="n">has</span> <span class="n">no</span> <span class="n">member</span> <span class="err">&#39;</span><span class="n">swiftLevel</span><span class="err">&#39;</span>
</span><span class='line'><span class="n">engineer</span><span class="p">.</span><span class="n">swiftLevel</span>
</span><span class='line'><span class="o">^~~~~~~~</span> <span class="o">~~~~~~~~~~</span>
</span></code></pre></td></tr></table></div></figure>


<p>このようなエラーが表示されます。<br/>
<code>p</code> や <code>po</code> コマンドは1回しか動的解決をしないため、<br/>
<code>Engineer</code> 型として格納された <code>engineer</code> には <code>swiftLevel</code> はメンバ変数として持ち合わせていないと見なされてしまうのです。</p>

<p>もちろん、<code>p</code> や <code>po</code> コマンドは式の評価が可能なので</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">p</span> <span class="p">(</span><span class="n">engineer</span> <span class="n">as</span><span class="o">!</span> <span class="n">iOSEngineer</span><span class="p">).</span><span class="n">swiftLevel</span>
</span><span class='line'><span class="p">(</span><span class="n">Int</span><span class="p">)</span> <span class="n">$R20</span> <span class="o">=</span> <span class="mi">3</span>
</span></code></pre></td></tr></table></div></figure>


<p>のようにキャストすることで中身を見ることはできます。<br/>
ですが、少々手間ではありますよね&hellip;<br/>
(深い階層のサブフィールドを見ようとすればするほど&hellip;)</p>

<p>これが <code>v</code> コマンドでは、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">v</span> <span class="n">engineer</span><span class="p">.</span><span class="n">swiftLevel</span>
</span><span class='line'><span class="p">(</span><span class="n">Int</span><span class="p">)</span> <span class="n">engineer</span><span class="p">.</span><span class="n">swiftLevel</span> <span class="o">=</span> <span class="mi">3</span>
</span></code></pre></td></tr></table></div></figure>


<p>とキャストせずとも結果を得ることができます。</p>

<h3>filter機能</h3>

<p>もう一つ、LLDBコマンドを利用する上で役立つのが <code>filter</code> 機能です。<br/>
折角なので使い方を見ていきましょう。</p>

<h4>filter機能の紹介</h4>

<p>フィールドの多い変数や、その変数と同じ型の要素を数十個持つ配列などを出力させる場合、とてもではないですが特定のフィールドを見つけることは困難です。</p>

<p>そんな時には <code>filter</code> 機能を利用して、特定の型であれば、特定のフィールドのみを表示するといったことができます。<br/>
指定の方法は簡単で、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">type</span> <span class="n">filter</span> <span class="n">add</span> <span class="o">&lt;</span><span class="n">Xcode</span><span class="err">のプロジェクト名</span><span class="o">&gt;</span><span class="p">.</span><span class="o">&lt;</span><span class="err">型の名前</span><span class="o">&gt;</span> <span class="o">--</span><span class="n">child</span> <span class="o">&lt;</span><span class="err">フィールド名</span><span class="o">&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>のようにするだけです。<br/>
筆者の例で言えば、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">type</span> <span class="n">filter</span> <span class="n">add</span> <span class="n">Sample</span><span class="p">.</span><span class="n">iOSEngineer</span> <span class="o">--</span><span class="n">child</span> <span class="n">name</span>
</span></code></pre></td></tr></table></div></figure>


<p>のような感じです。</p>

<p>これにより、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">v</span> <span class="n">engineer</span>
</span><span class='line'><span class="p">(</span><span class="n">MeasurementSample</span><span class="p">.</span><span class="n">iOSEngineer</span><span class="p">)</span> <span class="n">engineer</span> <span class="o">=</span> <span class="p">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">&quot;Ichiro&quot;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように今必要のないフィールドを省略して、結果を得ることができます。<br/>
※便宜上、 <code>v</code> コマンドを使いましたが <code>p</code> コマンドでも同じ結果を得ることができます。</p>

<p>因みに、機能利用後は忘れずに <code>filter</code> を解除しましょう。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">type</span> <span class="n">filter</span> <span class="n">delete</span> <span class="o">&lt;</span><span class="n">Xcode</span><span class="err">プロジェクト名</span><span class="o">&gt;</span><span class="p">.</span><span class="o">&lt;</span><span class="err">型の名前</span><span class="o">&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>で解除できます。</p>

<h4>filter機能の限界</h4>

<p>因みに、 <code>filter</code> 機能も万能ではなく、場合によっては複数回 <code>filter</code> をセットすることで結果を得なければいけないこともあります。<br/>
例えば、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">protocol</span> <span class="n">Engineer</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">name:</span> <span class="n">String</span> <span class="p">{</span> <span class="n">get</span> <span class="n">set</span> <span class="p">}</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">age:</span> <span class="n">Int</span> <span class="p">{</span> <span class="n">get</span> <span class="n">set</span> <span class="p">}</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="n">String</span><span class="p">]</span> <span class="p">{</span> <span class="n">get</span> <span class="n">set</span> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">struct</span> <span class="nl">iOSEngineer:</span> <span class="n">Engineer</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">name:</span> <span class="n">String</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">age:</span> <span class="n">Int</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="n">String</span><span class="p">]</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">swiftLevel:</span> <span class="n">Int</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">obcLevel:</span><span class="n">Int</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="k">struct</span> <span class="nl">WebEngineer:</span> <span class="n">Engineer</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">name:</span> <span class="n">String</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">age:</span> <span class="n">Int</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="n">String</span><span class="p">]</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">htmlLevel:</span> <span class="n">Int</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">cssLevel:</span> <span class="n">Int</span>
</span><span class='line'>    <span class="n">var</span> <span class="nl">jsLevel:</span> <span class="n">Int</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように新たに <code>Engineer</code> プロトコルに準拠した <code>WebEngineer</code> を定義し、以下のような配列を作成します。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">let</span> <span class="n">engineer1</span> <span class="o">=</span> <span class="n">WebEngineer</span><span class="p">(</span><span class="nl">name:</span> <span class="s">&quot;Taro&quot;</span><span class="p">,</span> <span class="nl">age:</span> <span class="mi">30</span><span class="p">,</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="s">&quot;react&quot;</span><span class="p">],</span> <span class="nl">htmlLevel:</span> <span class="mi">2</span><span class="p">,</span> <span class="nl">cssLevel:</span> <span class="mi">1</span><span class="p">,</span> <span class="nl">jsLevel:</span> <span class="mi">1</span><span class="p">)</span>
</span><span class='line'><span class="n">let</span> <span class="n">engineer2</span> <span class="o">=</span> <span class="n">WebEngineer</span><span class="p">(</span><span class="nl">name:</span> <span class="s">&quot;Jiro&quot;</span><span class="p">,</span> <span class="nl">age:</span> <span class="mi">35</span><span class="p">,</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="s">&quot;vue&quot;</span><span class="p">],</span> <span class="nl">htmlLevel:</span> <span class="mi">3</span><span class="p">,</span> <span class="nl">cssLevel:</span> <span class="mi">2</span><span class="p">,</span> <span class="nl">jsLevel:</span> <span class="mi">4</span><span class="p">)</span>
</span><span class='line'><span class="n">let</span> <span class="n">engineer3</span> <span class="o">=</span> <span class="n">iOSEngineer</span><span class="p">(</span><span class="nl">name:</span> <span class="s">&quot;Saburo&quot;</span><span class="p">,</span> <span class="nl">age:</span> <span class="mi">25</span><span class="p">,</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="s">&quot;gps&quot;</span><span class="p">,</span> <span class="s">&quot;bluetooth&quot;</span><span class="p">],</span> <span class="nl">swiftLevel:</span> <span class="mi">3</span><span class="p">,</span> <span class="nl">obcLevel:</span> <span class="mi">2</span><span class="p">)</span>
</span><span class='line'><span class="n">let</span> <span class="n">engineer4</span> <span class="o">=</span> <span class="n">WebEngineer</span><span class="p">(</span><span class="nl">name:</span> <span class="s">&quot;Shiro&quot;</span><span class="p">,</span> <span class="nl">age:</span> <span class="mi">28</span><span class="p">,</span> <span class="nl">specialty:</span> <span class="p">[</span><span class="s">&quot;react&quot;</span><span class="p">,</span> <span class="s">&quot;vue&quot;</span><span class="p">,</span> <span class="s">&quot;angular&quot;</span><span class="p">],</span> <span class="nl">htmlLevel:</span> <span class="mi">4</span><span class="p">,</span> <span class="nl">cssLevel:</span> <span class="mi">4</span><span class="p">,</span> <span class="nl">jsLevel:</span> <span class="mi">5</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="n">let</span> <span class="n">engineers</span> <span class="o">=</span> <span class="p">[</span><span class="n">engineer1</span><span class="p">,</span> <span class="n">engineer2</span><span class="p">,</span> <span class="n">engineer3</span><span class="p">,</span> <span class="n">engineer4</span><span class="p">]</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>engineers</code> をそのまま <code>v</code> コマンドで出力すると少し見えにくそうなので <code>filter</code> 機能が利用したいのですが、<br/>
<code>Engineer</code> に準拠しているからと言って</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">type</span> <span class="n">filter</span> <span class="n">add</span> <span class="n">Sample</span><span class="p">.</span><span class="n">Engineer</span> <span class="o">--</span><span class="n">child</span> <span class="n">name</span>
</span></code></pre></td></tr></table></div></figure>


<p>としても思ったようには動かず、全フィールドが出力されてしまいます。</p>

<p>そこで、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">type</span> <span class="n">filter</span> <span class="n">add</span> <span class="n">Sample</span><span class="p">.</span><span class="n">iOSEngineer</span> <span class="o">--</span><span class="n">child</span> <span class="n">name</span>
</span><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">type</span> <span class="n">filter</span> <span class="n">add</span> <span class="n">Sample</span><span class="p">.</span><span class="n">WebEngineer</span> <span class="o">--</span><span class="n">child</span> <span class="n">name</span>
</span></code></pre></td></tr></table></div></figure>


<p>としてやれば、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">(</span><span class="n">lldb</span><span class="p">)</span> <span class="n">v</span> <span class="n">engineers</span>
</span><span class='line'><span class="p">([</span><span class="n">Engineer</span><span class="p">])</span> <span class="n">engineers</span> <span class="o">=</span> <span class="mi">4</span> <span class="n">values</span> <span class="p">{</span>
</span><span class='line'>  <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">&quot;Taro&quot;</span><span class="p">)</span>
</span><span class='line'>  <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">&quot;Jiro&quot;</span><span class="p">)</span>
</span><span class='line'>  <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">&quot;Saburo&quot;</span><span class="p">)</span>
</span><span class='line'>  <span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">&quot;Shiro&quot;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>このように期待した結果を得ることができます。</p>

<h3>まとめ</h3>

<p>さて如何でしたでしょうか。<br/>
<code>p</code> , <code>po</code> , <code>v</code> コマンドは各々用途がありそうですが、特に <code>v</code> コマンドの利便性を感じてもらえたら幸いです。<br/>
筆者も積極的に <code>v</code> コマンドを使っていきたいと思います。</p>

<p>といったところで本日はここまで。</p>

<p>参考URL:</p>

<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2019/429/">LLDB: Beyond &ldquo;po&rdquo;</a></li>
</ul>


<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[iOSアプリのパフォーマンス計測に『os.signpost』を活用しよう！]]></title>
    <link href="http://grandbig.github.io/blog/2019/09/07/os-signpost/"/>
    <updated>2019-09-07T16:22:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/09/07/os-signpost</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>これまでメソッドの処理時間を計測するには、<br/>
<code>Date</code> 関数の <code>timeIntervalSince</code> メソッドを以下のように利用する場面が度々ありました。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">let</span> <span class="n">beginDate</span> <span class="o">=</span> <span class="n">Date</span><span class="p">()</span>
</span><span class='line'><span class="n">sample</span><span class="p">()</span>
</span><span class='line'><span class="n">let</span> <span class="n">endDate</span> <span class="o">=</span> <span class="n">Date</span><span class="p">()</span>
</span><span class='line'><span class="n">let</span> <span class="n">time</span> <span class="o">=</span> <span class="n">endDate</span><span class="p">.</span><span class="n">timeIntervalSince</span><span class="p">(</span><span class="n">beginDate</span><span class="p">)</span>
</span><span class='line'><span class="n">print</span><span class="p">(</span><span class="n">time</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>しかし実は <code>iOS12</code> から <code>os.signpost</code> を利用してもっと便利に処理時間を計測することができるようになりました。<br/>
本日は <code>os.signpost</code> を利用したことをメモとして記録しておきます。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>os.signpostのメリット・デメリット</h3>

<p><code>os.signpost</code> を利用すると何が良いかと言うと、</p>

<ul>
<li>最小/最大/平均の処理時間が自動で計測できる</li>
<li>CPU使用率など <code>Instruments</code> による他の計測結果と並行して状況を把握できる</li>
</ul>


<p>などが上げられます。</p>

<p>一方でデメリットは <code>os.signpost</code> がiOS12以降でしか利用できないということです。<br/>
よって、iOS11以下をサポートするアプリの場合、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">if</span> <span class="err">#</span><span class="n">available</span><span class="p">(</span><span class="n">iOS</span> <span class="mi">12</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>の条件分岐をした上で <code>os.signpost</code> を利用する必要があります。<br/>
しかし、これは時間が解決してくれるでしょう。<br/>
(あまり気にするデメリットではないということです。)</p>

<h3>os.signpostの使い方</h3>

<p>それでは早速、 <code>os.signpost</code> を使ってみましょう。<br/>
<code>os.signpost</code> は特にSDKを追加することなく、 <code>import</code> が可能です。</p>

<p>① <code>import os.signpost</code> を追加する</p>

<p>② OSLogオブジェクトを初期化する</p>

<p><a href="https://developer.apple.com/documentation/os/oslog/2320726-init">Apple Developer Document: OSLog &ndash; init</a>に記載のある通り、初期化をします。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">let</span> <span class="n">osLog</span> <span class="o">=</span> <span class="n">OSLog</span><span class="p">(</span><span class="nl">subsystem:</span> <span class="s">&quot;com.takahiro.MeasurementSample&quot;</span><span class="p">,</span> <span class="nl">category:</span> <span class="s">&quot;loop&quot;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p><code>subsystem</code> には <code>com.your_company.your_subsystem_name</code> の形で定義するようにと例として書かれています。<br/>
<code>category</code> はログのカテゴリ分けに利用できるため、カテゴリ種別で命名すると良いでしょう。</p>

<p>③ 計測開始時に <code>OSSignpostType</code> に <code>.begin</code> を指定した <code>os_signpost</code> メソッドを呼び出す</p>

<p><a href="https://developer.apple.com/documentation/os/3019241-os_signpost">Apple Developer Document: os_signpost</a>に記載のあるメソッドを呼び出します。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">os_signpost</span><span class="p">(.</span><span class="n">begin</span><span class="p">,</span> <span class="nl">log:</span> <span class="n">osLog</span><span class="p">,</span> <span class="nl">name:</span> <span class="s">&quot;sample&quot;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p></p>

<p>④ 計測完了時に <code>OSSignpostType</code> に <code>.end</code> を指定した <code>os_signpost</code> メソッドを呼び出す</p>

<p>③と同じメソッドですが、計測完了時には <code>OSSignpostType</code> に <code>end</code> を指定します。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">os_signpost</span><span class="p">(.</span><span class="n">end</span><span class="p">,</span> <span class="nl">log:</span> <span class="n">osLog</span><span class="p">,</span> <span class="nl">name:</span> <span class="s">&quot;sample&quot;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>これで、後は <code>Instruments</code> を起動して、<code>os_signpost</code> を追加して計測すればOKです。</p>

<p>今回は、下記のようなサンプルを用意して計測してみます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// ViewController.swift</span>
</span><span class='line'><span class="n">import</span> <span class="n">UIKit</span>
</span><span class='line'><span class="n">import</span> <span class="n">os</span><span class="p">.</span><span class="n">signpost</span>
</span><span class='line'>
</span><span class='line'><span class="n">class</span> <span class="nl">ViewController:</span> <span class="n">UIViewController</span> <span class="p">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">override</span> <span class="n">func</span> <span class="n">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">super</span><span class="p">.</span><span class="n">viewDidLoad</span><span class="p">()</span>
</span><span class='line'>        <span class="c1">// Do any additional setup after loading the view.</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="err">@</span><span class="kt">IBAction</span> <span class="n">func</span> <span class="n">tapButton</span><span class="p">(</span><span class="n">_</span> <span class="nl">sender:</span> <span class="n">Any</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="n">let</span> <span class="n">osLog</span> <span class="o">=</span> <span class="n">OSLog</span><span class="p">(</span><span class="nl">subsystem:</span> <span class="s">&quot;com.takahiro.MeasurementSample&quot;</span><span class="p">,</span> <span class="nl">category:</span> <span class="s">&quot;loop&quot;</span><span class="p">)</span>
</span><span class='line'>        <span class="n">os_signpost</span><span class="p">(.</span><span class="n">begin</span><span class="p">,</span> <span class="nl">log:</span> <span class="n">osLog</span><span class="p">,</span> <span class="nl">name:</span> <span class="s">&quot;sample&quot;</span><span class="p">)</span>
</span><span class='line'>        <span class="n">sample</span><span class="p">()</span>
</span><span class='line'>        <span class="n">os_signpost</span><span class="p">(.</span><span class="n">end</span><span class="p">,</span> <span class="nl">log:</span> <span class="n">osLog</span><span class="p">,</span> <span class="nl">name:</span> <span class="s">&quot;sample&quot;</span><span class="p">)</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">private</span> <span class="n">func</span> <span class="n">sample</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>        <span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span> <span class="p">..</span><span class="o">&lt;</span> <span class="mi">100000</span> <span class="p">{</span>
</span><span class='line'>            <span class="n">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>os.signpostでの計測結果</h3>

<p><code>Instruments</code> で計測した結果は下記のように見ることができます。</p>

<p><img src="http://grandbig.github.io/images/os-signpost.png" alt="Instrumentsのos.signpostの計測結果" /></p>

<p>上記結果から、計測処理が <code>平均88.97 [ms]</code> かかっていることがわかります。<br/>
またこの処理の実行中はCPUが100%に張り付いていることも見て取れます。</p>

<p>今回はサンプルなので <code>print</code> で1万回出力するメソッドを計測しました。<br/>
本物のアプリではボトルネックとなる処理を見つけ、如何にして処理を改善できないか検討することになるでしょう。</p>

<h3>まとめ</h3>

<p>いかがでしたでしょうか。<br/>
iOS13のリリースが近づいてきた(iOS12の普及が十分に達しつつある)今だからこそ改めて <code>signpost</code> を積極的に利用していきましょう。<br/>
といったところで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[iOS形態素解析で見る幽白、海藤戦のタブー能力]]></title>
    <link href="http://grandbig.github.io/blog/2019/08/10/analyze-morphologic/"/>
    <updated>2019-08-10T21:13:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/08/10/analyze-morphologic</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>最近、業務で形態素解析の話が出ました。<br/>
形態素解析はサーバサイドで実行していることが多く、あまりアプリでは馴染みがないと思っていたのですが、<br/>
意外とそんなことはなく、iOSでも簡単に試すことができることを知り、今更驚きました。</p>

<p>因みに、筆者が形態素解析と聞いて真っ先に思い出したのは、<br/>
「幽遊白書の海藤戦のタブー能力」でした笑。</p>

<p>今回はiOSでの形態素解析を、海藤戦のタブー能力を織り交ぜつつ説明したいと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>iOSで形態素解析</h3>

<p>iOSでは、形態素解析のための標準APIとして <code>NSLinguisticTagger</code> クラスが用意されています。 <br/>
これを利用すると、解析は非常に簡単で下記のように数行で実行できてしまいます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// 解析対象の日本語文章</span>
</span><span class='line'><span class="n">let</span> <span class="n">text</span> <span class="o">=</span> <span class="s">&quot;カウンターでワインを片手に人を待っています&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// 言語に日本語を指定</span>
</span><span class='line'><span class="n">let</span> <span class="n">tagSchemes</span> <span class="o">=</span> <span class="n">NSLinguisticTagger</span><span class="p">.</span><span class="n">availableTagSchemes</span><span class="p">(</span><span class="nl">forLanguage:</span> <span class="s">&quot;ja&quot;</span><span class="p">)</span>
</span><span class='line'><span class="n">let</span> <span class="n">linguisticTagger</span> <span class="o">=</span> <span class="n">NSLinguisticTagger</span><span class="p">.</span><span class="n">init</span><span class="p">(</span><span class="nl">tagSchemes:</span> <span class="n">tagSchemes</span><span class="p">,</span> <span class="nl">options:</span> <span class="mi">0</span><span class="p">)</span>
</span><span class='line'><span class="n">linguisticTagger</span><span class="p">.</span><span class="n">string</span> <span class="o">=</span> <span class="n">text</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// 解析を実施</span>
</span><span class='line'><span class="n">linguisticTagger</span><span class="p">.</span><span class="n">enumerateTags</span><span class="p">(</span><span class="k">in</span><span class="o">:</span> <span class="n">NSRange</span><span class="p">(</span><span class="nl">location:</span> <span class="mi">0</span><span class="p">,</span> <span class="nl">length:</span> <span class="n">text</span><span class="p">.</span><span class="n">count</span><span class="p">),</span> <span class="nl">scheme:</span> <span class="p">.</span><span class="n">tokenType</span><span class="p">,</span> <span class="nl">options:</span> <span class="p">[])</span> <span class="p">{</span> <span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">tokenRange</span><span class="p">,</span> <span class="n">sentenceRange</span><span class="p">,</span> <span class="n">stop</span><span class="p">)</span> <span class="k">in</span>
</span><span class='line'>  <span class="n">let</span> <span class="n">word</span> <span class="o">=</span> <span class="p">(</span><span class="n">text</span> <span class="n">as</span> <span class="n">NSString</span><span class="p">).</span><span class="n">substring</span><span class="p">(</span><span class="nl">with:</span> <span class="n">tokenRange</span><span class="p">)</span>
</span><span class='line'>  <span class="n">print</span><span class="p">(</span><span class="s">&quot;\(word): \(String(describing: tag!.rawValue))&quot;</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>上記の結果は、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="err">カウンター</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">で</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">ワイン</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">を</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">片手</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">に</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">人</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">を</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">待っ</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">て</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">い</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">ます</span><span class="o">:</span> <span class="n">Word</span>
</span></code></pre></td></tr></table></div></figure>


<p>のようになりました。<br/>
なかなか正確に単語ごとに分解できているように見えますね。</p>

<p>因みに、全てひらがなの文章があった場合にはどうなるのかというと&hellip;</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// 例文</span>
</span><span class='line'><span class="n">let</span> <span class="n">text</span> <span class="o">=</span> <span class="s">&quot;かうんたーでわいんをかたてにひとをまっています&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>結果は、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="err">か</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">うん</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">た</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">ー</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">でわ</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">いん</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">を</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">か</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">たて</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">に</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">ひと</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">を</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">まっ</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">て</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">い</span><span class="o">:</span> <span class="n">Word</span>
</span><span class='line'><span class="err">ます</span><span class="o">:</span> <span class="n">Word</span>
</span></code></pre></td></tr></table></div></figure>


<p>のようになりました。<br/>
なかなかに難易度が高いようですね&hellip;</p>

<h3>海藤戦</h3>

<p>では、基本的な説明が終わったところで、いよいよ海藤戦の再現をしてみましょう。</p>

<p>まずは、先程の実装を元に、形態素解析をかけた日本語の文章が特定の単語を含むかどうか調べる処理を以下の通り作ります。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">private</span> <span class="n">func</span> <span class="nf">analyzeMorphologic</span><span class="p">(</span><span class="n">_</span> <span class="nl">text:</span> <span class="n">String</span><span class="p">,</span> <span class="nl">target:</span> <span class="n">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
</span><span class='line'>  <span class="n">var</span> <span class="n">words</span> <span class="o">=</span> <span class="p">[</span><span class="n">String</span><span class="p">]()</span>
</span><span class='line'>
</span><span class='line'>  <span class="n">let</span> <span class="n">tagSchemes</span> <span class="o">=</span> <span class="n">NSLinguisticTagger</span><span class="p">.</span><span class="n">availableTagSchemes</span><span class="p">(</span><span class="nl">forLanguage:</span> <span class="s">&quot;ja&quot;</span><span class="p">)</span>
</span><span class='line'>  <span class="n">let</span> <span class="n">linguisticTagger</span> <span class="o">=</span> <span class="n">NSLinguisticTagger</span><span class="p">.</span><span class="n">init</span><span class="p">(</span><span class="nl">tagSchemes:</span> <span class="n">tagSchemes</span><span class="p">,</span> <span class="nl">options:</span> <span class="mi">0</span><span class="p">)</span>
</span><span class='line'>  <span class="n">linguisticTagger</span><span class="p">.</span><span class="n">string</span> <span class="o">=</span> <span class="n">text</span>
</span><span class='line'>  <span class="n">linguisticTagger</span><span class="p">.</span><span class="n">enumerateTags</span><span class="p">(</span><span class="k">in</span><span class="o">:</span> <span class="n">NSRange</span><span class="p">(</span><span class="nl">location:</span> <span class="mi">0</span><span class="p">,</span> <span class="nl">length:</span> <span class="n">text</span><span class="p">.</span><span class="n">count</span><span class="p">),</span> <span class="nl">scheme:</span> <span class="p">.</span><span class="n">tokenType</span><span class="p">,</span> <span class="nl">options:</span> <span class="p">[])</span> <span class="p">{</span> <span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">tokenRange</span><span class="p">,</span> <span class="n">sentenceRange</span><span class="p">,</span> <span class="n">stop</span><span class="p">)</span> <span class="k">in</span>
</span><span class='line'>    <span class="n">let</span> <span class="n">word</span> <span class="o">=</span> <span class="p">(</span><span class="n">text</span> <span class="n">as</span> <span class="n">NSString</span><span class="p">).</span><span class="n">substring</span><span class="p">(</span><span class="nl">with:</span> <span class="n">tokenRange</span><span class="p">)</span>
</span><span class='line'>    <span class="n">words</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">words</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>続いて、単純に文字列を含んでいるかどうか調べる処理も以下の通り作ります。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">private</span> <span class="n">func</span> <span class="n">contains</span><span class="p">(</span><span class="n">_</span> <span class="nl">text:</span> <span class="n">String</span><span class="p">,</span> <span class="nl">target:</span> <span class="n">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Bool</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="n">text</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>この2つのメソッドに、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="nl">text:</span> <span class="err">ああついでに氷をいれてくれ</span>
</span><span class='line'><span class="nl">target:</span> <span class="err">あつい</span>
</span></code></pre></td></tr></table></div></figure>


<p>を引数として設定してみましょう。</p>

<p>結果は、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="nl">analyzeMorphologic:</span> <span class="n">false</span>
</span><span class='line'><span class="n">contains</span>          <span class="o">:</span> <span class="n">true</span>
</span></code></pre></td></tr></table></div></figure>


<p>となりました。</p>

<p>前者の <code>analyzeMorphologic</code> は形態素解析をすることで、『意味を持った単語が文章内に存在しているか』を見るのに対して、<br/>
後者の <code>contains</code> は <code>target</code> となる文字列が『文章内に存在しているか』だけを見ています。</p>

<p>よって、海藤のタブー能力が前者だと気を抜いていると、<br/>
実は後者がルールであったがために引っかかり、魂を抜かれてしまうことになってしまうのです。</p>

<h3>まとめ</h3>

<p>海藤のタブー戦であれば、むしろ形態素解析をする必要はなかったのですが、<br/>
現実にソフトウェアを実装する現場では形態素解析が重要な場面は多分にあることでしょう。</p>

<p>と言ったところで、本日はここまで。</p>

<h3>参考URL</h3>

<ul>
<li><a href="https://developer.apple.com/documentation/foundation/nslinguistictagger/1410036-enumeratetags">Apple Developer Document: NSLinguisticTagger &ndash; enumerateTags</a></li>
</ul>


<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[シェルスクリプトで特定フォルダ配下のファイル名をチェックしよう！]]></title>
    <link href="http://grandbig.github.io/blog/2019/07/21/shellscript-1/"/>
    <updated>2019-07-21T22:52:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/07/21/shellscript-1</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>大量のファイルを新規作成して、そのファイルが一定のルールを持った名称であるかどうかをチェックする際に、流石に目視で確認するのは大変過ぎるので、<br/>
シェルスクリプトで確認できるようにしてみました。</p>

<p>その時のメモとして残しておこうと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>実装したシェルスクリプトの説明</h3>

<p>例として、今回は、iOSアプリ内で利用する画像ファイル名に <code>@2x</code> , <code>@3x</code> が付与されていることを確認するためのシェルスクリプトを作りました。</p>

<p>実際の処理内容は下記の通りで、ルールにそぐわないファイルを出力してくれます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='sh'><span class='line'><span class="c"># check_filename.sh</span>
</span><span class='line'><span class="c">#!/bin/sh</span>
</span><span class='line'>
</span><span class='line'><span class="c"># 引数として指定されたフォルダ配下を見る</span>
</span><span class='line'><span class="nv">folderPath</span><span class="o">=</span><span class="nv">$1</span>
</span><span class='line'>
</span><span class='line'><span class="nb">echo</span> <span class="s2">&quot;問題のあるファイルは...&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="k">for </span>filepath in <span class="nv">$folderPath</span>; <span class="k">do</span>
</span><span class='line'>
</span><span class='line'>  <span class="c"># 〜@2x.png または 〜@3x.pngのファイル以外を正規表現で抽出する</span>
</span><span class='line'>  <span class="k">if</span> <span class="o">[[</span> ! <span class="nv">$filepath</span> <span class="o">=</span>~ .+@<span class="o">[</span>23<span class="o">]</span>x.png <span class="o">]]</span>; <span class="k">then</span>
</span><span class='line'><span class="k">    </span><span class="nb">echo</span> <span class="nv">$filepath</span>
</span><span class='line'>  <span class="k">fi</span>
</span><span class='line'><span class="k">done</span>
</span><span class='line'>
</span><span class='line'><span class="nb">echo</span> <span class="s2">&quot;です。&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>実行方法は下記の通りです。</p>

<p>相対パスの場合は、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='sh'><span class='line'><span class="nv">$ </span>./check_filename.sh <span class="s2">&quot;./*&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>絶対パスの場合は、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='sh'><span class='line'><span class="nv">$ </span>./check_filename.sh <span class="s2">&quot;/Users/xxxxxxx/Desktop/test_dir/*&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように指定できます。</p>

<h3>まとめ</h3>

<p>極力、ミスの起こりうる手動ではなく、自動で効率的に業務を遂行していきたいですね。<br/>
と言うことで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Xcodeの環境変数を有効に利用しよう！]]></title>
    <link href="http://grandbig.github.io/blog/2019/07/15/xcode-env/"/>
    <updated>2019-07-15T16:24:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/07/15/xcode-env</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>iOSアプリを開発する中で避けて通れないのが <code>Xcode</code> 周りの扱いです。<br/>
とは言え、個人でのアプリ開発であれば、そこまで工夫せずともビルド待ち時間に困ったりすることはないかもしれません。<br/>
ですが、業務で扱うような大規模なアプリ開発となると、やはり様々な工夫が必要になることでしょう。</p>

<p>その中の一つに『 <code>Xcode</code> の環境変数を有効に利用すること』が上げられます。<br/>
今回はその『 <code>Xcode</code> の環境変数』に関するメモ書きを残しておこうと思います。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>Xcodeプロジェクトの環境変数を調べる方法</h3>

<p><code>Xcode</code> の環境変数と一言で言っても、種類はかなりの数に上ります。<br/>
<a href="https://help.apple.com/xcode/mac/10.2/#/itcaec37c2a6">Xcode Help > Build settings reference</a>に一覧があるので、ざっと見てみるのも良いかと思います。<br/>
ただ、毎回、「あの環境変数はどんな名称だったっけ&hellip;？」という時にリファレンスを見るのも手間がかかります。<br/>
そこで、<code>Xcode</code> で用意されている、「環境変数を確認するためのコマンド」を積極的に利用しましょう。</p>

<p>使い方はシンプルで下記のようになります。</p>

<p><code>.xcodeproj</code> ファイルと同階層に移動して、以下コマンドを叩きます。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">$</span> <span class="n">xcodebuild</span> <span class="o">-</span><span class="n">showBuildSettings</span>
</span></code></pre></td></tr></table></div></figure>


<p>すると、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">Build</span> <span class="n">settings</span> <span class="k">for</span> <span class="n">action</span> <span class="n">build</span> <span class="n">and</span> <span class="n">target</span> <span class="o">&lt;</span><span class="err">プロジェクト名</span><span class="o">&gt;:</span>
</span><span class='line'>    <span class="n">ACTION</span> <span class="o">=</span> <span class="n">build</span>
</span><span class='line'>    <span class="n">AD_HOC_CODE_SIGNING_ALLOWED</span> <span class="o">=</span> <span class="n">NO</span>
</span><span class='line'>    <span class="n">ALTERNATE_GROUP</span> <span class="o">=</span> <span class="n">staff</span>
</span><span class='line'>    <span class="p">...</span>
</span><span class='line'>    <span class="n">YACC</span> <span class="o">=</span> <span class="n">yacc</span>
</span><span class='line'>    <span class="n">arch</span> <span class="o">=</span> <span class="n">arm64</span>
</span><span class='line'>    <span class="n">diagnostic_message_length</span> <span class="o">=</span> <span class="mi">199</span>
</span><span class='line'>    <span class="n">variant</span> <span class="o">=</span> <span class="n">normal</span>
</span></code></pre></td></tr></table></div></figure>


<p>のように結果が表示されます。<br/>
思ったよりも大量に出力されるため、初めから探したいものが決まっている場合は <code>grep</code> コマンドを利用して出力させて見るのが良いでしょう。</p>

<p>例えば、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">$</span> <span class="n">xcodebuild</span> <span class="o">-</span><span class="n">showBuildSettings</span> <span class="o">|</span> <span class="n">grep</span> <span class="s">&quot;PODS&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>のような感じで&hellip;。</p>

<h3>Xcodeの環境変数の利用実践例</h3>

<p>環境変数の調べ方がわかったところで、実践例を紹介しておきたいと思います。</p>

<h4>環境変数の実践利用①</h4>

<p>例えば <code>SwiftLint</code> を利用して静的解析をかける場合、常に <code>SwiftLint</code> を実行する必要はありません。<br/>
手元での開発時や、社内展開時のCI/CDツールにかける時に実行する場合が多くなるかと思います。</p>

<p>そんな時には、 <code>Build Phases</code> で下記のように設定することでしょう。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">if</span> <span class="p">[</span><span class="s">&quot;${CONFIGURATION}&quot;</span> <span class="o">=</span> <span class="s">&quot;Debug&quot;</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">which</span> <span class="n">swiftlint</span> <span class="o">&gt;/</span><span class="n">dev</span><span class="o">/</span><span class="n">null</span><span class="p">;</span> <span class="n">then</span>
</span><span class='line'><span class="n">swiftlint</span>
</span><span class='line'><span class="k">else</span>
</span><span class='line'><span class="n">echo</span> <span class="s">&quot;Configuration is not Debug or SwiftLint does not exist, download from https://github.com/realm/SwiftLint&quot;</span>
</span><span class='line'><span class="n">fi</span>
</span></code></pre></td></tr></table></div></figure>


<h4>環境変数の実践利用②</h4>

<p>例えば、 <code>Carthage</code> でOSSライブラリを管理している場合、 <code>Debug</code> 時と <code>Release</code> 時で利用する/利用しないライブラリが別れていることがあるかもしれません。 <br/>
その場合は、 <code>Build Phases</code> の <code>Input File Lists</code> と <code>Output File Lists</code> で指定するファイルのパスを工夫する必要があります。</p>

<p>まずは下記のように、 <code>Input File Lists</code> と <code>Output File Lists</code> 用のファイルを格納する構造をプロジェクト内に作ります。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">SampleApp</span>
</span><span class='line'><span class="err">├──</span> <span class="n">SampleApp</span>
</span><span class='line'><span class="err">│</span>    <span class="err">├──</span> <span class="n">inputXcfilelist</span>
</span><span class='line'><span class="err">│</span>    <span class="err">│</span>    <span class="err">├──</span> <span class="n">SampleApp</span><span class="o">-</span><span class="n">Debug</span><span class="p">.</span><span class="n">xcfilelist</span>
</span><span class='line'><span class="err">│</span>    <span class="err">│</span>    <span class="err">└──</span> <span class="n">SampleApp</span><span class="o">-</span><span class="n">Release</span><span class="p">.</span><span class="n">xcfilelist</span>
</span><span class='line'><span class="err">│</span>    <span class="err">├──</span> <span class="n">outputXcfilelist</span>
</span><span class='line'><span class="err">│</span>    <span class="err">│</span>    <span class="err">├──</span> <span class="n">SampleApp</span><span class="o">-</span><span class="n">Debug</span><span class="p">.</span><span class="n">xcfilelist</span>
</span><span class='line'><span class="err">│</span>    <span class="err">│</span>    <span class="err">└──</span> <span class="n">SampleApp</span><span class="o">-</span><span class="n">Release</span><span class="p">.</span><span class="n">xcfilelist</span>
</span><span class='line'><span class="err">│</span>    <span class="p">...</span>
</span><span class='line'><span class="err">├──</span> <span class="n">SampleAppTests</span>
</span><span class='line'><span class="err">├──</span> <span class="n">SampleAppUITests</span>
</span><span class='line'><span class="p">...</span>
</span></code></pre></td></tr></table></div></figure>


<p>続いて、 <code>Carthage</code> 利用時に指定する <code>Build Phases &gt; Run Script</code> 内の <code>Input File Lists</code> と <code>Output File Lists</code> にそれぞれパスを指定するだけです。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="c1">// Input File Lists Path</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">SRCROOT</span><span class="p">}</span><span class="o">/</span><span class="n">SampleApp</span><span class="o">/</span><span class="n">inputXcfilelist</span><span class="o">/</span><span class="n">SampleApp</span><span class="o">-</span><span class="n">$</span><span class="p">{</span><span class="n">CONFIGURATION</span><span class="p">}.</span><span class="n">xcfilelist</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// Output File Lists Path</span>
</span><span class='line'><span class="n">$</span><span class="p">{</span><span class="n">SRCROOT</span><span class="p">}</span><span class="o">/</span><span class="n">SampleApp</span><span class="o">/</span><span class="n">outputXcfilelist</span><span class="o">/</span><span class="n">SampleApp</span><span class="o">-</span><span class="n">$</span><span class="p">{</span><span class="n">CONFIGURATION</span><span class="p">}.</span><span class="n">xcfilelist</span>
</span></code></pre></td></tr></table></div></figure>


<p>因みに、 <code>Input File Lists</code> や <code>Output File Lists</code> を使わずとも、実践例①と同様にシェルスクリプト内で <code>${CONFIGURATION}</code> を利用する方法もあるのでは？と思われるかもしれません。<br/>
確かに、その方法でも問題なく成立する場合もありますが、</p>

<ul>
<li>変更時にファイルの <code>diff</code> として確認ができるので、レビューしやすい</li>
<li><code>Xcode10</code> からの新ビルドシステムが厳密なビルドを実行するため、エラーが出るパターンがある</li>
</ul>


<p>ことから <code>Input File Lists</code> や <code>Output File Lists</code> を利用することを筆者はおすすめしたいと思います。<br/>
(後者でうまくいかないパターンは、 <code>CONFIGURATION</code> を変えると、複数のターゲット間で <code>Input/Output</code> するモジュールが入れ替わる場合などです。 )</p>

<h3>まとめ</h3>

<p>さて、最近、 <code>Xcode</code> の環境変数周りを触る機会が久しぶりにあったためメモ書き程度に書き記しました。<br/>
忘れがちなので心に留めておきたいですね。<br/>
と言うことで本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[.jazzy.yamlを使ってコマンドを省略しよう！]]></title>
    <link href="http://grandbig.github.io/blog/2019/06/29/jazzy-2/"/>
    <updated>2019-06-29T21:50:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/06/29/jazzy-2</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>約3年前に<a href="https://github.com/realm/jazzy">jazzy</a>を利用していました。<br/>
(以前の記事：<a href="http://grandbig.github.io/blog/2016/01/12/jazzy/">jazzyを使って、Swiftで書いたプロジェクトのリファレンスを自動生成しよう！</a>)</p>

<p>今回、久々に <code>jazzy</code> を触ってみて <code>.jazzy.yaml</code> を利用する機会があったので、その時のメモを書き記しておきます。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>.jazzy.yaml</h3>

<p>では早速、 <code>.jazzy.yaml</code> の書き方を見ていきます。<br/>
作成する階層はアプリのプロジェクトの <code>xcodeproj</code> と同階層です。</p>

<p>実際の <code>.jazzy.yaml</code> は下記のようになります。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="cp"># .jazzy.yaml</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># ドキュメント化するスコープ</span>
</span><span class='line'><span class="nl">min_acl:</span> <span class="n">private</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># 出力先フォルダをクリーンするか否か</span>
</span><span class='line'><span class="nl">clean:</span> <span class="n">true</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># 出力先フォルダの指定</span>
</span><span class='line'><span class="nl">output:</span> <span class="n">docs</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># 作成者の名前</span>
</span><span class='line'><span class="nl">author:</span> <span class="n">Takahiro</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># 作成者のURL</span>
</span><span class='line'><span class="nl">author_url:</span> <span class="nl">https:</span><span class="c1">//grandbig.github.io</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># ドキュメント化する対象モジュールの名称</span>
</span><span class='line'><span class="nl">module:</span> <span class="n">JazzySample</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># ドキュメントコメントのないファイルはスキップするか否か</span>
</span><span class='line'><span class="nl">skip_undocumented:</span> <span class="n">true</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># ドキュメントにつけるバージョン番号</span>
</span><span class='line'><span class="nl">module_version:</span> <span class="mf">1.0</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># コピーライト</span>
</span><span class='line'><span class="nl">copyright:</span> <span class="err">©</span> <span class="mi">2019</span> <span class="o">-</span> <span class="n">Takahiro</span> <span class="o">-</span> <span class="n">Powered</span> <span class="n">by</span> <span class="n">Octopress</span>
</span><span class='line'>
</span><span class='line'><span class="cp"># ドキュメントのテーマ(apple/fullWidth/jonyの3つから選択可能)</span>
</span><span class='line'><span class="nl">theme:</span> <span class="n">apple</span>
</span></code></pre></td></tr></table></div></figure>


<p>このように定義しておけば実行するときのコマンドは</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="n">$</span> <span class="n">jazzy</span>
</span></code></pre></td></tr></table></div></figure>


<p>で済みます。</p>

<h3>まとめ</h3>

<p>今回は簡単に <code>.jazzy.yaml</code> の書き方についてまとめました。<br/>
本日はここまで。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[GitHubとSlackを連携させて、効率の良いPullRequestのレビューを実行しよう！]]></title>
    <link href="http://grandbig.github.io/blog/2019/05/26/github2slack/"/>
    <updated>2019-05-26T11:47:00+09:00</updated>
    <id>http://grandbig.github.io/blog/2019/05/26/github2slack</id>
    <content type="html"><![CDATA[<h3>はじめに</h3>

<p>今回は <code>GitHub</code> と <code>Slack</code> の連携を自作でやってみる話の備忘録です。</p>

<p>と言っても、手法は本当に簡易で、</p>

<ul>
<li><code>GitHub</code> 上で <code>Webhook</code> を設定する</li>
<li><code>GitHub</code> からの <code>POSTリクエスト</code> を受け付けるサーバを用意する</li>
<li>受け付けた <code>POSTリクエスト</code> から各種値を抽出して、Slackに通知する</li>
</ul>


<p>を対応することで実現することが可能です。<br/>
それでは早速見ていきましょう。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>




<!-- more -->


<h3>GitHub上でWebhookを設定する</h3>

<p><code>GitHub</code> 上で <code>Slack</code> と連携したい <code>Repository</code> を開いて、<code>Settings</code> ページにアクセスします。<br/>
<code>Settings</code> ページの左メニューから <code>Webhooks</code> を選択して、<code>Add webhook</code> を選択します。</p>

<p><img src="http://grandbig.github.io/images/github2slack_1.png" alt="Add webhookを選択" /></p>

<p>続いて、<code>Add webhook</code> ページを編集します。</p>

<p><img src="http://grandbig.github.io/images/github2slack_2.png" alt="Add webhookの編集" /></p>

<p>① <code>Payload URL</code> にPOSTリクエストの送り先を設定します<br/>
② <code>Content type</code> は <code>application/json</code> を設定します<br/>
③ <code>Which events would you like to trigger this webhook?</code> に設定したいものを設定します<br/>
　 ※筆者の場合は、個別に細かく設定可能な <code>Let me select individual events.</code> を選択しています。</p>

<h3>GitHubからのPOSTリクエストを受け付けるサーバを用意する</h3>

<p>ここは開発者の好きなインフラを用意すれば良いと思います。<br/>
AWSでもGCPでも良いかと思います。</p>

<p>また、APIサーバの作り込みに関しても、好きな言語で、自由にフレームワーク等を使って実装すれば良いと思います。<br/>
ポイントは、時間をかけずに作ることです。<br/>
(あくまでもチーム開発などを円滑に進めるための取り組みであって、この作り込みが目的ではないためです。)</p>

<p>因みに、筆者は、<code>Node.js</code>と<code>Express</code>を利用してAPIサーバを作ることにしました。</p>

<h3>受け付けたPOSTリクエストから各種値を抽出して、Slackに通知する</h3>

<p>では、少しAPIサーバの実装について紹介します。</p>

<h4>package.json</h4>

<p>まず、<code>package.json</code>は下記のようにしました。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="p">{</span>
</span><span class='line'>  <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;github2slack&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="s2">&quot;version&quot;</span><span class="o">:</span> <span class="s2">&quot;1.0.0&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="s2">&quot;description&quot;</span><span class="o">:</span> <span class="s2">&quot;GitHub Action Notification&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="s2">&quot;main&quot;</span><span class="o">:</span> <span class="s2">&quot;server.js&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="s2">&quot;scripts&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="s2">&quot;test&quot;</span><span class="o">:</span> <span class="s2">&quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;</span>
</span><span class='line'>  <span class="p">},</span>
</span><span class='line'>  <span class="s2">&quot;author&quot;</span><span class="o">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="s2">&quot;dependencies&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'>    <span class="s2">&quot;@slack/web-api&quot;</span><span class="o">:</span> <span class="s2">&quot;^5.0.1&quot;</span><span class="p">,</span>
</span><span class='line'>    <span class="s2">&quot;express&quot;</span><span class="o">:</span> <span class="s2">&quot;^4.17.0&quot;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>先程申し上げた通り <code>Express</code> を用いているのと、<code>Slack Web API</code>を簡単に利用できる <code>@slack/web-api</code>も導入しています。</p>

<h4>server.js</h4>

<p>続いてメインとなる<code>server.js</code>です。</p>

<p><code>POSTリクエスト</code>を受け付けるところまでですが、下記のように実装しています。<br/>
※ただ、<code>Express</code>を利用している、よくある書き方かなと思います。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">https</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;https&#39;</span><span class="p">);</span>
</span><span class='line'><span class="kd">var</span> <span class="nx">express</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;express&#39;</span><span class="p">);</span>
</span><span class='line'><span class="kd">var</span> <span class="nx">bodyParser</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;body-parser&#39;</span><span class="p">)</span>
</span><span class='line'><span class="kr">const</span> <span class="p">{</span> <span class="nx">WebClient</span><span class="p">,</span> <span class="nx">LogLevel</span><span class="p">,</span> <span class="nx">retryPolicies</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;@slack/web-api&#39;</span><span class="p">);</span>
</span><span class='line'><span class="kr">const</span> <span class="nx">slackChannel</span> <span class="o">=</span> <span class="s1">&#39;XXXXXXXX&#39;</span><span class="p">;</span>  <span class="c1">// 通知先のSlackチャンネルのID</span>
</span><span class='line'><span class="kr">const</span> <span class="nx">serverPort</span> <span class="o">=</span> <span class="mi">3000</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="kr">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>
</span><span class='line'><span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">bodyParser</span><span class="p">.</span><span class="nx">urlencoded</span><span class="p">({</span> <span class="nx">extended</span><span class="o">:</span> <span class="kc">false</span> <span class="p">}));</span>
</span><span class='line'><span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">bodyParser</span><span class="p">.</span><span class="nx">json</span><span class="p">());</span>
</span><span class='line'>
</span><span class='line'><span class="c1">/// POSTリクエストの受付</span>
</span><span class='line'><span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class='line'>  <span class="p">...</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'><span class="c1">/// サーバを起動</span>
</span><span class='line'><span class="nx">app</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="nx">serverPort</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Listening on port &#39;</span> <span class="o">+</span> <span class="nx">serverPort</span><span class="p">));</span>
</span></code></pre></td></tr></table></div></figure>


<p>肝心の<code>POSTリクエスト</code>で受け付けた後の処理ですが、</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// GitHubからのリクエストを分析</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">message</span> <span class="o">=</span> <span class="nx">makeMessageFromGitHubRequest</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span> <span class="o">---&gt;</span> <span class="err">後ほど説明します</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// 送るべきメッセージがないため、200で返して終了する</span>
</span><span class='line'>    <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">200</span><span class="p">).</span><span class="nx">send</span><span class="p">(</span><span class="s1">&#39;no message&#39;</span><span class="p">);</span>
</span><span class='line'>    <span class="k">return</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// Slackにメッセージを投稿</span>
</span><span class='line'>  <span class="kr">const</span> <span class="nx">slack</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebClient</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="p">{</span>
</span><span class='line'>    <span class="nx">slackApiUrl</span><span class="o">:</span> <span class="s1">&#39;http://xxxxxxxxxxxxxxxxxxxxxx&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">retryConfig</span><span class="o">:</span> <span class="nx">retryPolicies</span><span class="p">.</span><span class="nx">tenRetriesInAboutThirtyMinutes</span><span class="p">,</span>
</span><span class='line'>    <span class="nx">logLevel</span><span class="o">:</span> <span class="nx">LogLevel</span><span class="p">.</span><span class="nx">DEBUG</span>
</span><span class='line'>  <span class="p">})</span>
</span><span class='line'>
</span><span class='line'>  <span class="kr">const</span> <span class="nx">main</span> <span class="o">=</span> <span class="nx">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">try</span> <span class="p">{</span>
</span><span class='line'>      <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">slack</span><span class="p">.</span><span class="nx">chat</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span>
</span><span class='line'>        <span class="nx">channel</span><span class="o">:</span> <span class="nx">slackChannel</span><span class="p">,</span>
</span><span class='line'>        <span class="nx">text</span><span class="o">:</span> <span class="nx">message</span>
</span><span class='line'>      <span class="p">})</span>
</span><span class='line'>
</span><span class='line'>      <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">200</span><span class="p">).</span><span class="nx">send</span><span class="p">(</span><span class="s1">&#39;message: &#39;</span> <span class="o">+</span> <span class="nx">message</span> <span class="o">+</span> <span class="s1">&#39;\n&#39;</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">500</span><span class="p">).</span><span class="nx">send</span><span class="p">(</span><span class="nx">err</span><span class="p">.</span><span class="nx">message</span> <span class="o">+</span> <span class="s1">&#39;\n&#39;</span><span class="p">);</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nx">main</span><span class="p">()</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>


<p>のようになっています。<br/>
先程導入した <code>@slack/web-api</code> を利用すれば、いとも簡単に<code>Slack</code>への通知が可能となっています。</p>

<p>そして<code>Slack</code>にどんな通知を送るかについてですが、<code>GitHub</code>上で発生したイベント毎にメッセージが異なると考えられます。<br/>
よって、<code>GitHub</code>から<code>POSTリクエスト</code>された値を分析して、最適なメッセージを送る処理を導入してみましょう。</p>

<p>今回、筆者は下記のイベントをハンドリングすることにしました。</p>

<ul>
<li><code>PullRequest</code>に<code>Reviewer</code>をセットした時に、レビュー依頼を流す</li>
<li><code>PullRequest</code>のレビューをして、ステータスを変更した時に、レビュー依頼者に知らせる</li>
<li><code>PullRequest</code>の<code>diff</code>に沿ってコメントを書いたことを流す</li>
<li><code>PullRequest</code>の<code>Conversation</code>にコメントを書いたことを流す</li>
</ul>


<p>それらをハンドリングするメソッドをまずは用意します。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * GitHubからのリクエストの中身を精査して、メッセージを作成する</span>
</span><span class='line'><span class="cm"> * @param {object}  req   POSTリクエスト</span>
</span><span class='line'><span class="cm"> * @return {string}       メッセージ</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">function</span> <span class="nx">makeMessageFromGitHubRequest</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="kr">const</span> <span class="nx">gitHubEvent</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">headers</span><span class="p">[</span><span class="s1">&#39;x-github-event&#39;</span><span class="p">];</span>
</span><span class='line'>  <span class="kr">const</span> <span class="nx">gitHubBody</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="nx">gitHubEvent</span> <span class="o">===</span> <span class="s1">&#39;pull_request&#39;</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">makePullRequestMessage</span><span class="p">(</span><span class="nx">gitHubBody</span><span class="p">.</span><span class="nx">action</span><span class="p">,</span> <span class="nx">gitHubBody</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">gitHubEvent</span> <span class="o">===</span> <span class="s1">&#39;pull_request_review&#39;</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">makeChangeReviewStateMessage</span><span class="p">(</span><span class="nx">gitHubBody</span><span class="p">.</span><span class="nx">action</span><span class="p">,</span> <span class="nx">gitHubBody</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">gitHubEvent</span> <span class="o">===</span> <span class="s1">&#39;pull_request_review_comment&#39;</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">makePullRequestReviewCommentMessage</span><span class="p">(</span><span class="nx">gitHubBody</span><span class="p">.</span><span class="nx">action</span><span class="p">,</span> <span class="nx">gitHubBody</span><span class="p">.</span><span class="nx">comment</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">gitHubEvent</span> <span class="o">===</span> <span class="s1">&#39;issue_comment&#39;</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">makeIssueCommentMessage</span><span class="p">(</span><span class="nx">gitHubBody</span><span class="p">.</span><span class="nx">action</span><span class="p">,</span> <span class="nx">gitHubBody</span><span class="p">.</span><span class="nx">comment</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>それぞれイベントによって作成するメッセージが異なるため、1つずつメソッドを切り出しました。<br/>
『<code>PullRequest</code>に<code>Reviewer</code>をセットした時に、レビュー依頼を流す』場合の処理は下記です。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * PullRequest関連のメッセージを作成する</span>
</span><span class='line'><span class="cm"> * @param {string}  action      アクション種別</span>
</span><span class='line'><span class="cm"> * @param {object}  body        GitHubのBody情報</span>
</span><span class='line'><span class="cm"> * @return {string}             メッセージ</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">function</span> <span class="nx">makePullRequestMessage</span><span class="p">(</span><span class="nx">action</span><span class="p">,</span> <span class="nx">body</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">mention</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">from</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">content</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">title</span> <span class="o">=</span> <span class="nx">body</span><span class="p">.</span><span class="nx">pull_request</span><span class="p">.</span><span class="nx">title</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">message</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">linkUrl</span> <span class="o">=</span> <span class="nx">body</span><span class="p">.</span><span class="nx">pull_request</span><span class="p">.</span><span class="nx">html_url</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">switch</span><span class="p">(</span><span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">case</span> <span class="s1">&#39;created&#39;</span><span class="o">:</span>
</span><span class='line'>      <span class="nx">mention</span> <span class="o">=</span> <span class="nx">makeMentionTextAtPullRequestCreated</span><span class="p">(</span><span class="nx">body</span><span class="p">.</span><span class="nx">pull_request</span><span class="p">);</span>
</span><span class='line'>      <span class="nx">from</span> <span class="o">=</span> <span class="s1">&#39;「&#39;</span> <span class="o">+</span> <span class="nx">body</span><span class="p">.</span><span class="nx">pull_request</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">login</span> <span class="o">+</span> <span class="s1">&#39;」さんから&#39;</span><span class="p">;</span>
</span><span class='line'>      <span class="nx">content</span> <span class="o">=</span> <span class="s1">&#39;PullRequestのレビュー依頼が届きました\n\n&#39;</span><span class="p">;</span>
</span><span class='line'>      <span class="nx">message</span> <span class="o">=</span> <span class="nx">mention</span> <span class="o">+</span> <span class="nx">from</span> <span class="o">+</span> <span class="nx">content</span> <span class="o">+</span> <span class="nx">title</span><span class="p">;</span>
</span><span class='line'>      <span class="k">break</span><span class="p">;</span>
</span><span class='line'>    <span class="k">case</span> <span class="s1">&#39;review_requested&#39;</span><span class="o">:</span>
</span><span class='line'>      <span class="nx">mention</span> <span class="o">=</span> <span class="nx">makeMentionText</span><span class="p">(</span><span class="nx">body</span><span class="p">.</span><span class="nx">requested_reviewer</span><span class="p">);</span>
</span><span class='line'>      <span class="nx">from</span> <span class="o">=</span> <span class="s1">&#39;「&#39;</span> <span class="o">+</span> <span class="nx">body</span><span class="p">.</span><span class="nx">pull_request</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">login</span> <span class="o">+</span> <span class="s1">&#39;」さんから&#39;</span><span class="p">;</span>
</span><span class='line'>      <span class="nx">content</span> <span class="o">=</span> <span class="s1">&#39;PullRequestのレビュー依頼が届きました\n\n&#39;</span><span class="p">;</span>
</span><span class='line'>      <span class="nx">message</span> <span class="o">=</span> <span class="nx">mention</span> <span class="o">+</span> <span class="nx">from</span> <span class="o">+</span> <span class="nx">content</span> <span class="o">+</span> <span class="nx">title</span><span class="p">;</span>
</span><span class='line'>      <span class="k">break</span><span class="p">;</span>
</span><span class='line'>    <span class="k">default</span><span class="o">:</span>
</span><span class='line'>      <span class="k">break</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">message</span> <span class="o">+</span> <span class="s1">&#39;\n&#39;</span> <span class="o">+</span> <span class="nx">linkUrl</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * PullRequest作成時にレビューを要求された場合にメンションテキストを作成する処理</span>
</span><span class='line'><span class="cm"> * @param {object} pullRequest  PullRequest情報</span>
</span><span class='line'><span class="cm"> * @return {string}             メンションテキスト</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">function</span> <span class="nx">makeMentionTextAtPullRequestCreated</span><span class="p">(</span><span class="nx">pullRequest</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span><span class='line'>  <span class="kr">const</span> <span class="nx">requested_reviewers</span> <span class="o">=</span> <span class="nx">pullRequest</span><span class="p">.</span><span class="nx">requested_reviewers</span><span class="p">;</span>
</span><span class='line'>  <span class="nx">requested_reviewers</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">reviewer</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1">// Slack APIでは、&lt;@username&gt;の形でメッセージを作らないとメンションできない</span>
</span><span class='line'>    <span class="kr">const</span> <span class="nx">mention</span> <span class="o">=</span> <span class="s2">&quot;&lt;@&quot;</span> <span class="o">+</span> <span class="nx">reviewer</span><span class="p">.</span><span class="nx">login</span> <span class="o">+</span> <span class="s2">&quot;&gt; &quot;</span><span class="p">;</span>
</span><span class='line'>    <span class="nx">result</span> <span class="o">=</span> <span class="nx">result</span> <span class="o">+</span> <span class="nx">mention</span><span class="p">;</span>
</span><span class='line'>  <span class="p">});</span>
</span><span class='line'>  <span class="nx">result</span> <span class="o">=</span> <span class="nx">result</span> <span class="o">+</span> <span class="s1">&#39;\n&#39;</span><span class="p">;</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * PullRequestレビュワー追加時にメンションテキストを作成する処理</span>
</span><span class='line'><span class="cm"> * @param {object} user  ユーザ情報</span>
</span><span class='line'><span class="cm"> * @return {string}      メンションテキスト</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">function</span> <span class="nx">makeMentionText</span><span class="p">(</span><span class="nx">user</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="kr">const</span> <span class="nx">mention</span> <span class="o">=</span> <span class="s2">&quot;&lt;@&quot;</span> <span class="o">+</span> <span class="nx">user</span><span class="p">.</span><span class="nx">login</span> <span class="o">+</span> <span class="s2">&quot;&gt; &quot;</span><span class="p">;</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">mention</span> <span class="o">+</span> <span class="s1">&#39;\n&#39;</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>『<code>PullRequest</code>のレビューをして、ステータスを変更した時に、レビュー依頼者に知らせる』場合は下記です。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * PullRequestのReview関連のメッセージを作成する</span>
</span><span class='line'><span class="cm"> * @param {string}  action  アクション種別</span>
</span><span class='line'><span class="cm"> * @param {object}  body    GitHubのBody情報</span>
</span><span class='line'><span class="cm"> * @return {string}         メッセージ</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">function</span> <span class="nx">makeChangeReviewStateMessage</span><span class="p">(</span><span class="nx">action</span><span class="p">,</span> <span class="nx">body</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">mention</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">from</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">content</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">title</span> <span class="o">=</span> <span class="nx">convertCommentWithMention</span><span class="p">(</span><span class="nx">body</span><span class="p">.</span><span class="nx">review</span><span class="p">.</span><span class="nx">body</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">message</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">linkUrl</span> <span class="o">=</span> <span class="nx">body</span><span class="p">.</span><span class="nx">review</span><span class="p">.</span><span class="nx">html_url</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">switch</span><span class="p">(</span><span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">case</span> <span class="s1">&#39;submitted&#39;</span><span class="o">:</span>
</span><span class='line'>      <span class="nx">mention</span> <span class="o">=</span> <span class="nx">makeMentionText</span><span class="p">(</span><span class="nx">body</span><span class="p">.</span><span class="nx">pull_request</span><span class="p">.</span><span class="nx">user</span><span class="p">);</span>
</span><span class='line'>      <span class="nx">from</span> <span class="o">=</span> <span class="s1">&#39;「&#39;</span> <span class="o">+</span> <span class="nx">body</span><span class="p">.</span><span class="nx">review</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">login</span> <span class="o">+</span> <span class="s1">&#39;」さんから&#39;</span>
</span><span class='line'>      <span class="nx">content</span> <span class="o">=</span> <span class="s1">&#39;レビュー結果： *&#39;</span> <span class="o">+</span> <span class="nx">body</span><span class="p">.</span><span class="nx">review</span><span class="p">.</span><span class="nx">state</span> <span class="o">+</span> <span class="s1">&#39;* が返ってきました。\n\n&#39;</span><span class="p">;</span>
</span><span class='line'>      <span class="nx">message</span> <span class="o">=</span> <span class="nx">mention</span> <span class="o">+</span> <span class="nx">from</span> <span class="o">+</span> <span class="nx">content</span> <span class="o">+</span> <span class="nx">title</span><span class="p">;</span>
</span><span class='line'>      <span class="k">break</span><span class="p">;</span>
</span><span class='line'>    <span class="k">default</span><span class="o">:</span>
</span><span class='line'>      <span class="k">break</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">message</span> <span class="o">+</span> <span class="s1">&#39;\n&#39;</span> <span class="o">+</span> <span class="nx">linkUrl</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * コメント内にメンションがあった場合に、Slackが検知できるメンションテキストに置き換える処理</span>
</span><span class='line'><span class="cm"> * @param {object} comment  コメント</span>
</span><span class='line'><span class="cm"> * @return {string}         メンションテキスト</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">function</span> <span class="nx">convertCommentWithMention</span><span class="p">(</span><span class="nx">comment</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">comment</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/@[A-Za-z0-9-/]+/g</span><span class="p">,</span> <span class="s2">&quot;&lt;$&amp;&gt;&quot;</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>『* <code>PullRequest</code>の<code>diff</code>に沿ってコメントを書いたことを流す』場合は下記です。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * PullRequestのReviewコメント関連のメッセージを作成する</span>
</span><span class='line'><span class="cm"> * @param {string}  action  アクション種別</span>
</span><span class='line'><span class="cm"> * @param {object}  comment コメント情報</span>
</span><span class='line'><span class="cm"> * @return {string}         メッセージ</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">function</span> <span class="nx">makePullRequestReviewCommentMessage</span><span class="p">(</span><span class="nx">action</span><span class="p">,</span> <span class="nx">comment</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">from</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">content</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">title</span> <span class="o">=</span> <span class="nx">convertCommentWithMention</span><span class="p">(</span><span class="nx">comment</span><span class="p">.</span><span class="nx">body</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">message</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">linkUrl</span> <span class="o">=</span> <span class="nx">comment</span><span class="p">.</span><span class="nx">html_url</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">switch</span><span class="p">(</span><span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">case</span> <span class="s1">&#39;created&#39;</span><span class="o">:</span>
</span><span class='line'>      <span class="c1">// PullRequestのFileChanges内でコメントした場合</span>
</span><span class='line'>      <span class="nx">from</span> <span class="o">=</span> <span class="s1">&#39;「&#39;</span> <span class="o">+</span> <span class="nx">comment</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">login</span> <span class="o">+</span> <span class="s1">&#39;」さんから&#39;</span>
</span><span class='line'>      <span class="nx">content</span> <span class="o">=</span> <span class="s1">&#39;コメントが届きました。\n\n&#39;</span><span class="p">;</span>
</span><span class='line'>      <span class="nx">message</span> <span class="o">=</span> <span class="nx">from</span> <span class="o">+</span> <span class="nx">content</span> <span class="o">+</span> <span class="nx">title</span><span class="p">;</span>
</span><span class='line'>      <span class="k">break</span><span class="p">;</span>
</span><span class='line'>    <span class="k">default</span><span class="o">:</span>
</span><span class='line'>      <span class="k">break</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">message</span> <span class="o">+</span> <span class="s1">&#39;\n&#39;</span> <span class="o">+</span> <span class="nx">linkUrl</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>『* <code>PullRequest</code>の<code>Conversation</code>にコメントを書いたことを流す』場合は下記です。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="cm">/**</span>
</span><span class='line'><span class="cm"> * PullRequestやIssueのConversation欄のコメント関連のメッセージを作成する</span>
</span><span class='line'><span class="cm"> * @param {string}  action  アクション種別</span>
</span><span class='line'><span class="cm"> * @param {object}  comment コメント情報</span>
</span><span class='line'><span class="cm"> * @return {string}         メッセージ</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="kd">function</span> <span class="nx">makeIssueCommentMessage</span><span class="p">(</span><span class="nx">action</span><span class="p">,</span> <span class="nx">comment</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">from</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">content</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">title</span> <span class="o">=</span> <span class="nx">convertCommentWithMention</span><span class="p">(</span><span class="nx">comment</span><span class="p">.</span><span class="nx">body</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">message</span><span class="p">;</span>
</span><span class='line'>  <span class="kd">let</span> <span class="nx">linkUrl</span> <span class="o">=</span> <span class="nx">comment</span><span class="p">.</span><span class="nx">html_url</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">switch</span> <span class="p">(</span><span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">case</span> <span class="s1">&#39;created&#39;</span><span class="o">:</span>
</span><span class='line'>      <span class="nx">from</span> <span class="o">=</span> <span class="s1">&#39;「&#39;</span> <span class="o">+</span> <span class="nx">comment</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">login</span> <span class="o">+</span> <span class="s1">&#39;」さんから&#39;</span><span class="p">;</span>
</span><span class='line'>      <span class="nx">content</span> <span class="o">=</span> <span class="s1">&#39;コメントが届きました。\n\n&#39;</span><span class="p">;</span>
</span><span class='line'>      <span class="nx">message</span> <span class="o">=</span> <span class="nx">from</span> <span class="o">+</span> <span class="nx">content</span> <span class="o">+</span> <span class="nx">title</span><span class="p">;</span>
</span><span class='line'>      <span class="k">break</span><span class="p">;</span>
</span><span class='line'>    <span class="k">default</span><span class="o">:</span>
</span><span class='line'>      <span class="k">break</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">message</span> <span class="o">+</span> <span class="s1">&#39;\n&#39;</span> <span class="o">+</span> <span class="nx">linkUrl</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>まとめ</h3>

<p>さて、割と手軽に実装できることがわかったところで本日はここまで。<br/>
今後、もっと様々なイベントをキャッチして<code>Slack</code>に流す必要性が出てきたら、どんどん足していこうかな。</p>

<script async src="http://grandbig.github.io//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>


<p><ins class="adsbygoogle"style="display:inline-block;width:320px;height:100px"data-ad-client="ca-pub-2881241309408290"data-ad-slot="6603059167"></ins></p>

<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

]]></content>
  </entry>
  
</feed>
