Swift実践入門 ~ 今からはじめるiOSアプリ開発! 基本文法を押さえて、簡単な電卓を作ってみよう
初めてSwiftを触るエンジニアの方向けに、Swiftの基本から、実際に動くiOSアプリ開発までを解説します。実際に手を動かして覚える内容となっているので、Xcodeを操作しながら読み進んでください。
はじめまして、山野貴史と申します。Webアプリケーション開発やiOSアプリの制作等を行っています。
好物はRubyと、Rails。RailsAmpという、AMP(Accelerated Mobile Pages) に対応するRuby on Railsのプラグインgemを作りました。エディタはVim派です。Swiftも大好きです。
本稿では、初めてSwiftを触るエンジニアの方向けに、Swiftの基本から、実際に動くiOSアプリ開発までを解説します。Swiftを対話的に動作させて慣れた後、簡単な電卓アプリを制作してみます。
SwiftのサンプルコードやXcodeのスクリーンショットを豊富に掲載し、手を動かして覚える内容となっています。読者の皆様自身がXcodeの操作を通じて、実際の動作を確認しながら読み進んでいただけると嬉しく思います!
- Swiftの特徴
- インタラクティブ環境でSwiftを触ってみよう
- Vimmerならプラグイン「XVim」をインストールしよう
- Swiftの基本文法(1)変数、配列、ディクショナリの定義、制御構造
- Swiftの基本文法(2)関数、クロージャ、クラス定義・オブジェクト
- Swiftの基本文法(3)Optional型
- Swiftの基本文法(4)Optional型をアンラップする4つの方法
- 電卓アプリを作ってみよう!
- おわりに
Swiftの特徴
Swiftは、Apple社のiOSやmacOSのアプリケーション開発に用いられるプログラミング言語です。本稿の執筆時点で、最新バージョンはSwift 3.1です。
Swiftの主な特徴は以下の2点です。
- 静的型付け
- プログラム実行前の段階(コンパイル時)に、変数や定数の型情報を決定する。実行時エラーを事前に発見できる
- 型推論
- 変数や型を宣言しなくても、変数の型が自動的に決定される。記述が簡潔になる
また、Swiftはオブジェクト指向や関数型言語の特徴を持っています。WebエンジニアでRubyやPython等の経験があれば、Swiftの習得も早いはずです。オブジェクト指向やクロージャの概念が、Swift開発でも役に立ちます!
型付け | 処理系 | IDE/エディタ | 主な開発分野 | |
---|---|---|---|---|
Swift | 静的 | コンパイラ | Xcode | iOS、macOS、tvOS |
Java | 静的 | コンパイラ | Eclipse、NetBeans、IntelliJ IDEA、Android Studio等 | Webサーバーサイド、Android、各種OS・組み込みシステム等幅広い |
Ruby | 動的 | インタプリタ(JRuby等の処理系ではコンパイラ可) | Vim、Emacs、Atom、RubyMine等 | Webサーバーサイド(Ruby on Rails) |
Python | 動的 | インタプリタ | Vim、Emacs、Atom、PyCharm等 | Webサーバーサイド、機械学習、統計分析等 |
ひとつ注意点ですが、Swiftは書き方の自由度が比較的高いので、チーム開発をする場合は、あらかじめコーディング規約を共有しておいた方が良いでしょう。
Swiftでの命名や慣例のほか、さまざまな指針がSwift.orgにある「API Design Guidelines」にまとめられています。興味のある方は目を通してみてください。
Swift.org - API Design Guidelines
インタラクティブ環境でSwiftを触ってみよう
早速ですが、Swiftを触って動作させてみましょう。Swiftはコンパイラ言語でありながら、インタラクティブ(対話的)に実行しながら動作を確認することができます。Swiftに慣れるには、このインタラクティブ環境でいろいろと動作させてみるのが手っ取り早いです。
インタラクティブにSwiftを実行するには、ターミナル(コマンドライン)のREPL(レプル)を使う方法と、XcodeのPlaygroundを使う方法の2つがあります。
動作確認の環境
本記事中で、サンプルコードの動作確認を行った環境のバージョン情報です。
- MacOS Sierra 10.12.4
- Swift 3.1
- Xcode 8.3.2
- CocoaPods 1.2.1
- Expression 0.5.0(CocoaPodsライブラリ)
- iOSシミュレーター iPhone 7&iOS 10.3
Swift、Xcodeのバージョンは、以下コマンドで確認できます。
$ swift --version Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42) Target: x86_64-apple-macosx10.9 $ xcodebuild -version Xcode 8.3.2 Build version 8E2002
それでは、Swiftの実践へと移ります!
実行方法1. ターミナルのREPL
REPLとは、Read-Eval-Print-Loop(対話型実行環境)のことです。Swiftは、ターミナルからREPLを起動できます。
$ swift Welcome to Apple Swift version 3.1 ... 1> print("hello world") hello world 2> 1 + 1 $R0: Int = 2 3> :exit $
ターミナルでswift
と入力すると、矢印が表示され、入力待ちの状態となります。Swiftのコードを書いて、return
キーを押します。Swiftコードが実行され、コードが評価された結果が次の行に出力されます。REPLは、:exit
で終了できます。
実行方法2. XcodeのPlayground
Xcodeは、エディタのほか、デバッガやGUIデザイナ等の機能を備えた、SwiftのIDE(統合開発環境)です。XcodeのPlaygroundを使うと、Swiftを対話的に実行できます。 通常、SwiftでのiOSアプリ開発にはXcodeを使い、ビルドして実行します。
Xcodeは、Mac App Storeからインストールしましょう。
Xcodeを起動して、「Get started with a playground」を選択します。
または、Xcodeのメニューから[File]→[New]→[Playground...]を選択します。
ターミナルのREPLと同様に、PlaygroundでもSwiftを対話的に実行できます。Playground上で実行したコードが保存できますので、対話的に書いたコードを後で見直したい場合にも便利です。
Vimmerならプラグイン「XVim」をインストールしよう
唐突ですが、読者の皆さんはVim好きですか? 私はVimが大好きです! RubyやRails等でコードを書くときはもちろん、この記事執筆にも、いつでもどこでもVimを使っています。
Vim使いの方であれば、当然「XcodeもVimのキーバインドで使いたい」って考えますよね。そんなVimmer達の願いをかなえてくれるのが、XVimというXcodeの神プラグインです。Vim好きな方は、iOS開発に取り組む前に、ぜひXVimをインストールしてみてください。
Xcode 8の場合は、XVimインストールの前に、あらかじめXVimプラグイン用の証明書を作成しておく必要があります。証明書の作成方法は次のドキュメントをご参照ください。
Install XVim for Xcode 8 - XVimProject/XVim
証明書を作成した後は、以下の手順でXVimをインストールします。
$ cd ~/Library/Application\ Support/Xcode $ mkdir plugins $ cd plugins $ git clone https://github.com/XVimProject/XVim $ cd XVim $ make ... ** BUILD SUCCEEDED **
XVimは、Playgroundでも動作します。Vimmerな方はあらかじめインストールしておくと、Playground上でコードを書いて試すのが断然速くなります。
Emacsのキーバインド
XcodeをEmacsのキーバインドで使いたい場合、本稿執筆現在ではKarabiner正式版の最新バージョンがmacOS Sierraに対応していないため、代替としてHammerspoonを使うと良いようです。
Swiftの基本文法(1)変数、配列、ディクショナリの定義、制御構造
アプリ制作に入る前に、Swiftの基本的な文法を確認していきます。
Swiftの基本的な文法は、CやC++、Objective-C、Java、Ruby、Python、JavaScript等のプログラミング言語を一つでも知っている方であれば、比較的なじみやすいでしょう。
Swiftには、Bool
、Int
、Float
、Double
、Character
、String
、Array
、Dictionary
等の基本的な型に加えて、nil
(他言語ではnull
とも呼ばれる)を許容するOptional
という型が存在します。Swiftの特徴であるOptional
型や、それを扱うOptional Bindingと呼ばれる機能については、初見ですとちょっと難しいので後述します。
本稿のサンプルコードは、先述したターミナルのREPLか、XcodeのPlaygroundで試しながら読み進めると理解しやすいかと思います。
変数の定義
まずは変数の定義です。変数の定義にはlet
やvar
を使います。
// letでイミュータブルな変数(1行コメント) let immutableStr = "hello" immutableStr += " world" // => error /* * varでミュータブルな変数 * (複数行コメント) */ var mutableStr = "hello" mutableStr += " world" print(mutableStr) // => hello world
※イミュータブル……作成後に状態を変えられないオブジェクトのこと。対義語はミュータブル
上の例では、型推論により、変数の型はString
型となります。明示的に型を指定するには、以下のように書きます。
var mutableStr: String = "hello"
Swiftでは 暗黙の型変換は行われません。よって、型を変換したい場合は、明示的に記述する必要があります。
let label = "This year is " let year = 2017 let thisYear = label + String(2017) print(thisYear) // => This year is 2017 // 文字列内での変数展開 let nextYear = "Next year is \(year + 1)" print(nextYear) // => Next year is 2018
配列の定義
配列(Array
型)は次のように定義します。
var itemsArray = ["foo", "bar", "baz"] // 型を明示 var itemsArray: [String] = ["foo", "bar", "baz"]
ディクショナリの定義
ディクショナリ(Dictionary
型)は、["キー": "値"]
のように記します。
var itemsDictionary = [ "foo": "FOO", "bar": "BAR", "baz": "BAZ" ] // 型を明示 var itemsDictionary: [String: String] = [ "foo": "FOO", "bar": "BAR", "baz": "BAZ" ]
制御構造
for
ループや、if
~else
による制御は次のように記述します。
let list = [3, 7, 9, 12, 8, 5] for number in list { if number % 2 == 0 { print("number \(number) is even") } else { print("number \(number) is odd") } }
while
ループです。
var number = 1 while number < 10 { print(number) number += 1 }
switch
~case
です。
let age = 17 switch age { case 0...6: print("kindergarden kid") case 7...12: print("primary school student") case 13...15: print("junior high school student") case 16...18: print("high school student") case 19...22: print("college student") default: print("business person") } // => high school student
Swiftでは、RubyのようなRange
オブジェクトを使うことができ、さらにswitch
~case
では、数値がRange
オブジェクトの範囲に入るかどうかを良い感じに判定してくれます。
ここまではC言語由来のプログラミング言語の文法に似ていますので、分かりやすいです。
Swiftの基本文法(2)関数、クロージャ、クラス定義・オブジェクト
関数(メソッド)
関数(メソッド)定義の書き方です。
func greet(expression: String, person: String) -> String { return "\(expression) \(person)." } greet(expression: "Hello", person: "Mike") // => "Hello Mike."
以下の書き方が、関数定義の基本になります。
func 関数名(引数1: 引数1の型, 引数2: 引数2の型, ...) -> 返り値の型 { }
関数を呼び出すときは、名前付き引数として(引数ラベル: 引数の値...)
のように渡します。
関数定義で引数ラベルを明示する場合は、以下のように書きます。アンダースコアを使うと、関数呼出し時に名前付き引数のラベルを省略できます。
func greet(_ expression: String, to person: String) -> String { return "\(expression) \(person)." } greet("Hello", to: "Mike") // => "Hello Mike."
なお、Swiftでは関数のシグネチャの書き方が多種多様です。見慣れない書き方があった場合は、その都度チェックしてみてください。
クロージャ
続いて、Swiftでのクロージャの例を見てみましょう。JavaScript等で同様の例をご存知の方も多いのではないでしょうか。
func incrementer() -> ( () -> Int ) { var count = 0 func increment() -> Int { count += 1 return count } return increment } var counter = incrementer() counter() // => 1 counter() // => 2 counter() // => 3
return
される関数を無名関数にしてみます。
func incrementerWithAnonymousFunc() -> ( () -> Int ) { var count = 0 return { () -> Int in count += 1 return count } } var counter2 = incrementerWithAnonymousFunc() counter2() // => 1 counter2() // => 2 counter2() // => 3
関数は、引数として関数を受け取ることができます。
func numbersMap(list: [Int], condition: (Int) -> Int) -> [Int] { var numbers: [Int] = [] for item in list { numbers.append( condition(item) ) } return numbers } var items: [Int] = [1, 2, 3, 4, 5] numbersMap(list: items, condition: { (number: Int) -> Int in number * 2 }) // => [2, 4, 6, 8, 10]
このクロージャ機能により、Swiftではmap
、filter
、reduce
等のメソッドに、引数として無名関数のブロックコードを渡して処理することができます。
var numbers = [3, 7, 9, 12, 8, 5] // 配列の要素をすべて2倍にする numbers.map({ (number: Int) -> Int in return number * 2 }) // => [6, 14, 18, 24, 16, 10] // 奇数のみを抽出する numbers.filter({ (number: Int) -> Bool in return number % 2 == 1 }) // => [3, 7, 9, 5] // すべての合計を計算する numbers.reduce(0, { (total: Int, number: Int) -> Int in return total + number }) // => 44
さらに、map
、filter
、reduce
の引数でブロックコードを渡す場合は、型指定を省略した書き方が可能です。
numbers.map{ number in number * 2 } // => [6, 14, 18, 24, 16, 10] numbers.filter{ number in number % 2 == 1 } // => [3, 7, 9, 5] numbers.reduce(0){ total, number in total + number } // => 44
まるでRubyのコードのようですね。Rubyのブロック引数の||
がin
に変わっただけで、とてもよく似ています。SwiftがRubyに影響を受けたことを伺わせるシンタックスです。
クラス定義とオブジェクト
次は、クラス定義とオブジェクト作成のコード例です。他言語と似たような書き方ですので、分かりやすいかと思います。
class MyApp { // Shapeクラスの定義 class Shape { // nameプロパティ(インスタンス変数) var name: String // イニシャライザ(コンストラクタ) init(name: String) { self.name = name } } // 四角形: RectangleがShapeを継承 class Rectangle: Shape { var width: Double var height: Double init(name: String, width: Double, height: Double) { self.width = width self.height = height // 親クラスのイニシャライザ呼び出し super.init(name: name) } func area() -> Double { return width * height } } // 三角形: TriangleがShapeを継承 class Triangle: Shape { var bottom: Double var height: Double init(name: String, bottom: Double, height: Double) { self.bottom = bottom self.height = height super.init(name: name) } func area() -> Double { return bottom * height / 2.0 } } } // 正方形を作成 var square = MyApp.Rectangle(name: "My Square", width: 7.5, height: 7.5) square.name // => "My Square" square.area() // => 56.25 // 三角形を作成 var triangle = MyApp.Triangle(name: "My Triangle", bottom: 10, height: 8) triangle.name // => "My Triangle" triangle.area() // => 40
そのほか、Swiftでは、enum
(列挙型)やstruct
(構造体)、protocol
(インターフェース)、extension
(クラス拡張、モンキーパッチ)、generics
(ジェネリクス)機能も利用できます。ここで詳しくは触れませんが、興味がある方はドキュメント等を参照してください。
Swiftの基本文法(3)Optional型
Swiftの言語仕様の特徴として、nil
を安全に扱うためにOptional
という型が存在します。
そのため、Swiftでnil
を許容する変数は、Optional
型として定義する必要があります。変数をOptional
型で定義しない限り、変数にnil
を代入することはできません。非Optional
型の変数、たとえば通常のInt
型やString
型の変数には、nil
を代入できません。
Optional
型を使うことで、変数やメソッドの戻り値がnil
になり得ることを、プログラマが明示できます。不適切にnil
の可能性のある変数にアクセスするコードを書いていた場合、コンパイル時に警告が出て、nil
が返ることによる予期せぬ実行時エラーの発生を防ぐ働きをしてくれるわけです。
Ruby/Railsでは、次の例のようにメソッド呼び出しやメソッドチェインの途中でnil
が返ってしまい、NoMethodError
の実行時エラーが発生した経験はないでしょうか?
user.name # userがnilの場合NoMethodErrorになる user.name if user.present? # ifでチェックしuserが存在する場合のみ実行 user&.name # safe navigation operator -> userがnilの場合nilを返す user.try(:name) # RailsのObject#try -> userがnilの場合nilを返す
Ruby/Railsの場合、変数のnil
チェックやnil
アクセスに対する適切な処理は、プログラマのコーディングに委ねられています。上記の例のように、if
でnil
チェックしたり、try
メソッドやsafe navigation operator(通称ぼっち演算子&.
)を使うといった処理を、プログラマが責任を持って行う必要があります。
一方、Swiftでは、nil
になり得る変数を使う場合のために、言語仕様自体にnil
を安全に扱えるようにするOptional
型という仕組みが組み込まれています。
また、SwiftでOptional
型を扱う際には、さまざまな文脈で?
(クエスチョンマーク)や!
(エクスクラメーションマーク)が登場します。Rubyを使われる方は、Rubyでは真偽値を返すメソッドに?
が付いたり、破壊的メソッドに!
が付くことをご存知かと思いますが、Swiftではまったく概念が異なりますので、その点は注意してください。
Optional
型の実際のコードを見ていきましょう。
Optional型の変数を定義
// Optional Int var optionalInt: Int? = 5 // 通常のInt var int: Int = 5 // Optional String var optionalStr: String? = "hello" // 通常のString var str: String = "hello" // Optional Stringの変数にnilを代入 optionalStr = nil // 通常のStringにはnilを代入できない str = nil // => error: nil cannot be assigned to type 'String'
Int?
はnil
またはInt
型の値を代入できる型、String?
はnil
またはString
型の値を代入できる型です。Int?
とInt
はまったく別の型であり、同様にString?
とString
もまったく別の型です。
このT?
という書き方は、Optional<T>
のシンタックスシュガーですので、以下のようにも書けます。
var optionalInt: Optional<Int> = 5 var optionalStr: Optional<String> = "hello"
Swiftの基本文法(4)Optional型をアンラップする4つの方法
Swiftでは、Optional
型の変数を ラップ(wrap)されていると表現します。nil
を許容する型でラップされている(包まれている)というイメージでしょうか。
一方、値を扱えるようにするための処理を、アンラップ(unwrap)すると表現します。
アンラップせずに、Optional
型の変数に対してメソッドを呼び出そうとすると、変数がアンラップされていないよ! とエラーが発生します。
var optionalStr: String? = "hello" optionalStr.uppercased() // => error: value of optional type 'String?' not unwrapped
Optional
型の変数をアンラップ処理するには、4つの方法があります。
- Optional Binding
- Optional Chaining
- Forced Unwrapping
- ImplicitlyUnwrappedOptional型
方法1. Optional Binding
Optional Bindingは、Optional
型の変数を安全に扱うための代表的な方法です。
if
やguard
を使って、Optional
型の変数がnil
でないことを確認してアンラップを行います。guard
は関数の中でしか使えませんので、以下の2つのコード例はfunc
の中で書いています。
まずは、if
を使ったOptional Bindingのコード例です。
func hello() -> String { let optionalStr: String? = "hello" if let unwrappedStr = optionalStr { return unwrappedStr.uppercased() } return "" } print( hello() ) // => HELLO
次に、guard
を使ったOptional Bindingのコード例です。
func helloWithGuard() -> String { let optionalStr: String? = "hello" guard let unwrappedStr = optionalStr else { return "" } return unwrappedStr.uppercased() } print( helloWithGuard() ) // => HELLO
guard
は、いわゆるガード節の働きをします。Optional
型の変数がnil
だった場合の処理を、else
内に書きます。Optional
型の変数がアンラップできた場合は、以降の行でアンラップ済みの変数を使えます。
これらのOptional Bindingにより、Optional
型の(nil
になり得る)変数を安全に扱えます。
方法2. Optional Chaining
Optional Chainingは、アンラップした変数の型のメソッドを呼び出したい場合に使う方法です。
Optinal
型の変数の後ろに?.
を付けて、メソッド呼び出しを書きます。変数がnil
でない場合はメソッド呼び出しの結果を返し、nil
の場合はnil
を返します。Railsのtry
や、Rubyのsafe navigation operator(&.
)と似た働きをします。
Optional
型の変数を定義する?
とは、意味合いがまったく違う点に注意です。
var optionalStr: String? optionalStr?.uppercased() // => nil optionalStr = "hello" optionalStr?.uppercased() // => "HELLO"
Optional Chainingを使うと、以下のように?.
でつないで安全にメソッドチェインを書くことができます。
if let userName = user?.profile?.name { print(userName) }
user
またはprofile
またはname
がnil
を返した場合は、user?.profile?.name
の結果がnil
を返しますので、Optional Bindingのif
ブロック内のprint(userName)
は実行されません。
方法3. Forced Unwrapping
Forced Unwrappingとは、文字通り強制的にアンラップを行う方法です。T?
型の変数の後ろに!
を付けることで変数を強制的にアンラップし、T
型として扱えます。
var optionalStr: String? = "hello" optionalStr!.uppercased() // => HELLO
ただし、このForced Unwrappingを使う方法は、危険であることを認識しなければなりません。Optional
型の変数がnil
だった場合にForced Unwrappingしてしまうと、ランタイムエラーが起こる(アプリの場合だったら落ちる)可能性があります。変数の中身が絶対にnil
ではない場合のみに、Forced Unwrappingを使うようにします。
Swiftでは、!
は注意すべきマークと意識してしておくと良いでしょう。!
を使う場合は、絶対に処理が失敗しないようにする必要があります。
方法4. ImplicitlyUnwrappedOptional型
nil
が代入可能な変数を定義する型として、ImplicitlyUnwrappedOptional
型があります。
var implicitlyStr: String! // nil implicitlyStr = "hello" // "hello" implicitlyStr.uppercased() // => "HELLO"
ImplicitlyUnwrappedOptional
型の変数は、メソッド呼び出しの際に、通常の型と同じような呼び出し方で書けます。
ただし、Forced Unwrappingと同様に、ImplicitlyUnwrappedOptional
型の変数がnil
の場合、実行時エラーになる可能性がある点に注意です。絶対に変数がnil
になり得ない場合にのみ、ImplicitlyUnwrappedOptional
型を使うようにします。
Optional
型の変数を扱う際には、上述した4つの方法を用いて適切に処理するようにします。
ここまでで、Swiftの基本文法は終了です。すべてを網羅しているわけではありませんので、詳細は公式ドキュメントや書籍をご確認ください。
電卓アプリを作ってみよう!
Swiftの特徴から基本文法までを、駆け足で見てきました。最後のまとめとして、簡単な電卓アプリを作ってみましょう。ここまでに紹介したSwift文法の知識があり、もう少しだけXcodeの操作法を学べば、実際に動くアプリを作ることができます。
制作する電卓アプリのUIと仕様
ここでは、次のようなアプリを制作します。
- 入力された計算式を表示する
- 答えを表示する
- 数字、演算子(+、ー、×、÷)、小数点、カッコを入力でき、それらを使った四則演算ができるようにする
- =(イコール)を押したら、計算式を評価して答えを表示
- C(クリア)ボタンを押したら、計算式と答えをクリアする
- 入力された計算式が不当な場合、「式を正しく入力してください」と表示する
この電卓アプリのUIと仕様を考えたときの手書きのノートです(字が汚くてすみません)。こんな感じでざっと紙に書いてみると考えがまとまりやすいです。もちろんソフトウェアのツールを用いてもかまいません。
仕様を考えた後は、作業手順用のToDoリストを書き出すと良いでしょう。
作業手順(ToDoリスト)
- 必要なライブラリをCocoaPodsでインストール(今回はExpressionというライブラリを使います)
- View Controllerにラベル(UILabel)を配置( 式用、=用、答え用 )
- View Controllerにボタン(UIButton)を配置( 0123456789.÷x-+=()C )
- ラベルのOutlet接続
- ボタンのAction接続
- ボタンが押されたら式用のラベルに式の文字列を表示・更新
- =ボタンが押されたら答え用のラベルに答えを表示
- Cボタンが押されたら式と答えをクリア
それでは電卓アプリを作っていきます!
1. Xcodeで新規プロジェクトを作成
Xcodeを起動して「Create a new Xcode project」または、メニューから[File]→[New]→[Project]を選択し、新規プロジェクトを作成します。
次の画面で、[iOS]の[Single View Application]を選択して[Next]で進みます。
次の画面で、以下のように入力して[Next]で進みます。[Organization Name]は任意の名前でかまいません。
[Organization Identifier]には、持っているドメイン等を入力します。これはアプリの識別IDの一部となるため重要で、必須入力です。通常は、所有しているドメイン名を逆順にして入力します。
次に、プロジェクトを保存する任意のフォルダを選択します。[Create Git repository on]のチェックを外して[Create]します。
ここで、Xcodeをいったん終了させます。
コンソールで.gitignore
を作成後、Git管理下にします。ここでは、プロジェクトを保存したフォルダをCalculatorApp
とします。
$ cd /path/to/XcodeProject/CalculatorApp $ vi .gitignore $ git init $ git add . $ git commit -m "initial commit"
SwiftとXcodeで開発するときの.gitignore
は、次のドキュメントが参考になります。
.gitignore for Swift / originally based on gibo swift and added some other settings.
以降は、作業を進めながら適宜git
コマンドでコミットしてください。
2. Expressionをインストール
続いて、Swiftのライブラリ「Expression」をインストールします。ここでは、電卓に入力された文字列をSwiftコードとして評価するために使います。
ライブラリの管理にはCocoaPods.orgを利用します。CocoaPodsは、RubyでいうBundlerのようなツールです。使用するライブラリをPodfile
(Gemfile
に相当)に書いて、インストールします。
CocoaPodsのインストール手順は、以下の通りです。pod setup
に少々時間がかかります。
$ sudo gem install cocoapods
$ pod setup
$ pod --version
1.2.1
プロジェクトを保存したフォルダに、Podfileを作成します。
$ cd /path/to/XcodeProject/CalculatorApp
$ pod init
Podfile
を次のように編集します。
platform :ios, '9.0' target 'CalculatorApp' do use_frameworks! pod 'Expression' end
ライブラリをインストールします。
$ pod install
CalculatorApp.xcworkspace
が作成されるので、これをXcodeプロジェクトとして開きます。
$ open CalculatorApp.xcworkspace
3. View Controllerにラベルとボタンを配置
Storyboardで、部品(オブジェクト)を配置していきます。Storyboardとは、アプリケーションのラベル(UILabel)やボタン(UIButton)等の部品を、GUIによる操作で配置できるXcodeの機能のことです。
プロジェクトを開いたら、Main.storyboardを選択します。
右下のObject Libraryに「UILabel」と入力して、UILabelを選択し、View Controller上にドラッグ&ドロップします。グリッド線が表示されるので、ラベルの幅を調整します。
右端にあるAttributes inspectorで、[Label]の[Text]を編集すると、ラベル名を変更できます。
同様の手順で、UILabelを用いて、=(イコール)用のラベル、答え用のラベルを配置します。
続いて、数字や演算子等のボタンを配置します。右下のObject Libraryで「UIButton」と入力し、UIButtonを選択し、View Controller上にドラッグ&ドロップします。ドラッグ&ドロップを繰り返し、グリッド線を調整して、UIButtonを次のように配置します。
Attributes inspectorの[Title]に、表示する文字を入力します。数字やカッコ、小数点、演算子は半角文字(ASCII)で入力し、そのままSwiftの計算式として評価できるようにします。また[Background]で[Ligth Gray]を選択して、ボタンを灰色にします。
ただし、プログラミングでは乗算に*
、除算には/
を使いますが、ボタンの表示ではユーザーが使いやすいように、通常の算術演算子である[×](乗算記号)と[÷](除算記号)を表示させましょう。これはプログラム中で置換します。
部品の配置がひと通り終わりました。ここでいったんビルドして、iOSシミュレーターを確認します。上部メニューで動作を確認したい機種を選択し、▶ボタンを押してビルドして、シミュレーターを起動します。今回はiPhone 7&iOS 10.3の環境でビルドしました。
しばらく待つと、以下のようにシミュレーター上に電卓の画面が表示されるはずです。数字や演算子のボタンを押すと、ボタン(UIButton)が押されたアニメーションが動きます。まだ計算を行うためのコードは実装していませんので、式や答えは表示されません。
4. ラベルのOutlet接続
Main.storyboard上に配置したラベルやボタンを、ソースコードから扱えるようにOutletやActionの接続を行います。
Main.storyboardでView Controllerを選択して、Assistant editorを開きます。Assistant editorは右上の○が2つ重なったアイコン(赤枠)です。
次に、Storyboardの「式用のラベル」を選択し、Controlキーを押しながら、Assistant editorで開いたView Controllerのソースコード上(画像の位置)にドラッグ&ドロップします。
[Name]にformulaLabel
と入力し、ほかは次の画像の通りに選択して[Connect]をクリックします。
この操作で、View Controllerのソースコードに、以下の行が追加されます。
@IBOutlet weak var formulaLabel: UILabel!
同様の操作を「答え用のラベル」にも繰り返すと、ラベル(UILabel)のOutlet接続が完了します。
Outlet接続とは、Storyboard上のオブジェクト(部品)を、ソースコード上の変数として使えるようにする仕組みです。
ここまでの作業で、View Controllerのソースコード、およびConnections inspectorのOutletsは、以下の画像のようになりました。
5. ボタンのAction接続
今度は、ボタン(UIButton)のAction接続の作成です。
数字の0
のボタンをControlを押しながらAssistant editorにドラッグ&ドロップした後、次の画像のように[Connection]はAction
、[Name]はinputFormula
、[Type]にはUIButton
と入力して、[Connect]します。
この操作で、以下の行が追加されます。このメソッドの中に、ボタンが押されたときに実行する処理を実装していくという仕組みです。
@IBAction func inputFormula(_ sender: UIButton) { }
同様の手順を繰り返して、[C]ボタンと[=]ボタンを除くすべてのボタンで、Action接続を作成します。[Name]は、すべて同じinputFormula
とします。
このため、Action接続を作成するたびに、ソースコード上に同名のinputFormula
アクションが追加されますが、1つだけ残して後は削除しましょう。[C]と[=]以外のボタンが押されたときは、すべてこのinputFormula
メソッドで処理を行います。
続いて[C]と[=]についてもAction接続を作成しておきます。[C]ボタンはclearCalculation
、[=]ボタンはcalculateAnswer
という名前にします。
ここまでで、View Controllerのソースコードには、inputFormula
、calculateAnswer
、clearCalculation
メソッドが追加されています。Connections inspectorで、[Received Actions]が以下の画像のようになっていれば、OKです。Storyboard上の部品と、ソースコード上の変数やメソッドが接続できました。
ここから先は、ソースコード上で実装を進めていきます。時折ビルドして、動作を確認しながら実装を進めると良いでしょう。
6. ボタンが押されたら式の文字列を表示・更新
ボタンが押されたときに実行する処理を、ソースコードに書いていきます。ViewController.swiftを開いて、ソースコードのviewDidLoad
メソッドおよびinputFormula
メソッドを、次のように編集します。
override func viewDidLoad() { super.viewDidLoad() // ビューがロードされた時点で式と答えのラベルは空にする formulaLabel.text = "" answerLabel.text = "" } @IBAction func inputFormula(_ sender: UIButton) { // ボタン(Cと=以外)が押されたら式を表示する guard let formulaText = formulaLabel.text else { return } guard let senderedText = sender.titleLabel?.text else { return } formulaLabel.text = formulaText + senderedText }
inputFormula
メソッドでは、sender.titleLabel?.text
で押されたボタンのTitleを取得できます。ボタンが押されるたびに、式を連結して、式用のラベルに表示するという処理です。
7. イコールが押されたら答えを表示し、Cが押されたら式と答えをクリアする
続いて、[=]ボタンが押されたときに計算を実行して答えを表示するcalculateAnswer
メソッド、および[C]ボタンが押されたときに式と答えをクリアするclearCalculation
メソッドを実装します。
これらのメソッドを実装すると、最終的にViewController.swiftは、以下のようになります。
import UIKit import Expression class ViewController: UIViewController { @IBOutlet weak var formulaLabel: UILabel! @IBOutlet weak var answerLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // ビューがロードされた時点で式と答えのラベルは空にする formulaLabel.text = "" answerLabel.text = "" } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } @IBAction func inputFormula(_ sender: UIButton) { // ボタン(Cと=以外)が押されたら式を表示する guard let formulaText = formulaLabel.text else { return } guard let senderedText = sender.titleLabel?.text else { return } formulaLabel.text = formulaText + senderedText } @IBAction func clearCalculation(_ sender: UIButton) { // Cボタンが押されたら式と答えをクリアする formulaLabel.text = "" answerLabel.text = "" } @IBAction func calculateAnswer(_ sender: UIButton) { // =ボタンが押されたら答えを計算して表示する guard let formulaText = formulaLabel.text else { return } let formula: String = formatFormula(formulaText) answerLabel.text = evalFormula(formula) } private func formatFormula(_ formula: String) -> String { // 入力された整数には`.0`を追加して小数として評価する // また`÷`を`/`に、`×`を`*`に置換する let formattedFormula: String = formula.replacingOccurrences( of: "(?<=^|[÷×\\+\\-\\(])([0-9]+)(?=[÷×\\+\\-\\)]|$)", with: "$1.0", options: NSString.CompareOptions.regularExpression, range: nil ).replacingOccurrences(of: "÷", with: "/").replacingOccurrences(of: "×", with: "*") return formattedFormula } private func evalFormula(_ formula: String) -> String { do { // Expressionで文字列の計算式を評価して答えを求める let expression = Expression(formula) let answer = try expression.evaluate() return formatAnswer(String(answer)) } catch { // 計算式が不当だった場合 return "式を正しく入力してください" } } private func formatAnswer(_ answer: String) -> String { // 答えの小数点以下が`.0`だった場合は、`.0`を削除して答えを整数で表示する let formattedAnswer: String = answer.replacingOccurrences( of: "\\.0$", with: "", options: NSString.CompareOptions.regularExpression, range: nil) return formattedAnswer } }
formatFormula
メソッドでは、入力された計算式の文字列がSwiftの計算式として評価できるように、String#replacingOccurrences
メソッドを正規表現とともに用いて、文字列の置換処理を行っています。
そのほかは、コメントを読めばどんな処理を行っているかだいたい分かるかと思います。
動作確認
それでは、最後にビルドして動作確認してみましょう。
飲み会代の合計30,000円(税抜)を、1,000円割引きクーポンを使って、7人で割り勘する場合を計算してみました。一人あたり4,486円(1円未満四捨五入)と正しく計算でき、うまく動いているようです。
以上、電卓アプリの制作お疲れさまでした!
おわりに
Swiftの特徴から、基本文法、簡単な電卓アプリの作成まで、駆け足で解説しましたが、なかなかのボリュームになってしまいました。この記事が、入門から実践に進むお役に立てば幸いです。
Swiftの入門時には、やはり Optional
型とそれを取り扱う文法が一番難しいだろうと思います。逆に言うと、そこさえ攻略できれば大丈夫なわけです。あとはXcodeのGUI操作ですが、これはとにかく触って、アプリを作りながら慣れるのが早いでしょう。
XcodeのAutoLayout機能や、画面遷移の実装等にも触れたいところでしたが、文字数の都合もあり、割愛しています。SwiftやXcodeはアップデートの速度が速いので、公式ドキュメントにはぜひ触れておきましょう。
参考文献
Swiftの公式ドキュメントはこちら。
- The Swift Programming Language (Swift 3.1): About Swift
- Swift Standard Library | Apple Developer Documentation
以下の日本語ドキュメントでも、機能の詳細・サンプルコード等が確認できます。
今回制作した電卓アプリのソースコードは、私のGitHubに上げていますのでご参照ください。
takafumir/CalculatorApp: CalculatorApp
それでは、今後もSwiftでのiOSアプリ開発をお楽しみください!
執筆者プロフィール
山野 貴史(やまの・たかふみ)@taka222
編集:薄井千春(ZINE)