fkm blog

software開発に関することを書いていきます

Xcodeで開発用サーバー向けビルドの時のアイコンを変える

小ネタだけど大事なやつ。

前回の方法で別アプリとしてビルドする方法はわかったものの、そのままだと同じアプリアイコンなので困る。

で、当然のように変更する方法があったのでご紹介。

開発用サーバー向けビルドのアイコンを追加する

DevAppIconと名前をつけておく。

f:id:fkm:20181017233644p:plain

TargetのBuild Settingsで指定する

Icon で検索すると次のように Asset Catalog App Icon Set Name がでてくるので、左端をクリック。すると次のようにConfiguration毎の設定がでてくるので、先ほどの DevAppIcon に書き換える。

f:id:fkm:20181017234038p:plain

気にしたほうがいいこと

おそらくAssetsとして本番用ビルドにアイコンが入ってしまう。なので本番用のをモノクロにして設定するといった、見られてもあんまり困らないようなものにしていたほうが良さげ。

XCodeでBuild Configuration毎にBundle IDを変える

開発用サーバー向けビルドと本番用ビルドで別のBundle IDが設定できると、別アプリとしてインストールできるのでとても便利。Androidだと applicationIdSuffix 相当の機能。

といっても設定箇所さえ分かれば一発。Targetの Build SettingsBundle で検索すると Product Bundle Identifier の欄が出てくるので、 Configurationに応じて変更してしまえばOK。なおBundle IDの設定が必要なPushとかFirebase Dynamic Linksとかが影響を受けるので注意。

f:id:fkm:20181016003312p:plain

XCodeで開発用サーバー向け設定を追加する

AndroidだとProduct Flavorで開発用サーバー向け設定を簡単に作れるけど、それをiOS/XCodeでやるには?という話

Configurationを追加する

まずはプロジェクト設定のところでConfigurationを追加する。 Debug を複製して Debug_DevServer と名前をつけておく。

f:id:fkm:20181014224428p:plain

コンパイル時のフラグを追加する

となりのBuild Settingsで flag で検索し、 Swift Compiler - Custom Flags を探す。その下に Active Compilation Conditions があるので、先ほど作成した Debug_DevServerDEBUG の部分をダブルクリックし、フラグ名として ENV_DEV_SERVER を追加する

f:id:fkm:20181014230502p:plain

Debug_DevServerでビルドするSchemeを追加する

次にSchemeのところで Edit Scheme を選び、Duplicate Schemeで複製。

f:id:fkm:20181014225650p:plain

複製したら Edit SchemeRunBuild ConfigurationDebug_DevServer に変更。

f:id:fkm:20181014230543p:plain

コード内で分岐する

#if フラグ#endif で分岐できるので、URLを本番用URLと開発用URLでわける。

#if ENV_DEV_SERVER
let BaseURL = "https://dev.example.com"
#else
let BaseURL = "https://prod.example.com"
#endif

これでOK!

コールバックとPromiseとasync/awaitと

情報整理の巻

コールバック

関数/メソッドを呼んだ時点ですぐ結果が得られない場合(非同期処理)、ぱっと思いつくのは次のようにコールバックを受け取る方法。

function getUser(id, callback) { /* ここで非同期処理 */ }
function getImage(id, callback) { /* ここで非同期処理 */ }

これを使って「ユーザーのプロフィール画像を取ってくる」みたいな処理は、次のように書ける。

getUser('user1234', (user) => {
    getImage(user.profile_image, (image) => {
        // わーい、プロフィール画像とれたー
    });
});

「非同期処理Aの結果を使って非同期処理Bをやる」というのが何度も続くと、どんどんネストが深くなっていく。これをコールバック地獄と呼ぶのはみんな知ってるよね?

Promise

そこで登場したのが Promise 。 非同期でなんらかの処理を行い、終わったら then() で渡した関数を呼んでくれる仕組み。さらに then() の中で何か値を返したり、 Promise オブジェクトを返したりすると、全体として「最後に返したオブジェクト」を扱う Promise オブジェクトになる。

考え方のベースにあるのは「ストリーム」(だと思われる)

実際のコードを見てみる。型があったほうがわかりやすいのでTypeScript風味に書いてみる。

function getUser(id): Promise<User> { /* Userを返すPromiseを作る処理 */ }
function getImage(id): Promise<Image> { /* Imageを返すPromiseを作る処理 */ }

これらを使うコードは、こんな感じ。

getUser('user1234')
    .then((user: User) => {
        return getImage(user.profile_image);
    })
    .then((image: Image) => {
        console.log(image);
    })

ネストが深くなるという問題は解決できそうだが、「ストリームを扱う」というのがベースにあるせいか、チェーンの中で繰り返しや条件分岐をやりたくなってきたときに困る。

async/await

「非同期処理を含む手続きを書きたい」のがこのテーマのゴール。ということで登場するのが async/await

「処理(ルーチン)を途中で中断しながら進めることのできる関数」を作るための仕組み。他の処理(ルーチン)と協調しながら処理をするので、「コルーチン」と呼ぶこともある(気がする)

「途中で止まって、続きを再開する」は、言語やライブラリのサポートが必要(たぶん)。

JavaScriptでそのような関数を書くには、 async をつける。

async function getUserImage(id) {
    const user = await getUser(id);
    const image = await getImage(user.profile_image);
    console.log(image);
}

非同期処理の結果を待って次に進みたい場合は await をつける。これだけといえば、これだけ。

途中で待ちながら処理を続けることができるので、次のようなループも書ける。

const images = [];
user.image_ids.forEach(async (id) => {
    const image = await getImage(id);
    images.push(image);
});

「async/awaitさえあればPromiseなんていらない」という方も中にはいるけど、「 Promisethen() に渡すのは関数」という性質はテストの面から有用だったりするので、うまく使い分けしていけばいいんじゃないかなぁ

Ractive.js + Page.js + Webpackのテンプレート(4)

このテンプレートの説明、その4 github.com

fetchTemplate() は指定したHTMLテンプレートを取ってくるメソッドとしたけど、そのHTMLテンプレートがどんなのかを説明する回

サンプルで読み込むHTMLテンプレートはこれ

<h1>Top</h1>

これが、 index.htmlid=container の部分に貼り付けられる。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>WebApp Template</title>
</head>

<body>
  <section id="container"></section>
  <script src="/page.js"></script>
  <script src="/main.js"></script>
</body>

</html>

HTML「テンプレート」なのでいろんな構文が使える。構文自体はとてもシンプルなのでHTML/CSSだけ書くのが得意という方でも心配ない。

あれ説明になってない?

Ractive.js + Page.js + Webpackのテンプレート(3)

このテンプレートの説明、その3 github.com

page.jsを使うと簡単にルーティングができるのがわかったので、次は各画面をどう実装するか。

/// <reference path="./IPage.ts" />
import Ractive from '../ractive';

export default class TopPage implements IPage {
    private app: IApplication;
    private ractive?: Ractive;
    
    constructor(app: IApplication) {
        this.app = app;
    }
    onCreate(): void {
        this.app.fetchTemplate('top.html').then((t: string) => {
            this.ractive = new Ractive({
                el: '#container',
                template: t,
            });
        })
    }
}

fetchTemplate()は指定したHTMLテンプレートを取ってくるメソッド。とれたら Ractive オブジェクトを new で作るだけ。お手軽!

ここ、ちょっと書き換えるか。。