ushumpei’s blog

生活で気になったことを随時調べて書いていきます。

React NativeでARを体感する、Introducing Expo AR

 9月末にExpoがアップデートされてiOSAR Kit対応が行われました。チュートリアルが公開されたのでちょっと手を出してみました。使用する要素としては、

  • expo
  • three
  • expo-three

のみです。3Dオブジェクトの生成はThree.jsレンダリングExpoExpo Threeという構成になっています。

Introducing Expo AR: Three.js on ARKit

デモ


Introducing Expo AR #1

感想

 Expoの制限はあるもののARアプリをJavaScriptでかけるというのは前提となる知識量のハードルが下がるかと思います。

 チュートリアルやると満足感が得られてしまうのでやや危険ですね。色々いじってみるのは大切な一歩だと思いますが、一歩踏み出した後に自分の発想が広がっていかないのがなかなか辛いところです。

サービスがSSL化するとき

 ものすごく短い話ですが、サービスがSSL化するときの対応を想定してみました。近々知り合いのサイトにSSL入れられることになりそうなのが理由です。そういえばはてなブログSSL化するそうですね!

概要

 httphttpsに書き換えるの面倒ですよね。省略できた気がします。(確かにできるみたいです参考: リンクのhttp:https:を省略して現在のプロトコルでリンク先にアクセスさせる)。ただ参考リンクのタイトル通り、通信は現在のスキームになるのでほおっておくと 本来SSLが必要なページでもhttpで接続されてしまいます。というのはかなり問題だと思うので、移行するときは以下の切り分けをするかもしれないです。

  • リソースとかユーザーの情報を扱わないリンクに関しては、スキーム省略
  • 明示的にssl化したページはしっかりhttps書いておく
    • またはsslページにリダイレクトさせる

切り分け方法: 追記(2017年10月3日)

  • 内部
    • リソースとかユーザーの情報を扱わないリンクに関しては、スキーム省略
    • 明示的にssl化したページはしっかりhttps書いておく
  • 外部
    • リンク先に合わせたスキームを書く

感想

 でも切り分けるくらいならhttpsに置換しちゃったほうが早くない?というと、それもそうだと思います。前に、「画面動かない!直して!->ssl切れちゃってました」、というhttps -> http現象があったので対応減らせたらいいなと思った次第です。

 混在コンテンツ(Mixed Content)に対しても効果があるそうです。

配列でsplitもどき(JavaScriptメモ)

 JavaScriptに関するメモです。配列(Array)を適当な部分で分割したかったのでsplitもどきの関数を書きました。文字列のsplitをちゃんと配列版にしたわけではないので「もどき」と言っています。

function split(array, separator) {
  return array.reduce(function(p, n, i) {
    if (n !== separator) {
      p[p.length - 1].push(n);
    } else {
      if (i !== 0 && i !== array.length - 1 && array[i - 1] !== separator) p.push([]);
    }
    return p;
  }, [[]])
}
// 's'で分割する
split(['a', 'b', 's', 'c', 'd', 's', 'e'], 's')
=>
[
  ["a", "b"],
  ["c", "d"],
  ["e"]
]

// 配列の最初と最後に区切り文字があるときは、空配列を作らずに無視する
// 連続している区切り文字も1つとみなし、空配列は作らない
split([1,2,3,3,3,3,3,4,5,4,3,2,2,1,1,2,3,3,4,1], 1);
=>
[
  [2, 3, 3, 3, 3, 3, 4, 5, 4, 3, 2, 2],
  [2, 3, 3, 4]
]

// 区切り文字が配列に含まれていないときは、配列そのものを要素に持った配列を返す
split([1,2,3,3,3,3,3,4,5,4,3,2,2,1,1,2,3,3,4,1], 0)
=>
[[1, 2, 3, 3, 3, 3, 3, 4, 5, 4, 3, 2, 2, 1, 1, 2, 3, 3, 4, 1]]

感想

 必要に駆られて書いてみたので、文字列のsplitの配列版にはなっていないです。スプレッドシートExcel方眼紙状態になっていて、そこからデータを取得しなきゃいけない、というときに使用しました。

 配列版splitを作る場合、配列を区切り文字にして配列を分割できるようにするのが自然だと思います。文字列のsplitをうまく使って配列版をかけないか?と思ったのですが、空文字とかの扱いが微妙です。

 なんか圏論とか使うと整理できそうな気がしました。文字列対象から配列対象への射を、配列関手で写した先みたいな。全然よくわかっていないです。圏論ということはHaskellでの実装を見ると面白いかもしれないです。(多分split的なものがある?)

ページに目次をつけて、項目を押したらスクロールする

 ものすごい小さい話ですが、毎回忘れてしまうのでメモします。

$('html,body').animate({
  scrollTop: window.pageYOffset + target.getBoundingClientRect().top - 8 // 要素がブラウザ上部ぴったりになるので少し隙間をあけたり
}, 500, 'swing');

 まずwindow.pageYOffsetについて、これはwindow.scrollYエイリアスだそうです。ページの一番上を0として、どのくらい下にスクロールしているかのピクセル値を返します。

 次にelement.getBoundingClientRect().topですが、現在クライアントが見ている画面の上端を0として、そこからどれくらい要素が離れているかをピクセル値で返します。例えば要素をスクロールして通り過ぎていたら、負の値が返ってきます。

  つまり、ページの一番上から現在表示している画面上端までの距離現在表示している画面上端から要素までの距離を足すことでページの一番上から要素までの距離を計算しているということになります。(多分絵で描くとわかりやすい???)

ページの一番上から現在表示している画面上端までの距離

f:id:ushumpei:20170925210302p:plain

現在表示している画面上端から要素までの距離

f:id:ushumpei:20170925210314p:plain

まとめ

 多分覚えたのでもう忘れないはずです。ぶっちゃけていうとすでにここに書いてありました。。。

おまけ

 デモです。

See the Pen MEbNmj by ushumpei(@ushumpei) on CodePen.

一枚にまとめたやつ

<html>
  <head>
    <style type="text/css">
      .container {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
      }
      .menu {
        background-color: #eee;
        display: flex;
        flex-direction: column;
        flex: 1;
        height: 120px;
        justify-content: space-around;
        padding: 8px;
        position: sticky;
        top: 8px;
      }
      .content {
        display: flex;
        flex-direction: column;
        flex: 3;
        justify-content: space-between;
        margin-left: 8px;
      }
      .section {
        height: 100vh;
      }
      .section > h1 {
        background-color: #eee;
        padding: 8px;
        margin-top: 0;
      }
    </style>
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
    <script>
      window.addEventListener('load', function() {
        var menuItems = Array.from(document.getElementsByClassName('menu-item'));
        var sections =  Array.from(document.getElementsByClassName('section'));
        menuItems.forEach(function(item, i) {
          var target = sections[i];
          item.addEventListener('click', function() {
            $('html,body').animate({
              scrollTop: window.pageYOffset + target.getBoundingClientRect().top - 8 // 要素がブラウザ上部ぴったりになるので少し隙間をあけました
            }, 500, 'swing');
          });
        });
      });
    </script>
  </head>
  <body>
    <div class="container">
      <div class="menu">
        <a href="#" class="menu-item">section1</a>
        <a href="#" class="menu-item">section2</a>
        <a href="#" class="menu-item">section3</a>
        <a href="#" class="menu-item">section4</a>
        <a href="#" class="menu-item">section5</a>
      </div>
      <div class="content">
        <div class="section"><h1>section1</h1></div>
        <div class="section"><h1>section2</h1></div>
        <div class="section"><h1>section3</h1></div>
        <div class="section"><h1>section4</h1></div>
        <div class="section"><h1>section5</h1></div>
      </div>
    </div>
  </body>
</html>

google app scriptで背景色の置換

google app scriptで背景色の置換スクリプトを書いたのでメモします。以下の内容が含まれています。

  • 独自(「拡張ツール」)メニューの追加
  • モーダル(背景色置換モーダル)の表示
  • 初期値の挿入(テンプレートhtmlの使用)方法

概要

 主にgoogleのガイドを参考にして作成しました。とりあえずできることと、コードを記載します。

 できること;

  • スプレッドシート内での背景色の置換、置換実行モーダルの表示
  • 選択したセルの背景色を置換元としてデフォルト値として挿入

 そんなこといいから、という方は、スプレッドシートを開いて、「ツール」>「スクリプトエディタ」を選択し、スクリプトエディタのgsファイルにjavascriptコード、新規作成でhtmlファイルを作成しテンプレートファイル(index.html)を貼り付けてください。貼り付けたあとリロードするとメニューに「拡張ツール」が現れるかと思います。現れない場合はコメント頂けると幸いです。。。

f:id:ushumpei:20170925003319p:plain

 コード;

main.gs

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('拡張ツール')
    .addItem('背景色置換', 'showModal')
    .addToUi();
}

function showModal() {
  var background = SpreadsheetApp.getActiveSheet().getActiveCell().getBackground();

  var template = HtmlService
    .createTemplateFromFile('index.html');
  template.from = background;
  
  var html = template
    .evaluate()
    .setWidth(300)
    .setHeight(150)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);

  SpreadsheetApp.getUi().showModalDialog(html, 'Replace background color');
}

function replaceBackgroundColor(from, to) {
  var sheet = SpreadsheetApp.getActiveSheet();
  var data = sheet.getDataRange();
  var backgrounds = data.getBackgrounds();
 
  var replacedCount = 0;
  for(var row = 0; row < backgrounds.length; row++) {
    for(var col = 0; col < backgrounds[row].length; col++) {
      if (backgrounds[row][col] != from) continue;
      sheet.getRange(row + 1, col + 1).setBackground(to);
      replacedCount++;
    }
  }

  ui = SpreadsheetApp.getUi();
  ui.alert('Successfully replaced!', replacedCount + ' cell\'s background color replaced to ' + to + ' from ' + from, ui.ButtonSet.OK);
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
    <script>
      function handleClick() {
        document.getElementById('replace').setAttribute("disabled","disabled");
        var form = document.getElementById('form');
        var from = form.querySelector('input[name="from"]').value;
        var to = form.querySelector('input[name="to"]').value;
        if (!from || !to) return showError('Input #from and #to');
        google.script.run
          .withSuccessHandler(closeModal)
          .withFailureHandler(showError)
          .replaceBackgroundColor(from, to);
      }
      
      function closeModal() {
        google.script.host.close();
      }
      
      function showError(message) {
        document.getElementById('error').innerHTML = 'Error: ' + message;
        document.getElementById('replace').removeAttribute("disabled");
      }
    </script>
    <style type="text/css">
      html, body, #form { height: 100%; }
    </style>
  </head>
  <body>
    <div id="form" style="display: flex; justify-content: space-between; flex-direction: column;">
      <div style="display: inline-flex; justify-content: flex-end">
        <label for="from" style="flex: 1">From: </label>
        <input id="from" type="text" name="from" placeholder="from" value="<?= from ?>" style="flex: 3">
      </div>
      <div style="display: inline-flex; justify-content: flex-end">
        <label for="to" style="flex: 1">To: </label>
        <input id="to" type="text" name="to" placeholder="to" style="flex: 3">
      </div>
      <button id="replace" onclick="handleClick()">Replace</button>
      <p id="error" style="color: red"></p>
    </div>
  </body>
</html>

解説

下準備

 onOpenスプレッドシートファイルを開いたときに実行する関数を指定できます。ここではスプレドシートのUIのAPIを呼び出して、メニューの追加と、メニュー項目と選択時の実行関数の登録を行なっています。

 メニュー項目が選択された際に実行される関数は、選択中のセルの背景色取得、テンプレートへの値挿入、モーダルの表示などです。対応するコードを以下に記載します;

 まず現在選択中のセル(光っているセル)の背景色を取得するためにgetActiveCellを使ってセルを取得します。そのあとにgetBackgroud#?????? 形式のカラーコードを取得し格納します。

  var background = SpreadsheetApp.getActiveSheet().getActiveCell().getBackground();

次に、作成済みのhtmlテンプレートを読み込み、プレースホルダfromに対し背景色を割り当てます。プレースホルダーの指定は、

<input id="from" type="text" name="from" placeholder="from" value="<?= from ?>" style="flex: 3">

のようにvalue=<?= form ?>となっていて、この仕組みを使って置換前の背景色のデフォルト値、として現在選択中のセルの背景色を割り当てます。参考

  var template = HtmlService
    .createTemplateFromFile('index.html');
  template.from = background;

最後にモーダルの表示処理を行います。

  var html = template
    .evaluate()
    .setWidth(300)
    .setHeight(150)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);

  SpreadsheetApp.getUi().showModalDialog(html, 'Replace background color');

 evaluateメソッドを呼び出すことで、テンプレートからhtmlオブジェクトを生成します、モーダルとして表示するためにサイズを設定し、実行方式をiframeに指定しました。実行方法に関しては他に選択肢があるようで、調べきれていないです。なので特に深い意味はありません、チュートリアルに沿って、iframeを指定しただけです。

モーダル

 モーダルの記載方法に関しては1ページのhtmlを書いて行く感じです。heightwidthが決まっている以外はwebと同じ感覚だと言えます。ただし、少し違うのは、スクリプトエディタで定義した関数を呼び出す方法があるということです。方法としては提供されているAPIを使用します;

      function handleClick() {
        document.getElementById('replace').setAttribute("disabled","disabled");
        var form = document.getElementById('form');
        var from = form.querySelector('input[name="from"]').value;
        var to = form.querySelector('input[name="to"]').value;
        if (!from || !to) return showError('Input #from and #to');
        google.script.run
          .withSuccessHandler(closeModal)
          .withFailureHandler(showError)
          .replaceBackgroundColor(from, to);
      }

 handleChlick関数はユーザーが置換実行する際に押下するボタンに紐づいている関数です。概要としては、表示されたモーダルに対象の背景色、変換後の背景色を入力し、「置換」を押下したら、シート内の全てのセルに対して、背景色の置換を行うためのものです。処理の流れとしては二回連続のクリック禁止、入力した値の取得、軽いバリデーション、google.script.runAPIを使用した背景色置換スクリプトの実行です。

 詳細が必要なのはgoogle.script.runかと思います。このAPIgoogle.script.run.hogehoge()と記載することで、スクリプトエディタで定義したhogehoge関数を呼び出すことができます。またここでは、実行の成功時(withSuccessHandler)、失敗時(withFailureHandler)にコールバックを指定しています。コールバック関数は index.htmlで定義したものが指定可能です。

 以下はモーダルに値を入力して実行した際に呼び出されるスクリプトです。

function replaceBackgroundColor(from, to) {
  var sheet = SpreadsheetApp.getActiveSheet();
  var data = sheet.getDataRange();
  var backgrounds = data.getBackgrounds();
 
  var replacedCount = 0;
  for(var row = 0; row < backgrounds.length; row++) {
    for(var col = 0; col < backgrounds[row].length; col++) {
      if (backgrounds[row][col] != from) continue;
      sheet.getRange(row + 1, col + 1).setBackground(to);
      replacedCount++;
    }
  }

  ui = SpreadsheetApp.getUi();
  ui.alert('Successfully replaced!', replacedCount + ' cell\'s background color replaced to ' + to + ' from ' + from, ui.ButtonSet.OK);
}

 特に目新しいことはないかと思います。alertgetUiでUIから使用可能です。

感想

 まず不安に思ったこととしては、「それ標準機能であるよ」と言われることです。その辺どうなのでしょうか、、、たまにコード書きたい欲がまさってしまい、ちゃんとした検索を行わないという傾向があります。

 すごく感心したのが、google app scriptのモーダルでhtmlを表示できること、テンプレートを使用できることです。テンプレートを使用できるということは、webアプリケーションに近い何かを作れることだと思います。例えばSQLドライバーを作ってみたりすると楽しいかもです、スプレッドシートをデータベース、シートをテーブルと見立てて、とか思ったりします。

 javascriptでユーザーインプットを受け取る方法は、alertとかpromptとか柔軟性がなくて困ることがありました。webだと画面側をリッチにすればいいのですが、spreadsheetだとそうもいかないので、それを解消するためにガイドが役に立つのだと思います。

PhRUG(Philippine Ruby Users Group) September 2017 Meetupに行ってきた

 最近フィリピンに滞在しています。2017/09/21にフィリピンのRubyユーザーグループのイベントが行われるということで、海外勉強会ってどんな感じだろう?日本と違いがあったりするのかな?とか思い参加してきました。たくさん写真撮ってくるの忘れました。

 とりあえず結論、強く思ったのは、フィリピンでも日本でも、技術系の勉強会は大体同じ雰囲気、ということです。

概要

 イベントはmeetupから発見しました。PhRUG September 2017 Meetup  会場のホストはShield Foundryというベンチャー企業で、HPも綺麗な感じです、ただHPを見ても何を作っているかはよくわかりませんでした。場所はShield Foundryのオフィス、マニラのBGC(Bonifacio Global City)にあります。BGCにはいったことなかったですが、未来っぽい町でした。外資系企業がたくさんあるみたいで、金持ちが多いみたいな話です。

 余裕を持って会場へ向かったのですが、交通渋滞、ビルを間違える、などのアクシデントで、会場に着いたのは開始30分後くらいになってしまいました…

 会場のShield FoundryはいかにもITベンチャーといった感じでした。広さは3~40畳くらいで、オフィススペースとキッチンスペースに分かれています。オフィススペースには長机に沢山の椅子(アーロンチェア?)、各椅子の前の机にiMacとモニターがずらりと並んでいます。それぞれ漫画とか本とかも乗っていました。その他、バランスボールあり、サッカーボールあり、あとサッカーのアレ(二人でバーを回してボールを蹴り合うゲーム)もありました。テック系は世界共通なんだな、と少し安心しました。

 受付を済ませてプレゼンしているキッチンスペースに入ると、ビール手渡され、真ん中のダイニングテーブルにはピザが置いてあり、ご自由にどーぞといった感じでした。  

イベント

f:id:ushumpei:20170923211805j:plain

 最初の発表は、会場に到着したときは既に始まっていて、ちょっと詳細がわからなかったです…。発表者はエンジニアの一日、のようなスライドを表示していました。とりあえず自分はタガログ語が一切できないので、英語でプレゼンしていてホッとしました。ただ英語がそこまでできるわけでもないし、トピック不明だったのでなんとなく頷いたりして過ごしました。

 2番目の発表はrailsのパフォーマンス改善やテストなどの発表でした。bootsnap, PhantomJS, rack-mini-profiler, New Relic, その他諸々、をそれぞれいい感じに使って、計測、改善したよ!という感じです。そうですよね。納得な感じです。

 3番目の発表はjupyter/notebookを使って見た系の発表。マークダウン + コードでドキュメントかけるやつです。これ自体は先輩に教えてもらっていて知っていたのですが、バグレポートとかに使って、楽に可視化していこうよ!といった、やっぱりそう考えるよね的な共感を持ちました。

 4番目の発表は、開発Tipsの紹介でした。gitprecommit-hookでコミット前にrubocopしてます、とか。railsのサーバープロセスの優先順位あげたらなおった、とか。やっぱりコミット前にちゃんとしておきたいと思うよね!という再度、強い共感を持ったりしました。

感想

 会が終わった後にエンジニアの方にフィリピンのプログラミング言語事情について尋ねて見たところ、大企業とか、求人の多さで言えばjava, phpベンチャーruby, pythonといった感じです。scalaはとてもいい言語だと思うけど学習コストが高いのと、まだ事例をそこまで聞かない、とのことでした。日本では既に事例があるので、このあたりの情報共有とか何かできるかも(逆もあるだろうし。そういえばtwitterは?scalaの事例としてはどう捉えていたのだろう?国内事例がない、という感じなのだろうか?)。

 フロントエンドのjavascriptフレームワークに関しては、angular2, reactjavascriptフレームワークはリッチすぎて、本当に欲しいものがその中の1つだったりする、という日本でも割と聞く話を伺うことができました。ionicでネイティブを、とかいう話題もちらほら聞くようです。

 ここまで書いて、強いて挙げるならフィリピンと日本のIT界隈の相違点はなんなのだろうか?という方向に思考が向かいました。フィリピンの方が学歴に関して厳しいのかな?という予想を最近持っていますが、まだちゃんと議論したことがないので結論としては不十分と思います。さらに交流を重ねなければと思います。

 参加する前はブログを書くつもりがなかったため、少々微妙なレポートになってしまいました。来月もグループがあるそうなので、もし行くとなればもっといい感じにレポートしたいと思います。meetup便利です。

iOS11アップデートについていくためのSwift入門(主観)

 2017/09/20に、iOS11がリリースされました。今回のアップデートでは機械学習やSiriアプリ、ARなど様々な新機能が使えるようになりました。リンクを見ていて、「ちょっとSwift読めるようになっておかないと、おそらく半年くらいつまらなくなってしまう…上がってくるニュースを見ているだけになってしまう…」と思ったのがこの記事を書こうと思ったきっかけです。

 リファレンスとしてはAppleのサイト、The Swift Programming Language (Swift 4): A Swift Tourを使います。学習の目的はSwiftのコード(主に今後発表されるであろう「iOS11で追加された〇〇を使って見た系の記事のコード」)を読めるようになることです。

 Hello, world!を目標、と思ったのですが、ベタ書きで終わってしまいました。

print("Hello, world!")

概要

 リファレンスの内容は、Swiftを書き始めるのに十分な知識を得るためのツアーです。記事がSwiftのプロジェクトとしてまとめられていて、ダウンロードできるのでそれを使って学習していきます。

 セクションは次のようになっています;

  • Simple Values
  • Control Flow
  • Functions and Closures
  • Objects and Classes
  • Enumerations and Structures
  • Protocols and Extensions
  • Error Handling
  • Generics

 本記事ではリファレンス内に記載されている情報で、主に書き方に関することをメモしていきます。すでに序文の時点で以下の二点が記載されています;

  • グローバルスコープの処理は勝手に実行されるので、エントリーポイントとしてのmain関数が必要ない
  • 文末にセミコロンは必要ない

Simple Values

  • 変数宣言について、定数はlet、通常の変数はvarで宣言する: var hoge = 1とか。
  • 明示的な型の宣言はlet piyo: Double = 70 (後置型宣言)
  • \()で文字列に変数を埋め込める。"template \()"
  • 3ダブルクォート"""で複数行の文字列を囲むことができる
  • 配列リテラル["hoge", "piyo",]が使用可能
  • [“hoge”: 1, “piyo”: 2]辞書も可能
  • 空の辞書は[:]

Control Flow

  • if, switch, for-in, whileとかありますがこの辺は不思議なさそう
  • 型?でOptionalな変数を宣言できる(例var hoge: String? = "hoge")
  • if let hoge = { ... }でOptional変数から値を取り出すことができる、取り出すことができたらブロック内の処理を実行するという記法
  • if var piyo = { ... }ともかけるが、多分そんなに使わない
  • 取り出した変数のスコープはブロック内
  • その他にOptional変数を扱う方法としては、??を使って、defaultValue ?? optionalValueと言うように記述すると、値が取り出せる時のみ使用される
  • switchcasedefaultで構成していきます。breakは不要、defaultは必須です。
  • 0..<4で、0,1,2,3の範囲のレンジを作成できる

Functions and Closures

  • 関数定義はfunc、引数の定義はhoge: String(後置型宣言)、アロー->で戻り値の型を記述: func hoge(hoge: String) -> String { ... }
  • 引数を名前付きで渡すことができるhoge('fuga', piyo: 'giyo')これは定義時にfunc hoge(_ hoge: String, piyo fuga: String)と言うようにする。引数のラベルと言うもので、_はラベルなしを意味する。
  • 高階関数も作れる: (Int) -> Intとかで型を宣言
  • クロージャ使える:
{ (hoge: Int) -> Int in // クロージャの型と変数名
  return hoge * 2
}
  • 場合によっては型も省略できるらしいので、{ hg in ... }とかブラケットとinが出てきたらクロージャとして読む。引数も$0, $1と順番で記載されることがあるので、{ $0 < $1 }とか書かれていても混乱しないように。

Objects and Classes

  • クラスのインスタンス化はClassName()
  • フィールドはベタ書きでいいが、外部からアクセスするにはgetterメソッドが必要
  • イニシャライザー(コンストラクター)はinit、クラス内のメソッドでインスタンス自身を参照するにはself、デイニシャライザーはdeinit
  • 継承はclass SubClass: SuperClass { ... }
  • setter定義時のnewValueパラメーターは予約語
  • method?.hogemethodの戻り値があればhogeを呼び出すと言うことができる

Enumerations and Structures

  • enumはcaseで値を振っていく。ラベルから数値を取り出すのはrawValue、数値からラベルを作成するにはイニシャライザを使う(この時の値はOptional)。
  • structで構造体を定義。だいたいの機能はクラスと同じだが、構造体は常に値渡しされる。
  • class, struct, enumは似ている

Protocols and Extensions

  • protocolでインタフェースのようなものが定義できる
  • メソッドにmutating宣言することで、インスタンスプロパティを変更するメソッドを定義できる(クラスの時は不要、struct,enumの時だけ)
  • extensionで既存の型に機能拡張を行える、Protocolの機能を追加するのにも使える

Error Handling

  • do...catch構文でかく、怪しいところにtryをかく
  • try?でOptional値を取り出す

Generics

  • ジェネリクス<Item>で、Itemを変数のようにしようできる(数学でいう変数的な、x的な): var hoge = [Item]()Item型の配列を宣言とか。
  • where句もジェネリクス関連、親、子などの型の検証を記述できる

感想

 以上、自分がSwiftを読む際に詰まりそうなところをまとめ終えました。逆に言えば詰まらなそうなところは、私の主観の範囲で無視しています。

 Optional型の存在がちょっと気になりました。だいぶ面白そうです。またクラス、構造体、列挙型に関する扱いも違った見方を得られて興味深いです。自分としては列挙型はそこまで重要視していなかったのですが、Swiftをかくことでもっと本質をつかむことができるかもと、少し前のめりになりました。

 iOS`界隈の盛り上がりに少しでもついていきたいと思った次第です。

文法の勉強のため簡単なObjective-CのコードをXcodeで実行してみた

 前回、React Nativeのソースコードを読もうとして挫折したので、基礎文法を勉強するためにObjective-Cで何か書いて見ます。

 その前にObjective-Cwikiを少し読んで感じをつかみます。とりあえず読むために必要そうなこと3つです。

  1. C言語としてもいける、コンパイラディレクティブを駆使する
  2. メッセージ式 [Object method:arg1:arg2]でメソッド呼び出し
  3. クラス定義は定義部(.h)と実装部(.m)に分かれている

書いて見る

環境: Xcode: Version 8.3.3 (8E3004b)

 ではとりあえずhello worldします。別にアプリケーションが書きたいわけではないので、コマンドラインツールプロジェクトとして書きます(もっと言えばコマンドラインツールが書きたいわけではないですが、実行の構成方法が不明だったため一旦これでいかせていただきます)

 まずXcodeを起動してFile > New > Projectを選択します。モーダルが表示されるので、macOSタブのCommand Line Toolを選択しNextを押します。名前などは自由に設定します、ただし使用言語はObjective-Cにして、初期設定を終えます。

f:id:ushumpei:20170919225414p:plain

 次にコードを書いていきます。すでにmain.m(エントリーポイントとなるファイル)が生成済みだったので、そこに処理を記述していきます。クラスの定義の練習をするためにHelloクラスを作成しましたのでそれを呼び出します。(@コンパイラディレクティブの他にもリテラルを使用するときの接頭辞として使う、いやおそらくコンパイラディレクティブでリテラルを変換している?)

  • main.m
#import <Foundation/Foundation.h>
#import "Hello.h"

int main(int argc, const char* argv[]) {
    @autoreleasepool {
        Hello* object = [[Hello alloc] init];
        NSString* message = @"Hello world!";
        [object setMessage:message];

        [object say];
        NSLog(@"[object message]: %@", [object message]);
    }
    return 0;
}

 次にHelloクラスをNew > FileからmacOSタブのCocoa Classを選択します。こうするとHello.hHello.mファイルが生成されます。内容を記述していきます。以下を定義しています、

  • 挨拶文を保持するmessage変数
  • 挨拶文を取得するmessage関数
  • 挨拶文を格納するsetMessage関数
  • 挨拶文をコンソールに出力するsay関数

です。

  • Hello.h
#import <Foundation/Foundation.h>

@interface Hello : NSObject {
    NSString* message;
}

-(NSString*) message;
-(void) setMessage: (NSString*) s;
-(void) say;

@end
  • Hello.m
#import "Hello.h"

@implementation Hello

-(NSString*) message {
    return message;
}

-(void) setMessage: (NSString*) s {
    message = s;
}

-(void) say {
    NSLog(@"say: %@", message);
}

@end

動かして見る

 Xcodeのウィンドウ上部メニュー左辺りにある▶︎(Runアイコン)を押してコードを実行します。

f:id:ushumpei:20170919230326p:plain

感想

 コンパイラディレクティブをちゃんと覚えていけばある程度読めるようになると思いました。@autoreleasepoolGCということでいいのかな?いやちゃんと覚えないといけないですね。とりあえず公式ドキュメントをこの辺から探して見ます。

 書けるようになる必要があるか?という問いに関しては一瞬、「まあでもSwiftあるし、、、」とか思いましたが、まだまだ使われているため、やっておいて損はなさそうな気がします。

React Nativeの画像遅延読み込み(ライブラリのソースを読んで見る)

 この記事を要約すると、「画像遅延読み込みの方法が知りたくて、ライブラリのソースコードを読んで、Objective-Cのコードにたどり着いて、次に進めなくなってしまい一旦諦めたけれど、これを糧にもっと勉強しようという気になった」という自己満話です。

概要

 少し前までWebサービスのユーザーのインターネット回線速度をそこまで強く意識したことがありませんでした。

 もちろんパフォーマンス計測はしていましたが、サーバー側の処理を改善することで対処していて、あくまで一般的なユーザーは回線がある程度の速度であることを仮定していましたし、回線が弱すぎるのはユーザー側の問題としている部分がありました。

 この回線の強弱に関する考え方が私が感じた、モバイルアプリとWebの大きな違いでした。

 モバイルでは回線が弱くなることが容易に起こりうるし、そのせいでアプリが操作不能になってしまうことは、ユーザーにとってストレスだと思います(操作不能にする方がいいケースもあると思います)。いや、といよりモバイルから閲覧するWebサービスとアプリとの違いでいうと、アプリだとオフライン状態でも使えるように作れる部分があるので、極力そうしていった方が親切だし、ユーザーも安心だよね、という話です。

 多分モバイルアプリ開発者の方々からすると当然すぎることだと思うのですが、私がこのことに気がつくためには、いつまでたっても画像が読み込まれないまま動かない、という経験が必要でした。(頭が悪いです)

読んでみる

 そんなこともあり遅延読み込みのライブライreact-native-image-progressとかに触れたのですが、そもそもどうやって遅延読み込みを実現しているか、ソースを読んでみようというのが今回の内容です。

 とりあえず一番はじめのリリース0.1.0を読む;

  • 基本的にはreact-nativeImageコンポーネント
  • Imageの読み込み開始、進捗、終了イベントハンドラにコールバックを登録している
  • Imageのprogressイベントについて苦労したんだろうなと感じられる
  • 遅延読み込み方法はImageのコードを読まなければわからない

 ということなのでreact-nativeImageコンポーネントのソースを読む;

  • RCTImageViewを読むべきとわかる

 ここからObjective-Cです、RCTImageView.mを読む;

  • RCTDirectEventBlockで詰まる…

すみません、詰まりました。

感想

 知らないことがたくさん見つかりましたし、いかに色々なことを知らないままで使っていたかわかりました。予想として遅延読み込みに関してはjavascriptfetchメソッドとかで実装されてて、簡単に読めないかな、とか思って軽く記事を書き始めたのですが、普通にネイティブでした。

 結局わからずじまいになってしまい、かなりダメダメなので、しばらくいろんなソースを読んでみることにすると思います。Objective-Cの基本的な構文も抑えて今回の詰まったところを解消したいです。あとReact Nativeの仕組みもちゃんと知りたくなりました。

 余談ですがFacebookのライセンスの話、どう捉えるか考えなければ。。。

ハノイの塔

なんだか久しぶりにjavascriptハノイの塔を解いて見ました。以下の関数は、number枚のハノイの塔をtime回操作した時の状態を計算するものです。特に目新しいものではないと思います。

const hanoiSnapshot = (number, time) => {
  // 円盤の移動が完了した以降の操作はエラーを表示(良いやり方かどうかは悩む)
  if (time > Math.pow(2, number) - 1) throw new Error(`${Math.pow(2, number) - 1}回目の操作時点ですでに塔は完成しています`);

  // 三本柱の配列データを定義
  hanoi = [[], [], []];

  // 1~number番目の円盤それぞれの場所を計算して柱に格納して行く
  for (let i = 1; i <= number; i++) {

    // 移動する方向
    const direction = Math.pow(-1, number + i + 1);

    // 移動した回数
    const moveCount = Math.floor((Math.pow(2, i - 1) + time) / Math.pow(2, i));

    // 1番目の柱を位置0とした時の円盤の絶対位置
    const absolutePosition = moveCount % 3;

    // 方向を加味して場所を0~2に正規化して決定
    const position = (direction * absolutePosition < 0) ? (3 + direction * absolutePosition) % 3 : absolutePosition;

    // 柱に円盤を追加する
    hanoi[position].unshift(i);
  }

  return hanoi;
}

感想

イメージとしては円盤は方向と速度を持っていて、3つの柱を移動しつつ、くるくる回って行る感じです。数学で三乗根の乗算を考えるときに書くような図をイメージするとわかりやすい気がします。

wgetでサイズ確認

ものすごく小さな話かつググったら速攻出ますが、自分にとっては切実な問題なのでメモします。

結論から言うと--spiderオプションです。

$ wget --spider ***URL.zip***

Spider mode enabled. Check if remote file exists.
--2017-08-31 09:49:43--  ***URL.zip***
...
Length: 11121426 (11M) [application/zip]
Remote file exists.

補足(curl)

たまにwget入ってないよ、となる時があるのでcurlの場合も追記します。普通にヘッダ取得しているだけです。

$ curl --head ***URL.zip***

HTTP/1.1 200 OK
...
Content-Length: 11121426
...

(単位byte)

ちなみにファイルのダウンロード自体はcurl -O ***URL.zip***なのですね。勉強不足です。

感想

一日の通信量上限のあるネット環境で生活しているため、迂闊に大きいファイルをダウンロードすると終わってしまう、と言う背景です。

URLの拡張子がzipな理由は特にないです。

辞書.appのデータ構造の補足

注意: 結論としては補足になっていません。。。RELAX NGXMLスキーマここで勉強した際のメモ書き程度になっています

先日書いた辞書.appの記事ではあまり辞書を作ることに関して調べきれていなかったところがあるので追記します。先日の記事でも単語と意味を登録することはできましたが、MyDictionary.xmlの構造(辞書のデータ構造)について補足していきたいと思います。

MyDictionary.xmlの構造(辞書のデータ構造)

MyDictionary.xmlRELAX NGというxml schema言語で記述されています。xmlに明るくないためやや中途半端な説明になりますが、RELAX NGは外部のデータセットを使用する前提で使われることが大半のようです(参考)

xmlなのでデータを定義するために、簡単な方法を提供してくれているそうです。例えば子要素はelement、属性はattributeなどがあり、それらに対し1つ以上持つ<oneOrMore>、つけてもつけなくても<optional>、どちらか一方だけ<choice>、などの制約をツリーとして書いていくことができます。私のイメージではタグを定義して作っていく感じです。

まず最初の方の行を見ていきます。

<?xml version="1.0" encoding="UTF-8"?>
<d:dictionary xmlns="http://www.w3.org/1999/xhtml" xmlns:d="http://www.apple.com/DTDs/DictionaryService-1.0.rng">
...
</d:dictionary>

ここでは、2つのネームスペース、xmlhttp://www.apple.com/DTDs/DictionaryService-1.0.rngのを宣言しています。またxmlns:dの部分で、d:というプレフィックスを宣言しているため、データセットを使用する際は語頭にd:をつけて使用していくことになります。(ようやくd:entryなどd:をつけて記述していた意味がわかりました)

続いてデータセットを見てどんな定義があるのかを見ていきます。内容は/Dictionary Development Kit/documents/DictionarySchema/modules/dict-struct.rngに記載されております。(http://www.apple.com/DTDs/DictionaryService-1.0.rngはアクセスしても404でちょっと分かっていません。ネームスペースとしてだけ使っているのでしょうか?)

webで公開されているものもあるので、適宜そちらも参照ください。

まず初めに<grammer>が全体を囲っていることから、Named patternsと呼ばれるタグを分割して定義できるようにするパターンで記述していることがわかります(リンク先の セクション4. Named patterns)。

<grammer>は、1つの<start>と複数の<define>を持ちます。<define name="hoge">と定義したものは、<ref name="hoge">により参照することができるため、定義を組み合わせて最終的な<start>を組み立てていく形になります。

ここで定義されている主なものを見てみましょう;

dictionary

<define name="dictionary">
    <element name="dictionary" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng">
        <zeroOrMore>
            <ref name="style"/>
        </zeroOrMore>
        <ref name="dictionary.attlist"/>
        <oneOrMore>
            <ref name="entry"/>
        </oneOrMore>
    </element>
</define>

dictionaryの定義は、

  • 実態はdictionaryタグ。アップルのネームスペースを属性として指定している。(ネームスペースの必要性の理解が曖昧です。名前が一般的なので、重複しないようにという慣習的なことでしょうか?それかプレフィックスを共通化するためとか関係している?)
    • タグは0個以上のstyleを使用できる
    • タグは1個以上のentryを使用できる
    • タグはdictionary.attlist属性リストを使用できる
      • dictionary.attlistはXTHMLのバージョンと国際化指定を属性として持っているようです

refでは参照先がelementなのかattributeなのかわからないため、どう表現したら適切か悩みます(おそらく言語で表現しにくいからこそ、この記法が使われているのでしょうが)。

entry

<define name="entry">
    <element name="entry" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng">
        <ref name="entry.attlist"/>
        <zeroOrMore>
            <ref name="index"/>
        </zeroOrMore>
        <ref name="Flow.model"/>
    </element>
</define>

entryの定義は、

  • 実態はentryタグ。
    • 0個以上のindexが使用可
    • Flow.model(なんだこれ。内部にhtml書けることと関係している?)が使用可
    • entry.attlistが使用可
      • entry.attlistid.attrib属性が使用可能(<index id="hoge"とか書ける)
      • parental-control属性が使用可能(任意)
      • title属性が使用可能(任意) <- (なぜなくてもいいのだろう?)

index

<define name="index">
    <element name="index" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng">
        <ref name="index.attlist"/>
    </element>
</define>

<define name="index.attlist">
    <attribute name="value" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng">
        <text/>
    </attribute>
    <optional>
        <attribute name="title" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng">
            <text/>
        </attribute>
    </optional>
    <optional>
        <ref name="parental-control.attrib"/>
    </optional>
    <optional>
        <ref name="priority.attrib"/>
    </optional>
    <optional>
        <attribute name="anchor" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng">
            <text/>
        </attribute>
    </optional>
    <optional>
        <attribute name="yomi" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng">
            <text/>
        </attribute>
    </optional>
</define>

indexの定義は

  • 実態はindexタグで、属性としてvalue、オプションの属性としてtitleparental-controlpriorityanchoryomiなどが使用可能

といった形になっています。

つまり?

つまり今のところ、そういうデータ構造になっているということがここを読むとわかるようになったというだけのことでした。。。

これらのデータ構造が辞書データとして取り込まれた時に実際どのように扱われるかをみていく必要があり、補足し切れていないです。

データ構造の読み方はわかった、ならばタグとしてどのような形をしているか、その子要素、属性はどのように扱われるか、それらのことを次にまとめていきたいと思います。

外国語学習のためにMacの辞書.appに追加する辞書を作成する

概要

Macでは設定すると三本指タップで単語の辞書検索が行えます、文章書いている時や読んでる時に作業を中断せずに意味を調べられるのですごく便利に使わせていただいています。

最近フィリピンで生活しているので、タガログ語を読み書きすることがたまにあります。わからない単語を調べるために辞書.appに追加する辞書ファイル(.dictionary)が欲しかったのですが、ネットを探しても見当たりませんでした。

html形式の辞書を見つけたので、Appleの公式ガイドライン(Dictionary Services Programming Guide)から辞書ファイルを作成してみました。その時の手順をメモしておきます。

注意: 以下の手順を行うにはApple Developer Programへの登録が必要です

Dictionary Development Kitのダウンロード

ガイドラインの指示に従い、Downloads for Apple DevelopersからAuxiliary Tools for Xcode - February 2012を検索してダウンロードします。

ダウンロードしたdmgを開くと、Dictionary Development Kitディレクトリがあるので、/Developer/Extras/以下にコピーします。その後、その中にあるproject_templateを自分用の辞書に書き換えていきます。(テンプレートなのでコピーとかして使っていきます)

テンプレートの中身はこんな感じです。

.
├── Makefile
├── MyDictionary.css
├── MyDictionary.xml
├── MyInfo.plist
└── OtherResources
    ├── Images
    │   └── dictionary.png
    ├── MyDictionary.xsl
    └── MyDictionary_prefs.html

2 directories, 7 files

バリデーションツール: jing.jar

辞書ファイルはxmlで、RELAX NGという記法で作成します。xmlフォーマットバリデーションのためjingを、githubから落としてきてビルドしておきます。

$ git clone git@github.com:relaxng/jing-trang.git
$ cd jing-trang
$ ./ant

javaがいります。jdkインストールしてJAVA_HOMEの設定もしておきましょう。

辞書の作成

MyInfo.plistの編集

製作者などのメタ情報を記載していきます。特に配布の予定はないのでざっと変更したくらいです。各キーの説明はCreating Dictionaries > Table 2-1を参照ください。

一旦make

まだ辞書の内容はテンプレートのままですが、完成イメージを見たいのでとりあえずmake & make installしてみます。

$ make
"""/Developer/Extras/Dictionary Development Kit"/bin"/build_dict.sh"  "My Tagalog English Dictionary" MyDictionary.xml MyDictionary.css MyInfo.plist
- Building My Tagalog English Dictionary.dictionary.
- Cleaning objects directory.
- Preparing dictionary template.
- Preprocessing dictionary sources.
- Extracting index data.
- Preparing dictionary bundle.
- Adding body data.
- Preparing index data.
- Building key_text index.
- Building reference index.
- Fixing dictionary property.
- Copying CSS.
- Copying other resources.
- Finished building ./objects/My Tagalog English Dictionary.dictionary.
echo "Done."
Done.

$ make install
echo "Installing into ~/Library/Dictionaries".
Installing into ~/Library/Dictionaries.
mkdir -p ~/Library/Dictionaries
ditto --noextattr --norsrc ./objects/"My Tagalog English Dictionary".dictionary  ~/Library/Dictionaries/"My Tagalog English Dictionary".dictionary
touch ~/Library/Dictionaries
echo "Done."
Done.
echo "To test the new dictionary, try Dictionary.app."
To test the new dictionary, try Dictionary.app.

辞書.appを開き、環境設定から自分の辞書を選べるようになっているので、探してチェックを入れます。これで三本指タップで自分の辞書が表示されるようになりました。

後は辞書の内容をちゃんと書いていくだけです。

MyDictionary.xmlの編集

今回は簡単な辞書を作成していきます。タガログ語から英語の翻訳はwebからスクレイピングしました。

単語の定義ファイルは以下のようになりました

<?xml version="1.0" encoding="UTF-8"?>
<!--
   This is a sample dictionary source file.
   It can be built using Dictionary Development Kit.
-->
<d:dictionary xmlns="http://www.w3.org/1999/xhtml" xmlns:d="http://www.apple.com/DTDs/DictionaryService-1.0.rng">
<d:entry id="dictionary_application" d:title="Dictionary application">
    <d:index d:value="Dictionary application"/>
    <h1>Dictionary application </h1>
    <p>
        An application to look up dictionary on Mac OS X.<br/>
    </p>
    <span class="column">
        The Dictionary application first appeared in Tiger.
    </span>
    <span class="picture">
        It's application icon looks like below.<br/>
        <img src="Images/dictionary.png" alt="Dictionary.app Icon"/>
    </span>
</d:entry>
...
<!-- 単語ごとにd:entryタグを作っていきます -->
<d:entry id="一意な値" d:title="単語名">
  <d:index d:value="検索キー"/>
  <h1>Hoge</h1> <!-- 単語のの内容、htmlかけます -->
</d:entry>
...
</d:entry>
</d:dictionary>
  • htmlMyDictionary.cssマークアップ可能です
  • OtherResourcesディレクトリにアセット、例えばOtherResources/hoge/image1.pngを入れておけばhoge/image1.pngで参照できます

以上です。

感想

ものすごい必要最小限のものができました。 単語間のリンクとかうまくいっていない…今度調べないと…

何よりも辞書を作る作業がとても面倒でした。著作権フリーのものを使って、.dictionary形式で配布したほうがいいかもしれません。というか絶対どこかに「タガログ -> 英語」とか「タガログ -> 日本語」あると思うのですが、なんか見つからないです…

あとhtmlから抜いてきたのも失敗でした、他の辞書フォーマットから変換する処理を書いた方が楽だったかと思います。

iTunes ConnectにアプリをアップロードしてTestFlightでテスト

概要

iTunes ConnectにアプリをあげてTest Flightする方法がわからなかったので、多分これでいけるんじゃないかという手順をまとめました。個人的にメモしておきます。間違い、訂正ありましたら教えていただきたいです。

前提としてApple Developer Program登録済みです

当記事で使用しているXcodeのバージョンは8.3.3です、7以下のバージョンでは「3. XcodeからArchiveをアップロード」で言及しているAutomatically manage sigining機能は使えないようです

手順

0. 既存アプリのリネーム(optional)

簡単のため既存のReact Nativeアプリを再利用します。他の環境の方は無視していただいて結構です。

React Nativeアプリのリネームは簡単です。react-native-renameをインストールし、コマンドを発行します。

$ npm install -g react-native-rename
$ cd path/to/project/root
$ react-native-rename 新しいアプリの名前

1. Apple Developer Portalで新しいApp IDを作成

Developper Portalにログインし、Certificates, Identifiers & Profiles画面のサイドメニューIdentifiers > App IDsをクリックします。画面右上の+を押してApp ID作成に必要な情報を入力していきます。以下は各入力項目の説明です。

f:id:ushumpei:20170710170637p:plain

  1. App ID Description
    • name: 整理しやすいような名前をつける
  2. App ID Suffix (以下どちらかを選択、今回はExplicit App ID)
    • Explicit App ID > Bundle ID: reverse-domain name style stringとか一意になるように名前をつける
    • Wildcard App ID > Bundle ID: com.domainname.*のように指定すると、App IDを複数の(iTunes Connectのレコードという意味での)アプリで使用できるらしいですが、今回は使用しません。

その他は特に変更しませんでした、お好みで設定してください。

2. iTunes Connectで新規アプリレコードを作成

iTunes Connectにログインし、マイ App画面の左上の+を押して新規アプリに必要な情報を入力していきます。

f:id:ushumpei:20170710170736p:plain

今回は、

  • プラットフォーム: iOS
  • 名前: 新しいアプリの名前
  • プライマリ言語: 日本語
  • バンドルID: 先ほどApp IDを作成した時のバンドルIDを選択。
    • セレクトボックスにはname - bundle_idのように表示されます
    • 選択肢に表示されない場合はリロードして見てください
  • SKU: バンドルIDと同じもの
    • SKUはiTunes Connect IDと呼ばれるiTunes Connect内で一意になる値のようです。

を入力しました。

3. XcodeからArchiveをアップロード

3.1. アプリの情報の編集

新しいアプリの名前.xcodeprojファイルをxcodeで開いてGeneralタブへ行き項目を編集します。(react-nativeであればプロジェクトルート以下のios/新しいアプリの名前.xcodeprojにあります)

f:id:ushumpei:20170710170904p:plain

  • Identity
    • Bundle Identifier: iTunes Connectで新規アプリレコード作成の際に入力した値
    • Version: 0.0.1
    • Build: 0.0.1
      • VersionとBuildは変えなくてもいいですがせっかくなので。
  • Signing
    • Automatically manage sigining: チェックを入れます、プロビジョニングファイルの作成などやってくれるのでおすすめです。
    • Team: 自分のチームを選択してください

以上で設定終了です。

3.2. Archiveのアップロード

画面上部、Product > Archiveを選択しアーカイブを実行します。しばらく時間がかかる処理です。アーカイブが完了すると、ウィンドウが立ち上がります。

注意: Archiveが選択できない場合があるので、こちらを参考にしてください。

f:id:ushumpei:20170710170940p:plain

アップロードしたいArchiveを選択し、ウィンドウ右のUpload to App Store...を押すとアップロードの準備に入ります(先にValidate...を押してからの方が堅実だと思います、今回はやりませんでしたが)。

チームを聞かれるので自分のものを選択すると、アップロード画面になります。内容が問題なければUploadを押してください。これも、しばらく時間がかかる処理です。アップロードが完了しビルドが作成されるとiTunes Connectからメールが来るので、気楽に待ちましょう。

ただし、失敗することもあるので、20分くらい音沙汰がなければアップロード画面の結果にエラーがないか確認しましょう。(Validate...してからのアップロードなら、多少は安心していいかもしれません)

注意: Uploadを押した後、処理の表示がAuthenticating with the iTunes storeで止まってしまうことがあるようです。全てに対応できる方法かはわかりませんが、この辺りが参考になりました

4. iTunes Connect でTestFlightのテストを開始

4.1. TestFlightの準備

再びiTunes Connectにログインし、マイ Appで先ほど作成したアプリを選択します。タブからTestFlightを選択すると、アップロードしたビルドが表示されているかと思います。

f:id:ushumpei:20170710172810p:plain

ビルドに「輸出コンプライアンスがありません」と警告が表示されています、この状態ではまだTestFlightでテストを開始することができません。ビルドの値を押すと、画面が切り替わり、コンプライアンス情報を提出というボタンを押すとモーダルが表示されます。今回特に暗号化していないのでいいえを選択しました。モーダル右下の内部テストを開始を押すと、テストの準備が整いました。

f:id:ushumpei:20170710173317p:plain

4.2. テストへの招待

サイドメニューのテスター & グループの小項目iTunes Connect ユーザを追加外部テスターを追加を選択しテストへの招待を行います。今回はiTunes Connect ユーザを追加から自分に対して招待を行いました。

5. 終わり

TestFlightからメールが届くので指示に従い、TestFlightアプリをインストールしている端末からテストが開始できるようになります!

感想

おそらくはこれでいいだろうというものをまとめてみました。自分の環境ではダメだったなど、ご指摘いただければ幸いです。

特に使いだしてから色々いじった気がするのでXcode周りの手順がやや不安です。

FlatListのデータ更新時に再描画されない

概要

ちょっと困ってしまって、検索が時間かかったので他の人の手助けになればと思いメモします。

問題

React NavigationとFlatListでリスト編集サンプルアプリを作成した時に、FlatListのデータ更新時に再描画されない問題に遭遇しました。

  • データをFlatListで表示する画面がある
  • 他の画面でデータを更新する
  • FlatListの画面を開くとデータが描画されていない(更新したものだけでなく、元からあったものの描画されない)
  • ちょっとスクロールすると再描画される

なんだこれ?と悩んでしまいました。

解決

検索するとextraDataを設定しろとか色々見つかったのですが、このIssueから解決できました。

github.com

コメントで言及されているケースと同じ状態だったのでFlatListremoveClippedSubviews={false}を追加してあげると再描画されるようになりました。

感想

removeClippedSubviewsは高速化のためのフラグだそうです。FlatListがラップしているVirtualizedListのドキュメントにこのプロパティに関する言及があります。またこのページに「バグを引き起こすかも」、という注意がされているのも確認できます。

上記の方法でもうまくいかないケースがあるそうなのでIssueを読んでみると何か糸口が見つかるかもしれません。