「Android 開発者のためのSwift入門」という書籍が出ます

[amazonjs asin=”4897979838″ locale=”JP” title=”Android開発者のためのSwift入門”]

中西が、Tech Boosterの日高さんと一緒に書いた上記の本が5/27に発売されます。早ければ今週末くらいには書店やアマゾンから入手可能かもしれません。1

なぜこんな(タイトルの)本を書いたのか?

ネイティブでのAndroidアプリ開発とiOSアプリ開発の両方やっていると、それぞれのプラットフォームに違いはあっても、似ている部分も多々あることがわかります。そして、一方の経験がもう一方の開発に生きることもよくあります。

たとえばどちらのプラットフォームでも、「メインスレッド(UIスレッド)では重い処理を行ってはいけない」という原則があります。

しかし、ネイティブのiOSアプリ開発ではこれまで、Objective-Cという開発言語を使用することが必須でした。前書きにも記載したのですが、個人的にはObjective-Cは非常に好きな言語の一つです。しかし、以下のような特徴があってハードルは高めだということも理解しています。

  • C言語をベースとしている
  • オブジェクト指向の記述方法として、C言語とは大きく異るSmalltalkライクな記法が追加されている

そのようなこともあって、Androidアプリ開発の経験があってiOSアプリ開発に興味もあるけれども手を出しかねているという話もよく聞きました。

マニア的にいろいろなプログラミング言語を学習してきた経験からも、ある言語での経験は新しい言語の習得に役立つことを知っています。Haskellなどのような関数型言語のようにパラダイムが大きく違う場合は、その理解に時間がかかったりもしますが、そういう場合でなければ、プログラミング言語は結構類似しているので、一つ知っていると他の言語の学習はしやすいものです。

そういう背景があって、本書の企画が生まれました。

なぜ共通開発ではなく、それぞれを学習するのか?

筆者たちが、ゲーム開発などの特定の領域を除いて、共通開発環境で複数のプラットフォーム向けに一つのアプリを開発するということのメリットがほとんどないと考えているためです。

バグ改修や将来的なメンテ、機能追加などを考えると、ネイティブ開発をすることが最終的には手間がかからないと考えているためです。特に、OSが提供する最新の面白い機能を利用しようとするなら、ネイティブ開発は必須です。

iOSについて

完全に一対一で対応するものがあるわけではないのですが、できるだけAndroid経験者がどういうものか理解しやすくなるように、「Androidの○○のようなもの」という説明をするようにしています。

また、Android経験者がiOSアプリ開発を行う際の壁の一つであるUIレイアウト作成については、ページ数を割いて解説をしております。Auto LayoutやSize Classesについても実際にハンズオン形式で確認いただくようになっています。

Swiftについて

Swift言語を駆使すると非常にマニアックなことも出来るのですが、実際の現場ではそこまでマニアックな記述方法が必要になることはほとんどありません。

それを踏まえて、以下の様な方針で言語仕様を解説しています。

  • 基本型(整数型や文字列型など)、ifやfor、whileなどの制御構文、といったJavaを知っていれば理解しているはずのことは、Swiftでの文法の特徴などを説明し、細くは説明しない
  • (Androidで利用するバージョンの)Javaからの移行でひっかかるだろう文法(例: Optional型)などはしっかりと説明する

言語仕様の詳細については、本書以外に、他の書籍やウェブサイトなどを参照してより深められることが望ましいと考えております。

本書では何について書いていないか

たくさんのことが書かれていません。一例を挙げると、インスタンスオブジェクトが参照カウンタ(Reference Counting)で生存管理されていることについてはあまり説明していません。

これは、ARC(Automatic Reference Counting)によってほぼ意識しないで記述できるためです。Androidアプリ開発者がiOSアプリを最初に学習する時点でそこまで説明するのは不要だろうと判断をしています。

ARC以降のiOSアプリ開発では、インスタンスオブジェクトの破棄(release)を意識しないといけない場面が少ないためです。

また、利用したUI部品のAPIを網羅的に説明するなどはしていません。

本書ならではで取り上げていること

Yahoo! JapanさんのYahoo! Open Local Platform(YOLP)の提供する地図SDKと、検索API(Web API)について説明しています。

標準の地図やGoogleさんのSDKやAPIを利用せず、上記を利用したのは以下の理由によります。

  • WebViewで表示するYahoo! Japanさんの地図サービスが、スマートフォン最適化され、さらに機能が充実している
  • 地図SDKで提供している機能が豊富なので、本書での学習後に独自の拡張に取り組んでいきやすい
  • 検索APIが提供する情報が豊富である

追伸

デザイナーさん向けに、AndroidとiOSを両方理解して、ちゃんとそれぞれに適したデザインを作る本とかどなたか出して欲しいですね。そしてベストセラーになってほしいです。


  1. 2015年5月21日現在、東京都内のいくつかの書店(書泉ブックタワー、ジュンク堂池袋本店など)に並び始めたようです。 

Android: NotificationListenerServiceとAccessibilityServiceの違い

Android 4.3から導入された NotificationListenerService で出来る機能は、AccessibilityServiceでも実現できる場合があります。

新しい NotificationListenerService を使わなくてもできるのであれば、昔ながらのやり方をすれば良いと考えられるかもしれませんが、新しいOSバージョンでは NotificationListenerService を利用し、どうしても古いバージョンでも対応する必要がある場合は AccessibilityService を併用することをおすすめします。

NotificationListenerService の利用を推奨する大きな理由は以下の通りです。

  • 通知管理のほとんどをシステム側に任せられ、またできることが増える
  • セキュリティメニューによって有効・無効が管理されるため、ユーザに通知管理をダウンロードしたアプリに任せることのセキュリティリスクを意識させやすい

業務用アプリであれば、対象端末をAndorid 4.3以上に限定して開発することで、工数を大幅に削減することも可能となるでしょう。システムを設計する際に、ソフトウェアの開発工数とAndroid 4.3端末の導入にかかる費用を合算して検討することで、お客様にとって最適なシステムを提案できるでしょう。 ((新型Nexus7など、Android 4.3を指定した端末の導入は現実的な選択肢になってきていると、弊社では考えております。))

Google Playで広いユーザに対して提供するアプリを開発しなければならない場合に、通知管理機能を開発しなければならない場合は、引き続きAccessibilityService も利用しての開発を頑張りましょう。

通知管理について

AccessibilityService を利用して、裏技的に通知を管理しようとする場合、通知が届く度にその情報をそのアプリ内で管理する情報として保持し、アプリが起動される度にそのアプリの通知情報を削除するといったことをアプリ内で行う必要があります。それをちゃんと設計・実装するには大きな工数が必要となります。

また残念ながら、管理のためにたくさんのコードを書いたとしても、通知バーに表示されている情報とアプリが管理している情報を正確に一致させることはできません。

NotificationListenerService を利用する場合、以下のことができるので、アプリ側で行う処理を非常にシンプルにすることが可能となりますし、アプリ内の情報を通知バーの情報と一致させることは容易です。

  • getActiveNotifications() で有効なすべての通知を取得する
  • cancelAllNotification() や cancelNotification() で通知を削除する
  • onNotificationPosted() で新しい通知の到着をトリガとして処理を実行する
  • onNotificationRemoved() で通知の削除をトリガとして処理を実行する

通知の即時実行と、実行後の自動削除を行うサンプルコード

以下のコードサンプルは、通知が届いたらすぐに通知をタップしたのと等価な処理を実行するものです。その時、通知が削除可能かどうか、タップした時に自動削除することを要求しているか確認して、どちらもtrueなら通知を削除する処理を呼び出しています。

@Override
public void onNotificationPosted(StatusBarNotification sbn) {
    Notification notification = sbn.getNotification();
    Intent intent = new Intent();
    PendingIntent pendingIntent = notification.contentIntent;
    try {
        pendingIntent.send(this, 0, intent);

        int id = sbn.getId();
        String tag = sbn.getTag();
        String packageName = sbn.getPackageName();
        int flags = sbn.getNotification().flags;

        // 通知がキャンセル可能、かつユーザによる通知タップ時に
        // 消える設定の場合に通知を消す
        if (sbn.isClearable() &&
            flags & Notification.FLAG_AUTO_CANCEL) {
            cancelNotification(packageName, id, tag);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}