見出し画像

足し算ゲームのiPhoneアプリを作ってみた話 1. SwiftUIに挑戦してみた動機とかなんとか

最初にお断りしますが、私はスマートフォンアプリ開発の専門家ではないです。ただし計算機の専門家ではありますし、膨大なデータの超高速処理についてのコンサルタント・ソリューションアーキテクトを本業としています。SwiftもUIKitもSwiftUIも趣味的に勉強しているだけ、とお考えください。息子くんに「勉強しているパパの姿」を見せてあげたかったというのもあります。あえてこの記事がプログラミング教育に関連するのだと考えるなら、どちらかというと子供向けの教育というよりも、プログラミングに興味のある大人向けの読み物と受け取って頂ければと思います。

iOSでのアプリ開発のことなんてすっかり忘れてる私です

実のところ私のiOS開発の知識は、Cocoa Touch(ここたっち、と、読みます。最近話題の厚生労働省のアレとは関係ありません)で会話していた時代で止まっています。そんなわけで、とりあえずやってみるか、と、UIKitを使ったプログラミングをしてみました。

画像1

ここまでは半日もかからずできました。Swift便利だけど癖があるなぁとか、やっぱりUIをWYSWYGで作れるのは楽だよな〜なんて思いながらコーディングしてました。いろいろ試行錯誤しながら進めているので、変数名とかめちゃくちゃです。

で、作ってみたものを息子くんに試してもらったんですよ。そしたら、繰り上がりの小さな1を入力しようとして、1ともう一つの数字を、タタッとすごい速さで入力しようとするのです。

画像2

くりあがりの1が書けない!やりなおし!

なるほど、繰り上がりの1は小さく書くものだから、素早く入力すれば小さく表示されるのでは?と思ったようです。もちろん、そんな高度な入力判定は実装していませんから、息子くんにはダメ出しをもらってしまいました。それなら、というわけで、ついでに話題のSwiftUIで作り直してみよう!と思ってしまったのです。

ああ、思わなければよかった←

嘘です。勉強した価値はありましたし、とっても楽しかったです。とはいえ、なかなか大事なポイントの情報にたどり着けなくて苦労しました。実際のところSwiftUIの説明動画とかを見ていると、「こ〜んなに簡単に素敵なUIが作れるんですよぉ」的な話が前面に出ていることが多くて、実際のアプリを作るときに必要な基礎知識がふんわり流されているように感じました。そのあたりのことをご説明できたらなぁと思います。

SwiftUIを見てJavaを思い出した私です

1995年にJavaが登場したとき、実はアルファバージョンから触らせてもらっていました。Javaの特徴の一つは良くも悪くもWrite Once. Run Anywhere.  だと思います。Javaはそのための工夫を重ねてきました。それはたとえばWindows, Mac, X11など様々な環境でも同じようにGUIが動作するようにするための工夫でした。
SwiftUIも同じチャレンジをしているのだと思います。Javaとは異なりプラットフォームを自社製品に制限できますし、自社ハードの良さを最大限に活かすようなソフトウェアのデザインができます。しかし、やはり画面デザインも抽象化の度合いを高くしていくというのは、依然としてチャレンジングな領域なのだと思います。

SwiftUIでは画面構成をプログラムコードによって記述します

しかも、宣言的っぽく手続きを書きます。これが完全に宣言型ではないところにわかりにくさがあります。

画像3

これはXcodeでSwiftUIのアプリを作成するときに提供されるテンプレートです。Hello, World! というテキストを表示しているだけのアプリです。Apple社的な説明としては、このbodyという変数の定義を宣言的に記述することで、UIのデザインができるということになっています。

struct ContentView: View {
   var body: some View {
       Text("Hello, world!")
           .padding()
   }
}

varは変数を宣言するための命令です。bodyはその変数名で、Viewプロトコルを実装する何かを返しますよということを言っています。プロトコルと言うのは、Java風に言うとInterfaceです・・・ってこれじゃ不親切ですので、まじめに説明してみようと思います。このお話をするために、「オブジェクト指向」のお話を少しさせていただきたいと思います。

なぜオブジェクト指向で作るのか?

そんな名前の本を見たことがあるような気がします。残念ながら未読です。

GUIアプリケーションを作成するのにオブジェクト指向プログラミングはよくマッチします。オブジェクト指向プログラミングでは、プログラムの部品同士の使い方・使われ方の決め事をプログラムコードの形で記述することができるのです。このお話をするために、GUIアプリケーションからちょっと離れて、よくあるHello, World!のプログラムコードをみてみたいと思います。

#include<stdio.h>
int main( int argc, char** argv){
  printf("Hello, World!\n");
  return 0;
}

たとえばこれはUNIX上のC言語のプログラミングの例です。UNIXではmainと名前をつけられた関数からプログラムコードが始まることになっています。これはシステムと、プログラマが書くコードの間に

「まず最初に、mainって名前の関数を呼ぶよ!」
「その関数に与えられる引数は、こんな風に決めてるよ!」

というような約束があることを前提としているのです。

画像4

この約束をGUIプログラミングに拡大してみたいと思います。

そもそもGUIプログラミングって大変なんです

コマンドラインのプログラムでは、エントリポイント(C言語ならmain()関数)から処理が始まって、基本的にはプログラマがその処理の流れを作っていきます。これに対してGUIアプリケーションはユーザからの入力をウィンドウシステムからのメッセージとして受け取り→そのメッセージに対応する処理を行い→ウィンドウシステムからのメッセージを受け取りに戻るというループを繰り返します。

画像6

たとえばボタンの上にマウスが乗っかったときに、マウスカーソルの形を👆に変えるとか、ボタンの上でマウスのボタンが押されたときにボタンを凹ませるグラフィックを描画するとか、ボタンの上でマウスのボタンが離されたときに、規定の計算を行うとか・・・きめ細かく、ウィンドウシステムからのメッセージに対する応答をプログラムで記述してあげる必要があります。それは例えば、ポインタのいちを調べて、その座標にある画面の要素を調べて、それがOKボタンだったら〜して、キャンセルボタンだったら〜して・・・という具合に非常に面倒なものなのです。

このような形でのソフトウェアの記述は、プログラマに大きな負担を強いるものです。ウィンドウシステムからのメッセージについての説明は開発用のリファレンスマニュアルに記載されており、プログラマはウィンドウシステムの挙動の仕様について詳しくなる必要があります。そこで、ウィンドウシステムとのやりとりの特に面倒な部分を、アプリケーションフレームワークというソフトウェア部品にまかせてしまおうと思います。

画像7

このような構成にすることで、イベント・ドリブンのプログラミングが容易になります。「こんなイベントが起きたら、こんな名前のこんな引数で機能呼び出しをしますよ〜」というような約束をアプリケーションフレームワークが定義します。プログラマはその約束に従ってプログラムをすることになります。

プログラマがフレームワークの約束に沿ったコーディングをしていることを保証するために、オブジェクト指向プログラミングが良くマッチします。Swiftでのprotocolは、オブジェクトの形を定義します。振る舞いの実際の中身は定義しません。たとえばViewというprotocolはbodyという変数を持つということだけを定義しています。プログラマはViewを実装すると決めたなら、そのViewの形にあうように自身のオブジェクトを実装する義務を負います。この義務を怠るとコンパイル時にエラーになります。

プログラムモジュール間の「約束」を規定する

オブジェクト指向言語というのは、プログラムモジュール(部品)間の「約束」を、プログラミング言語の形で記述することを可能にするものです。フレームワークの開発者はフレームワークを適切に使ってもらうための「約束」を記述します。アプリケーションの開発者は、アプリケーションフレームワークが何をしているのか、その詳細をつぶさに知る必要はありません。ただ、アプリケーションフレームワークが要求する「約束」をきちんと理解し、その「約束」に従ったコーディングをすることで、自分の求めるアプリケーションを迅速に開発することができます。「約束」に従っていない場合には、コンパイラがエラーや警告をだしてプログラマにその問題点を教えてくれるのです。

SwiftUIのHello, World!に戻ります

struct ContentView: View {
   var body: some View {
       Text("Hello, world!")
           .padding()
   }
}

SwiftUIのHello, World!は、Viewというprotocol(お約束)に従ったオブジェクト定義をしています。View protocolはbodyという変数が実体を持つことを要求しており、このプログラムコードでは、computed propertyの形で bodyの実体を定義しています。

GUIの画面を作成し返却する

オブジェクトが持つ値の返却を求められたときに、必要な計算処理を行い、その結果を返却するものをcomputed propertyと呼びます。Xcodeが提供するサンプルコードは省略記法で書かれているので、なんとなく不思議な雰囲気に見えますが、冗長に書けばこんな感じです。

struct ContentView: View {
   var body: some View {
       get{
           var てきすと = Text("Hello, world!")
           var 返却するGUI部品 = てきすと.padding()
           return 返却するGUI部品
       }
   }
}

SwiftUIのアプリケーションフレームワークは、この画面描画が必要であると判断したときに、bodyの中身を要求します。このときgetで定義された手続き処理が実行され、View protocolを実装するなにがしかのGUIオブジェクトを返却するのです。

長くなったので、一旦ここで終わりにします。次回はSwiftUIによる画面レイアウトに四苦八苦したお話をしたいと思います←

以下、投げ銭です。本文はありません。

ここから先は

0字

¥ 100

この記事が気に入ったらサポートをしてみませんか?