iOSアプリ開発に着手しようとし、Swiftを勉強し始めました。今まで触れてきた言語と共通する部分がありながらも、異なる要素もそれなりに含まれているなという印象です。そういった言語間の違いがあらわれそうなところを調べながらまとめてみました。
この記事ではいきなり書けることを目標とするのではなく、これをおさえておけば、なんとなくソースが読めるかも、というレベルを目指しています。概要をまとめただけで、ややこしい文法の詳細は他のページや記事に任せるようにしています。
みなさんが使われている言語によっては「これは他の言語にも共通じゃないか」と思われることもあるかもしれませんが、ご容赦ください。
※バージョンはSwift3.1を想定
おすすめリンク
良いページがあれば随時更新します。個人的に公式ドキュメントを読めばだいたいなんとかなりそう。
- Swift公式ドキュメント
- ここを読んでおけば間違いない。A Swift Tourのページではコードを書きながら重要な文法を一通り学べるのでおすすめです。全部英語なので横文字がしんどい人には厳しいかもですが。。。
- 逆引きSwift
- やりたいことが明確であればここの逆引きを使うと便利です。
変数まわり
変数の宣言
最初にvarを置くことで変数を定義できる。
型を指定した変数宣言・代入
var hoge: Int = 3
型推論を利用した宣言・代入
var hoge = 3
定数の宣言
最初にletを置くことで定数を定義できる。
値が変更できないオブジェクトを定義する。
大文字で書くとかconst
を書くのではなくlet
を置く。
let hoge: String = "Hoge, foo bar"
数値を文字列にパースする
これは馴染みがあるかも。
var num: Int = 3
var str = String(num)
print(str)
// -> 3
IntとFloatやDoubleの演算
Swiftでは型が違う値を演算する際に自動的にパースしてくれないので、単純にInt型とFloat型などを演算しようとするとエラーが出る。そのため自分でパースする必要がある。
var integer: Int = 2
var flt: Float = 0.71828
//そのまま計算すると怒られる
//var napier = integer + flt
/*
Int -> Floatに変換
*/
var napier = Float(integer) + flt
print(napier)
// -> 2.71828
/*
Float -> Intに変換
Float型の小数点以下は切り捨てられてInt型に変換される。
*/
napier = integer + Int(flt)
print(napier)
// -> 2
文字列と数値を結合
他の言語でよくみる形
var num = 1
var str = "num: " + num
print(str)
// -> num: 1
こういう書き方もできる。
PHPの変数展開(echo "hello, $name";
)に似た書き方。
var num = 1
var str = "num: \(num)"
print(str)
// -> num: 1
配列
配列には変更出来るものと出来ないものが存在する。
なお不変な配列の方が高速に動作する。
var mutable: [String] = [String]()
let immutable: [String] = [String]()
mutable.append("hoge")
mutable.append("hige")
//不変な配列の中身を変更しようとすると怒られる
//immutable.append("hoge")
辞書型
辞書も配列と同様に変更出来るものと出来ないものが存在する。演算速度も配列同様不変なものの方が速い。
var mutable: [String:Int] = [String:Int]()
let immutable: [String:Int] = [String:Int]()
mutable["hoge"] = 1
mutable["hundred"] = 100
mutable["million"] = 1000000
//不変な辞書の中身を変更しようとすると怒られる
//immutable["hoge"] = 1
Optional型
他の言語で言うところのnull
はSwiftでnil
という。Rubyと同じ呼び方ですね。
Swiftはデフォルトでは変数作成時に自動でnil
を設定されることがないです。つまり値を入れることが大前提となっています。値がないことを許容するにはOptional型を利用する必要があります。Optional型ではnilを代入することが可能になります。
Optional型の変数を利用するには、値がnilでないことを明示的に保証する必要があります。ある変数にOptional型を適用することをOptional型でラップすると良い、Optional型の値がnilでないことを保証することをアンラップするといいます。
ラップの仕方はvar hoge :String?
のように型名の後ろに?
を付けるだけです。
アンラップの仕方は
if let hoge = hoge{}
とすれば、変数hogeがnilでないことを保証できればブロック内の処理が実行されます。ブロック内ではアンラップされた変数hogeが使用出来ます。
ただ変数hogeがnilでないかどうかを判定するのみでよく、hogeの値を使用しなくて良い場合は
if let _ = hoge{}
と書くことでアンラップ後の変数代入を省略できます。
var str1: String? = "hoge"
var str2: String?
var str3: String
print(str1)
// -> Optional("hoge")
//出力はアンラップ前のものになる
print(str2)
// -> nil
//何も代入しないと変数にはnilが設定される
//print(str3)
//エラーが生じる
//Optional型でラップしていないためnilが許容されていない
/*
if let文
*/
if let str1 = str1{
//優先的にアンラップ後の変数(ローカル変数)を呼び出すため、str1は変更できない
//str1 = "foobar"
print(str1)
}
//let str1はif letブロック内のローカル変数のため、if letの外ではグローバルのstr1が参照されるため内容が変更できる
str1 = "foobar"
Type Alias
既存の型を別名で呼びたい時に使用する。
typealias Hoge = Int32
hoge = Hoge.max
関数
定義
関数は次のように定義する。
func 関数名(引数名 :引数型名) -> 戻り値型名{
//処理
}
引数がない場合はカッコ内になにも書かなくてよい。
func myFunc() -> Int{
}
戻り値がない場合はVoidを明示する必要はなく、省略できる。
//これでもおk
func myFunc(arg1: String) -> Void{
}
//こっちのほうが楽
func myFunc(arg1: String){
}
引数の別名
引数の名前を詳細に付けた場合、変数名が長くなることが考えられます。関数定義内で繰り返し変数を参照する場合文字数が多いのは煩わしいので、引数に別名を定義することができます。
func 関数名(引数の別名 引数名:引数の型){
}
ただし関数を呼び出す際には元の引数名を指定して呼び出さないといけません。
func myFunc(longLongArgName shortArg:String){
print(shortArg)
}
myFunc(longLongArgName: "Arg")
エラー処理
私はあまり見慣れていない特殊な形式でした。
いいまとめを見つけたのでこれについては次の記事を参照してください。
関数呼び出し時の引数名の省略
PlayGroundで関数を適当に作っていじっていたのですが、引数名がなくても動作する場合と、しない場合があってちょっとしたルールがあるのかもと調べてみると良い記事が見つかりました。
基本的には引数名は指定する必要があるみたいですね。
クラス
定義
プロパティでオブジェクトを、メソッドで処理を保持する。クラスについては他の言語と共通するところが多くわかりやすいと思います。
基本はこれ。馴染みやすいと思います。
class クラス名{
var myProp = "サンプルのプロパティ"
func myFunc(){
print("サンプルのクラスメソッド")
}
}
クラスの継承
class `クラス名` :`継承元クラス名`{
}
メソッドのオーバーライド
継承元のメソッドの定義を変更したいときに使う。
override
キーワードを使って再定義する。
class A{
func greet(){
print("Aだよ")
}
}
class B :A{
override func greet() {
print("Bだよ")
}
}
var a = A()
var b = B()
a.greet()
// -> "Aだよ"
b.greet()
// -> "Bだよ"
継承元クラスのメソッドを呼び出す
例えば継承したクラスでメソッド
super.myFunc()
で呼び出せる。
オーバーライドと継承元クラス(スーパークラス)のメソッドを呼び出す例を紹介します。
class ScalarSum{
var x1,x2 :Int
func tasu() -> String{
return String(x1 + x2)
}
init(x1 :Int, x2 :Int){
self.x1 = x1
self.x2 = x2
}
}
class VectorSum :ScalarSum{
var y1,y2 :Int
//親クラスのtasuメソッドをオーバーラード
override func tasu() -> String{
return "(\(x1+x2) , \(y1+y2))"
}
init(x1 :Int, x2 :Int, y1 :Int, y2 :Int){
self.y1 = y1
self.y2 = y2
//親クラスのinitを呼び出す
super.init(x1: x1,x2: x2)
}
}
var scl = ScalarSum(x1: 1, x2: 2)
print(scl.tasu())
// -> 3
var vec = VectorSum(x1: 1,x2: 2, y1: 3, y2: 4)
print(vec.tasu())
// -> "(3 , 7)"
構造体
定義
struct 構造体名{}
機能
クラスと同様プロパティとメソッドを保持します。ですが、メソッドの引数にオブジェクトを渡した場合、クラスでは参照渡しになるのに対し、構造体では値渡しになります。
class testClass{
var value :Int = 0
func show(){
print(String(value))
}
}
struct testStruct{
var value :Int = 0
func show(){
print(String(value))
}
}
let cls1 = testClass()
let str1 = testStruct()
//クラスは参照が、構造体は値が代入される
var cls2 = cls1
var str2 = str1
cls2.value = 5
str2.value = 5
cls1.show()
// -> 5
cls2.show()
// -> 5
str1.show()
// -> 0
str2.show()
// -> 5
タプル
複数の値をひとまとめにして扱うことができ、配列とことなり異なる型の値をセットにできます。後で要素を追加したり変更したりできません。
Pythonを使っている人には馴染みがあるかもしれませんね。
定義
ラベルを付ける場合
(変数名1 値1, 変数名2 値2, ...)
ラベルをつけない場合
(値1, 値2, ...)
例
var ramenTuple = (price: 980, taste: "味噌", noodles: "thin")
print(ramenTuple.price) // -> 980
print(ramenTuple.taste) // -> 味噌
//インデックスで参照することもできる
print(ramenTuple.2) // -> thin
構文
for文
いわずもがなですね。多くを書かなくても大丈夫だと思います。しかしC言語でよく使う形式のfor文は廃止される予定とのことです。
つまり次のように書くとWarningで怒られます。
for var i = 0; i < 3; i++
{
print("\(i)")
}
対策としては次の節で紹介するfor in文を代用することができます。
for in文
カウンタをインクリメント(デクリメント)していくのではなく、直接数値範囲を指定するイメージです。なお配列などのコレクションなどにも使うことができ便利です。この構文はPython使用者の方には馴染みがあるのではないでしょうか。
for文の代わりにこのあたりの表現を使うと良いと思います。ちなみに条件式とカウンタ変数はカッコで囲んでも囲まなくてもどちらでも大丈夫です。ただしブロックを意味する中括弧ははずせません。
//a..<bの範囲は(a <= x < b)
for i in 0..<3{
print("\(i)")
}
// -> 0 1 2
//a...bの範囲は(a <= x <= b)
for i in 0...2{
print("\(i)")
}
// -> 0 1 2
//カウンタが必要ない時
for _ in 0..<3{
print("a")
}
// -> a a a
配列に適用する例
var testArr = ["Swi", "ft", "たのし", "い"]
for str in testArr{
print(str)
}
-> Swiftたのしい
いい記事を見つけたので、詳しくはこちらを参照されると良いと思います。
C言語スタイルの for ループが Swift 3.0 で廃止される見込みの為(エラーメッセージには将来のみたいな表現だけど)、これまでこのスタイルで書いてきた for ループを書き直さないといけなくなりました。そこで、典型的なパターンを書き直す方法をまとめてみました。
if文
特に変わったことはないので簡潔に例だけ。
var value1 = 1
var value2 = 2
if value1<value2{
print("1は2より小さい")
}
-> 1は2より小さい
if value1 > value2{
print("2は1より小さい")
}else if value1==value2{
print("1は2と同じ")
}else{
print("1は2より小さい")
}
-> 1は2より小さい
ちなみに、他の言語では条件式のところで数値を指定すると、0と比較して自動的にBool型として評価されることがありますがSwiftではそのような仕様はありません。かならずvalue > 0
などとBool型として明示的に表現する必要があります。
switch文
こちらも簡潔な例をまず示します。
let value = 2
switch value{
case 0:
print("value is 0")
case 1...5:
print("value is 1~5")
case 6:
print("value is 6")
default:
print("other")
}
// -> value is 1~5
default
がないと「Switch must be exhaustive, consider adding default clause」というエラーがでます。つまりswitchは分岐が網羅的である必要があるのです。
お気づきかもしれませんがbreak
なるキーワードが必要ありません。ケースごとに処理は独立しており、そのまま下のケースに処理が移動するフォールスルーはデフォルトでは適用されないためです。break
書き忘れで怒られることも無くて済みますね。フォールスルーするようにしたい時には次のように明示します。
let value = 2
switch value{
case 0:
print("value is 0")
case 1...5:
print("value is 1~5")
fallthrough
case 6:
print("value is 6")
default:
print("other")
}
// -> value is 1~5 value is 6
下の例はパターンマッチにより分岐する例です。
let
の右の変数x
(名前はなんでもいいです)が、x.hasSuffix("bird")
の条件を判定し、
条件を満たしていればケース直下の文を実行します。この場合ではname
の末尾にbird
がついていることが条件となっており、birdが出力されます。
let name = "blue bird"
switch name{
case "tori":
print("tori")
case let x where x.hasSuffix("bird"):
print("bird")
default:
print("something else")
}
// -> "bird"
また、enum型と一緒に使うことが多いようです。
enum Taste{
case Shoyu
case Miso
case Shio
case Tonkotsu
}
let favorite = Taste.Tonkotsu
switch favorite{
case .Shoyu:
print("You like Shoyu")
case .Miso:
print("You like Miso")
case .Shio:
print("You like Shio")
case .Tonkotsu:
print("You like Tonkotsu")
}
// -> You like Tonkotsu
while文
特に変わったことはないので簡潔に例だけ。
var i = 0
while i < 3{
print("\(i)")
i += 1
}
// -> 0 1 2
補足しておくと、上記の例ではインクリメントのオペレータ++
を使用していませんが、++
や--
などのオペレータは今後廃止が見込まれているためです。
Swiftに今後追加される言語仕様について調べてみた - Qiita
guard
先ほど紹介したif let文に似ていて、処理の実行の前に変数の値が適切なものかを保証するための構文です。
guard let 確認後に使う変数名 = 確認する変数名 else{
変数が適切で無い場合の処理
}
どう使うのか?というところはこのあたりを参照してください。
Swift 2.0 で追加された guard のいいところ - Qiita
Swift の guard は正しく使いましょう - Qiita
便利な配列制御
関数型プログラミングっぽい便利な関数がたくさんあります。詳細の解説はは次の記事に譲りますが、おおまかには配列の要素を絞ったり、ソートしたり、足したり、よく使う操作をするのにとても便利です。
filter
,map
,reduce
,sort
などが一例です。
[Swift]これは覚えたい!!filter、map、reduceを使うとスゴい便利だった - Qiita
Swiftのmap, filter, reduce(などなど)はこんな時に使う! - Qiita
まとめ
Swiftは色んな言語から様々な概念を取り入れているんだなと思いました(小並感)。使いこなすことが出来れば気持よくプログラミングできるポテンシャルの高い言語だと思います。
新しい言語なのでバージョンアップが激しいので情報のキャッチアップが大変ですが、進化する楽しみもありますね。
今後Swiftがどのように変わっていくかを示したドキュメント
GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.