今更iPhone3Gで自作アプリを動かしてみる

昨年9月に、とある方から、とあるお祝いの品として、使わなくなったiPhone3Gを譲り受けた。
最初から電話として使うつもりは全く無く、iPhoneという物を触ってみたいと思ったのと、丁度、Androidの実機で自作アプリを動かしていた頃で、iPhoneでも自作アプリを動かしてみたいと思ってお願いしたものだ。

入手後すぐ、意気揚々と「すべてのコンテンツと設定を消去」してiOSを最新の4.2.1にバージョンアップすると、ロック画面で「アクティベーションが必要です」か何かのメッセージが出て、何もできなくなってしまった。
数日後にアクティベーション専用SIMというものを手に入れて、起動はできたが、Safariを使ってても全体的に動作が遅いし、App Storeで色々なアプリをインストールしようとしても、「このアプリケーションはこのiPhoneとは互換性がありません」とか「このアプリケーションにはiOS X.Xが必要です」とかと表示されてほとんど失敗し、1週間もするとiPhoneに触るのが億劫になり、同時にiPhone3Gは古すぎて自作アプリを開発する上で条件が悪いことも知って億劫になり、その後はiPodの代わりとして使うだけになっていた。

iPhone3Gとその次機種であるiPhone3GS以降とでは、アプリ開発をする上で大きく異なる点がいくつかある。
・iPhone3Gは、インストールできるiOSのバージョンが4.2.1まで
 それに対してiPhone3GSならiOS 6もインストールできる
・3GはOpenGLES 1.1のみ、3GSなら2.0も使用可能
・3GはARMv6アーキテクチャー、3GSはARMv7アーキテクチャー
従って、折角ならiPhone3GS以降でアプリ開発を始めてみたかったのである。

その後、新しいiPhoneを買うかどうかを迷っていたのだが、年が明けて孔子の論語に記載される所の不惑となる年齢に達したことにより、やっぱり今使っているガラケーに何の不満も無い自分にはこれが使える限り金のかかるスマートフォンは不要、と惑わなくなり、当分iPhoneを買うことは無いと確定したので、もらい物のiPhone3Gで自作アプリを動かすことにした。

●開発環境入手
iPhoneの開発環境はXcodeであるが、3GはARMv6アーキテクチャーなので、Xcodeのバージョンに制約がある。Xcode 4.5でARMv6がサポートされなくなったので、Xcode 4.4.1がARMv6用の最新である(*1)

古いバージョンのXcodeは、https://developer.apple.com/xcode/downloads/の"Additional Tools"の所の"View downloads"というリンクの先のリストからダウンロードできる(Apple IDが必要)。

●iPhone3G用の"Hello, world!"アプリの作成
Xcode 4.4.1にはiPhoneシミュレーターも同梱されているので、シミュレーターで動作するiPhoneアプリならすぐに着手できる。

  1. XcodeのメニューバーからFile→New→Projectとする。
  2. "Choose a template"の画面で、iOS Applicationの"Single View Application"を選ぶ。

    ※Xcode 4.4.1のiOS Applicationのtemplateの内、iOS 4.2.1でも動くのは、"Single View Application"と"Tabbed Application"の2つだけのようである。"OpenGL Game"はiOS 4.2.1にGLKitが無いので動作せず、それ以外はiOS 4.2でStoryboardがサポートされていないのでbuildできない。
  3. "Choose options"の画面では、"Use Storyboards"のチェックは外す。
  4. プロジェクトが作成されたら、左上のソースコードツリーの"ViewController.xib"を開く。
  5. InterfaceBuilder(GUI編集画面)が開くので、"Label"とか"Round Rect Button"とかがある所から"Label"をドラッグし、iPhoneの画面の適当な所にドロップする。
  6. ドロップした"Label"をダブルクリックし、"Hello, world!"に変える。
  7. "Run"を押す
→ シミュレーターが開き、"Hello world!"が表示されるはず。

※4.〜6.について、InterfaceBuilderを使うのでなく、真面目に何らかのソースコードを書く場合は、以下の2つの参考記事のそれぞれの4ページ目辺りを参照されたい。
参考記事:
iOS SDKで始めるObjective-C入門(2):iOSアプリ開発初心者に捧ぐ開発環境Xcodeの概要とインストール - @IT
SDKで始めるiPad/iPhoneアプリ開発の勘所(1):いまさら聞けないiPhone/iPadアプリの作り方の基礎 - @IT

●実機へのインストールについて
さて、これが問題である。

正しくは、"Apple Developer"に登録(無料)した上、"iPhone Developer Program"に登録(有料:$99/year)する必要があり、その上で長大な手順が続く。
参考:ここが大変だよiPhone開発(4):ここが大変だよiPhone実機テスト+iPhone OS 3.0の新機能 - @IT

無料で実機に自作アプリをインストールするには、いわゆるJailBreakを行う以外に、手段が全く無いようである。

今回、特にアプリを配布するつもりが無く、試しに簡単なプログラムを動かしてみたい程度で、しかも時代遅れの機種に、1万円を払うのは厳しい。
このiPhoneは、特に使っておらず、筆者にとっては自作アプリを動かせなければ無用である。JailBreakすることによって再起不能になっても問題無い。
ただ、iPhoneのJailbreakingは法に触れるかどうかがグレーであり、それ故に実施するかどうか悩んだのだが、日本における判例が無いことと、著作権法第20条の2に、プログラムを動かす為のプログラムの改変は同一性保持権の侵害に当たらないとある(*2)ことと、Appleが異議を申し立てていた、アメリカの著作権局が発行したDMCAのJailbreakingに関する見直し条項が、その後の2012年10月22日の改正でも、合法的に入手したプログラムを動作させる為の「携帯無線電話」のプログラムの改変は禁止されない、と維持された(*3)ことから、自作アプリをiPhoneで動作させる目的に限ってのJailbreakingは、2015年10月28日までは違法性を問われる確率が極めて低いと判断し、実施に踏み切った。

以下は、JailBreakingが済んでいることと、iPhoneにsshがインストールされている前提の手順である。
  1. Xcodeの左側のツリー表示の一番上のプロジェクトのアイコンを選び、プロジェクト設定の画面にする。
  2. "PROJECT"と"TARGETS"がある列の、"TARGETS"の所のアイコンを選ぶ。
  3. "Build Settings"の"Architectures"を、"Standard (armv7)"から"armv6"に書き換える(*5)
    編集可能な列が2列あるが、右側の列を操作すると左側の列も連動するので、右側の列を操作すると良い。
  4. 同じく"Build Settings"の"Code Signing"の所を全て"Don't Code Sign"にする。
  5. 下の方にある"Validate Settings"を押して、修正させる。
  6. ツールバーにある"iPhone 5.1 Simulator"を"iOS Device"に変える。
  7. メニューバーから"Product"→"Build"する。(*7)
  8. ツリー表示のProductsの下のXXX.appを右クリックし、"Show in Finder"を選ぶ
  9. ターミナルを開き、
    codesign -fs "[適当な証明書の名前]" [パス]
    とする。[パス]の所は、8.で開いたFinderからdrag&dropする。
  10. iPhoneに転送する。
    1つの方法としては、ターミナルから
    scp -r [パス] root@[iPhoneのIPアドレス]:/Applications/
    とする(*4)
  11. iPhoneを"Respring"する。
    1つの方法としては、sshでiPhoneにログインし、
    killall -HUP SpringBoard
    とする。

●トラブルシューティング
正規の手順だと、iPhoneをPCにUSB接続していると、実機のアプリをデバッガで実行できるのだが、上記の手順だとそれができないので、クラッシュログとデバッグコンソールに頼ることになる(*6)

シミュレーター上では動作しても、実機ではハングアップすることは頻繁にある(*4)
アプリ起動するとハングアップして勝手に閉じてしまう場合、クラッシュログが残っているかどうかを確認する。

  1. iPhone3GをPCにUSB接続する。
  2. "Window"→"Organizer"を開く。
  3. "iPhone3G"の"Device Logs"を開く。
または、MacのiTunesでiPhoneを同期すると、~/Library/Logs/CrashReporter/の中にコピーされるようだが、筆者の経験上、同期してもなかなかコピーされないことがあったので、お勧めしない。

コードが思い通りに動かない場合は、printfデバッグを行う。
  1. コードに、
    NSLog(@"[フォーマット文字列]"[, 引数1, 引数2, ...]);
    を加える。[フォーマット文字列]には、"%d"など、Cのprintfと同じ書式が使えるようである。
  2. "Window"→"Organizer"を開く。
  3. "iPhone3G"の"Console"を開く。
  4. iPhoneでアプリを実行する。

●免責事項
本記事を参考にしたことにより生じたいかなる損害に関しても、筆者は一切の責任を負わないものとする。


(*1)^
Xcode 4.4.1以前に含まれるiPhone SDKをXcode 4.5にコピーすることにより、Xcode 4.5でもARMv6とARMv7のどちらでも動く実行ファイルを作成できたという情報もある(参考リンク:[1] [2] [3])が、筆者の手持ちのXcode 4.6.3では、Build時に

ld: file is universal (2 slices) but does not contain a(n) armv6 slice
となり、同じ方法では成功しなかった。
"Build Active Architecture Only"をYesにすると、ARMv6専用にはBuildできたが、ARMv7でも動くようにできないならあまり意味が無いし、筆者としてはARMv6で動けば十分なので、Xcode 4.4.1を使うことにした。

(*2)^
著作権法第20条より:
第二十条  著作者は、その著作物及びその題号の同一性を保持する権利を有し、その意に反してこれらの変更、切除その他の改変を受けないものとする。
2  前項の規定は、次の各号のいずれかに該当する改変については、適用しない。

三  特定の電子計算機においては利用し得ないプログラムの著作物を当該電子計算機において利用し得るようにするため、又はプログラムの著作物を電子計算機においてより効果的に利用し得るようにするために必要な改変
(*3)^
In "Statement of the Librarian of Congress Relating to Section 1201 Rulemakings" dated July 26, 2010:
This is the fourth time that I have made such a determination. Today I have designated six classes of works. Persons who circumvent access controls in order to engage in noninfringing uses of works in these six classes will not be subject to the statutory prohibition against circumvention.

(2) Computer programs that enable wireless telephone handsets to execute software applications, where circumvention is accomplished for the sole purpose of enabling interoperability of such applications, when they have been lawfully obtained, with computer programs on the telephone handset.

In "Exemption to Prohibition on Circumvention of Copyright Protection Systems for Access Control Technologies" dated October 22, 2012:

Upon the recommendation of the Register of Copyrights, the Librarian has determined that the following classes of works shall be exempt from the prohibition against circumvention of technological measures set forth in Section 1201(a)(1)(A):

B. Wireless telephone handsets – software interoperability

Computer programs that enable wireless telephone handsets to execute lawfully obtained software applications, where circumvention is accomplished for the sole purpose of enabling interoperability of such applications with computer programs on the telephone handset.


It provides that the prohibition shall not apply to persons who are users of a copyrighted work in a particular class of works if such persons are, or in the
succeeding three-year period are likely to be, adversely affected by virtue of the prohibition in 2 their ability to make noninfringing uses of such works,
2012/10/22の改正により、
・SIMロック解除は現在では違法
・Tablets(iPad含む)のJailbreakingは違法
・Jailbreakingは合法的に入手したプログラムを携帯電話(iPhone含む)上で動作させる目的に限って禁止対象から除外される(それ以外の目的では禁止になる)
ことがわかる。

解説記事
Jailbreaking now legal under DMCA for smartphones, but not tablets | Ars Technica

(*4)^
シミュレーターでは動いても実機でハングアップすることは結構頻繁に発生する。
例えば、"OpenGL Game"テンプレートを用いたアプリは実機でのみハングアップする("Build Settings"の"Base SDK"が"iOS 5.1"であればシミュレーターでは動いてしまう)し、以上の手順と筆者のiPhone3Gでは、ソースコードで%(剰余)演算子を使うと、

Symbol not found: ___modsi3
となり、ハングアップする。整数型の除算でも
Symbol not found: ___divsi3
となってハングアップすることがある。

そんな時、scpを繰り返すことになり、毎回パスワードを打ち込むのが面倒になったので、次のようなコマンドでscpを実行することにより、パスワードが自動的に送信されるようにした。

expect -c "set timeout 20; spawn scp -r [drag&drop] root@[IP address]:/Applications/; expect \"password: \"; sleep 1; send [password]\r; expect [part of prompt]"
最後の[part of prompt]の部分は、ターミナルのプロンプトの一部を入れる。

なお、%演算子や整数の除算でハングアップするのは、"Build Settings"の"Apple LLVM compiler 4.0 - Code Generation"の"Optimization Level"を"None [-O0]"以外にすると、libgccの___modsi3や___divsi3が使われなくなり、回避できた。

(*5)^
なお、"TARGET"の"Build Settings"において、"Deployment"の"iOS Deployment Target"が"iOS 5.1"なのを"iOS 4.2"等にするは、今回の手順では必須ではないようだ。

(*6)^
そういう意味でも、ちょっとした規模のアプリを開発するなら、正規の手順で実機にインストールする方が良い。(それなりの規模のアプリだと、正規の手順でないと、できあがっても配布できないことの方が遥かに大きな問題だと思うが)

(*7)^
デバッグビルドでなくリリースビルドする場合は、"Product"→"Build For"→"Archiving"とする。
でき上がった.appファイルは、デバッグビルドでできた.appファイルの近くの、Release-iphoneosというディレクトリーの中を探す。