yynsmk's tech blog

何でもできる=何にもできない

SwiftUIのチュートリアルの文法(コンピューテッドプロパティ)について

はじめに

先日、SwiftUIが発表されて最近ちょくちょく公式チュートリアルをやっています。
2つ目のチュートリアルをやっていると、見たことのないプロパティの定義方法が出てきたので、今回はそれについてまとめてみました。

これが問題のコードです。

var locationCoordinate: CLLocationCoordinate2D {
    CLLocationCoordinate2D(latitude: coordinates.latitude, longitude: coordinates.longtitude)
}

ぱっと見コンピューテッドプロパティっぽいですが、gettterやsetterやreturn文がありませんよね。
この問題を解決すべく、まずコンピューテッドプロパティについて簡単に説明し、その後上記のコードについて解説しようと思います。

コンピューテッドプロパティとは?

プロパティ自身が値を保持しておらず、プロパティへのアクセス時に計算した値を返すプロパティです。
ちなみに、値を保持するプロパティはストアドプロパティといいます。

プロパティの種類については以下の記事がわかりやすいです。
qiita.com

コンピューテッドプロパティは次のように使います。

import UIKit

struct Circle {
    // ストアドプロパティ
    var radius: Double = 0
    
    // コンピューテッドプロパティ
    var area: Double {
        get {
            return radius * radius * 3
        }
        set {
            // プロパティに代入した値はnewValueでアクセス可能
            radius = sqrt(newValue / 3) 
        }
    }
}

var circle = Circle()
circle.radius // 0
circle.area   // 0 (getter)

circle.radius = 5
circle.radius // 5
circle.area   // 75 (getter)

circle.area = 27 // (setter)
circle.radius // 3
circle.area   // 27

コンピューテッドプロパティを定義するときはget{}でgetterを、set{}でsetterを指定し、getterには値を返す処理を、setterには値を更新する処理を書きます。
上のコードでは、getterで半径に応じた円の面積を返す処理を、setterで円の半径を更新する処理を書いています。

ここで、コンピューテッドプロパティのgetterは必須ですが、setterは省略可能です。

import UIKit

struct Circle {

    var radius: Double = 0
    
    var area: Double {
        get{
            return radius * radius * 3
        }
    }
}

var circle = Circle()
circle.radius // 0
circle.area   // 0 (getter)

circle.radius = 5
circle.radius // 5
circle.area   // 75 (getter)

circle.area = 27  // setterを定義していないのでコンパイルエラーになる

また、setterを省略した場合はget{}を省略してgetterを書くこともできます。

import UIKit

struct Circle {

    var radius: Double = 0
    
    var area: Double {
            // getがなくてもgetterとして動作する
            return radius * radius * 3
    }
}

以上がこれまでのコンピューテッドプロパティの定義方法です。

スポンサーリンク

新しいコンピューテッドプロパティ?

コンピューテッドプロパティについて理解できたところで、初めのコードを見てみましょう。

var locationCoordinate: CLLocationCoordinate2D {
    CLLocationCoordinate2D(latitude: coordinates.latitude, longitude: coordinates.longtitude)
}

これは、先程説明したsetterとget{}を省略した形に似ていますが、returnまでもが省略されています。
あれ?こんな書き方OKなの?といろいろ調べていると、以下のページが見つかりました。

Swift5.1公式ドキュメント
github.com

ドキュメントのShorthand Getter Declarationでは次のように書かれています。

If the entire body of a getter is a single expression, the getter implicitly returns that expression.

つまり、getterの中身の式が一つだけなら、returnは書く必要がないってことです。
この暗黙的なreturnは、短いコードのreturnの占める割合多くね?単一式だったらreturn書いてるってことにしようよってことで採用されたようです。
そのため、暗黙的なreturnは関数の定義においても使えます。

func oldCircleArea(with radius: Int) -> Int {
    return radius * radius * 3
}
func newCircleArea(with radius: Int) -> Int {
    radius * radius * 3 // returnを省略 
}
print(oldCircleArea(with: 5)) // 75
print(newCircleArea(with: 5)) // 75

まとめ

Swift5.1から関数の定義やgetterでは単一式の場合にreturnが省略でき、コード量を減らせるようになります。
SwiftUIのチュートリアルはとてもわかりやすいのですが、文法の解説がないのがなんだかなーって感じです。
他にも気になった文法が出てくれば、調べてまとめようと思います。