すてにゃん氏の技術ぶろぐ

技術っぽいこと書きます

Apple Swift で iOS開発

久しぶりのブログ更新。7/7から立命館大学にて"短期留学生"として研究をしていました。
自分の大学ではB4になったらシニアプロジェクトというのがあって、いわゆる卒論の代わりみたいな感じになります。僕はそのプロジェクトのために立命館大学にいま来ています。ここ3週間ちょいで色々進捗あったので忘れないうちにブログにメモっておきます。

目次

  1. 構想
  2. 開発環境
  3. サーバー構築
  4. iBeacon
  5. HTTPリクエスト
  6. JSON
  7. アカウント管理
  8. 地図
  9. 戦闘システム
  10. M7チップ
  11. ネットワーク通信
  12. これからの事
1. 構想

自分の大学ウースター工科大学と立命館大学の教授達と一緒に話し合った結果、プレイヤーが遊びながら健康的になれるようなゲームが作れたらいいなという結論に至りました。具体的には現実世界でリアルな友人と一緒に歩きまわって、iPhoneアプリ内で敵とエンカウントして倒したりお宝を集めたりするゲームです。簡単に言うと万歩計にRPG要素を入れた感じになります。

2. 開発環境

このアプリを作る上で僕達はAppleのiBeaconを使ってみようと思いました。ですがそれを利用するにはiOSデバイスが必要になるので必然的にiOSアプリを開発することになりました。自分は6月頃に二日間ほどObjective-Cを触ってハッカソンでアプリを作成したことがありますが、一緒に立命館大学にきた友人は全く触ったことがなかったので、ここは思い切ってAppleから発表されて間もないSwiftを使っちゃおうってことで決まりました。あまり深く考えずに決めましたがいざとなってはObjective-Cのコードも共存できるので大丈夫なはずです。

3. サーバー構築

アプリを作るにあたってサーバーが必要になってくるだろうなって最初に思ったので研究室のパソコンをサーバーにさせてもらいました。正直僕達はサーバーについての知識はほとんどなく(友人はPHPをある程度使えて僕はJavaScriptはできるけどサーバーサイドの経験は少なかった。2人ともデータベースの授業でMySQLはかじった程度)、ググりながらの作業になりました。最終的にUbuntu Serverをインストールしました。

How To Install Linux, Apache, MySQL, PHP (LAMP) stack on Ubuntu | DigitalOcean
How To Install and Secure phpMyAdmin on Ubuntu 12.04 | DigitalOcean

4. iBeacon

僕達のアプリは近くに居る友人と遊べる機能がつく予定なのでiBeaconを利用することにしました。iBeaconとSwiftを使ったサンプルアプリが幸いネットにありました。

SwiftとiBeaconを使ってお母さんが自分の部屋に近づいて来た事を警告するアプリをつくる - Qiita

5. HTTP リクエスト

いくらサーバーがあってもiPhoneアプリのほうからデータを送ることができなければ意味がないのでSwiftでHTTP POSTなどが実装できるか頑張った結果案外すぐできました。このへんで薄々気づきだしましたが、Swiftに関することをググるとわりとQiitaというサイトがヒットするのでStackOverflowの次にマークしておこうと思いました。

[Swift]POSTでAPIを叩く方法。(そしてJSONを取得する。) - Qiita
Tutorial: Post to Web Server API in Swift using NSURLConnection - iOS Life

6. JSON

JavaScriptが好きな自分はデータベースの情報はJSONファイルに出力したら便利なんじゃないか?と思ったのでPHPerの友人にそうしてもらいました。これでiPhoneアプリはそのリンクをアクセスしてJSONファイルをゲットしてちょちょいとJSONをパースできる…んだと思ってましたが予想外にSwiftでのJSONの扱いが大変だったのでここでちょっと苦労しました。JSONの複雑さにもよりますがイメージ的には以下のようにしないといけない感じでした:

var jsObj = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: &error)
if let hoge = jsObj as NSDictionary {
    if let piyo = hoge["fuga"] as NSArray {
        /* 処理 */
    }
}

未だにif let...っていうのに違和感を感じています。

7. アカウント管理

沢山の人のデータを管理するためには1人1人をなんらかの方法で区別する必要があります。このためには例えばユーザーIDを作ってもらってそれでログインさせるというのもアリですが、そのためには他人が自分のアカウントにログインしないようにパスワードも必要になってきます。ですがそういう情報はなるべく自分たちで管理したくないなと思いました(少なくともパスワードは暗号化する必要がありますしデータベースが流出したときのリスクを考えなければならない)。次に考えたのはiPhone自体の固有のIDのようなものを取得することです。検索しているとデバイストーケンというものがあるらしいですが、これを取得するのに意外と長い手順を踏まないといけないことが判明しました。最終的に僕達はアップルのAccountsフレームワークというものを使ってiPhone自体に保存されているフェイスブックツイッターのアカウント情報を利用しようと決めました。実際にアプリからツイッターフェイスブックへ投稿がしたかったらSocialフレームワークも多分必要になってきますが今回はログイン情報だけということでAccountsフレームワークだけで大丈夫そうです。

Apple Push Notification Service iOS - Accounts.frameworkを使ってFacebookのアクセストークンとか、メールアドレスを取得する - Qiita
swiftでtwitterアプリを作ったときにハマったこと - Qiita

8. 地図

プレイヤーの現在地や周りのプレイヤー・iBeaconの場所をみせるために地図も表示しなければならないです。僕たちはまず Google Maps SDK for iOS というものを導入しようと試みました。結論からいうと失敗しました。まずこのSDKは完全にObjective-Cで書かれているのですが、SwiftからObjective-Cを呼び出すのにBridging-Headerというファイルを作成してやらないとできないらしいのです。その通りにとりあえず書いてみて中々地図がアプリに表示されなかったです。ではとりあえずこのSDKについているサンプルコードをObjective-Cだけど実行してみようと思ってもなぜかそれすら上手く実行できませんでした。ここでこんなにつまずくのは時間が無駄になるなと思い、普通のiOSのMapKitを使おうかということになりました。そしたら結構パパっと書けたのでよかったです(あとでもし時間に余裕があればMapKit以外のものも試してみるかもしれません)。これで現在地と他のピンを表示させるアプリが作れます。あとは地図からプレイヤーの座標を検出してデータベースへPOSTなどもやってみました。

SwiftとMapKitで経路検索アプリを作成する - Qiita

9. 戦闘システム

戦闘システムについては基本的に僕の友人がずっと作ってたので詳しいことは言えませんがひとつ言えることは最初に彼はものすごくコンプレックスなゲーマー向けのRPGのようなものを考えていたのですが教授らからの指摘でシンプルにまとめました。まずこのアプリを作成するのに3ヶ月ほどしかないというのもありますし、歩きながら遊ぶアプリで歩きがメインなのでそこまでゲームをヘビーゲーマー向けにする必要はないということです。まあなんにしろ9月下旬までにはちゃんと動作するアプリが完成していることを祈っています。画面へのスプライトの表示には最近SpriteKitというのを使い始めました。

10. M7チップ

iPhone 5SからはM7チップというものが導入されたらしく、このチップはiPhoneがスリープ状態などのときでも歩数や現在の移動状況についての情報をずっと蓄積しているらしいです。なのでこれを利用すれば自分で歩数を数えるプログラムを書く必要がなくなりますし(そうする場合はちゃんと歩くときのノイズに対応するためにローパスフィルターなどをかけるようです)、今までは歩数を数えるためにはずっとiPhoneのバックグラウンドでアプリを起動しておく必要があったため電池をたくさん食いましたがこれを使えばその心配はないです。もちろん、これを使ってしまうとプレイヤーのターゲットをiPhone 5S利用者にしぼることになってしまいますが、この短期間の研究期間なのでとりあえずはやむを得ないと思います。時間が余ればほかのiPhoneのための対策も考えるかもしれません。
一つちょっと引っかかったのはよくwithHandlerというパラメータがここで登場したのですがSwiftで書き換える場合クロージャにしないといけない模様だったのでちょっとシンタックスなどを気をつける必要がありました:

Objective-C

[self.cmStepCounter startStepCountingUpdatesToQueue:self.operationQueue updateOn:1 withHandler:^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error) {
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        /* 処理 */
    }];
}];

Swift

cmStepCounter.startStepCountingUpdatesToQueue(operationQueue, updateOn: 1, withHandler: {numberOfSteps, timestamp, error in
    /* 処理 */
})

結果的にSwiftのコードがかなりシンプルになってるのが嬉しいです。self.cmStepCounterのように明示的に書く必要もないし、numberOfStepsがNSIntegerだとかも書かなくても大丈夫です。withHandlerのとこで{}を使ってクロージャにしているというのだけ気をつければ大丈夫でした。

ios - How to implement CMStepCounter CoreMotion - M7 Chip - Stack Overflow
M7 と少しだけ戯れてみた - griffin-stewieのブログ
iPhone 5sのM7チップを試してみる | ワンダープラネットエンジニア Blog

11. ネットワーク通信

サーバーはあるのでそれ利用して色々できますが、複数の人でネットワークプレイのようなことするのは難しいかなと思い色々ググっていました。そしたら色んなサイトにてGameKitを使えば簡単にネットワーク対戦ができると知ったので使ってみることにしました。とりあえず簡単なプログラムをつくってみようと思ったその瞬間です。GKSessionがどうやらdeprecatedだったようです。結局StackOverflowの回答をみてMultipeer Connectivityフレームワークを使ってみようと決めました。見た感じiOS6まではGKSessionが使えて、iOS7からはMultipeer Connectivityしか使えないようなので互換性では問題がありますが、M7チップのCore Motionフレームワークを使ってる時点でiPhone 5Sに限定しちゃっているのでとりあえず問題ではなさそうです。

GameKitを用いて複数のiOSデバイス間でP2P通信 - 5.1さらうどん
ios - GKSession is deprecated in ios7, what should i use now? - Stack Overflow

ググっているとMultipeer Connectivityフレームワークを使ったサンプルプログラムのソースを記載している人がいたので彼のコードをお借りしてSwiftで書き直して実行してみたらちゃんと動作確認できました。これはつい二日前のことです。

[iOS 7 SDK] Multipeer Connectivity framework で Peer-to-Peer Connectivity | hirooka.pro

12. これからの事

この数週間でサーバー系のことをやったりSwiftでシンプルなプログラムを沢山作ったりして充実していましたがそろそろバグだらけでも良いのでそれなりに遊べるような超アルファ版みたいなのを完成させたい気分です。そのためにもSpriteKitももっと触って色々とみんなと力を合わせてがんばりたいですね。ずっとコーディング漬けみたいな印象を受けるかもしれませんがしっかりとゲーセン行ったり日本橋清水寺訪ねたりしてるので結構楽しんでます。