20191125のSwiftに関する記事は7件です。

メモ

スクリーンショット 2019-11-25 23.00.12.png
スクリーンショット 2019-11-25 23.00.21.png

可変である上のlabelのオートレイアウトはwide上下左右に付与

スクリーンショット 2019-11-25 22.59.45.png

下のlabelはwide高さ左右に付与
下のオートレイアウト は付与しない

全体のコード

 func view() {
        let view1 = CustomView.init(frame: CGRect(x: 0, y: 0, width: 200, height: 591))
        view1.test.text = "あああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああ"
//        view1.test.sizeToFit()

        let height = view1.test.sizeThatFits(CGSize(width: view1.test.frame.size.width, height: CGFloat.greatestFiniteMagnitude)).height
        view1.test.heightAnchor.constraint(equalToConstant: height).isActive = true

        view.addSubview(view1)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RxSwiftでAPIを複数呼ぶよ

Firestoreで参照使ってガッチャンコさせてました。

struct A {
    let id: String
    let name: String
    let bIDs: [String]
}
struct B {
    let id: String
    let name: String
}

func getA() -> Single<A> {...}
func getB(id: String) -> Single<B> {...}

みたいなとき?のように toArray()を駆使することで比較的シンプルにかけるみたい。

func getBList() -> Single<[B]> {
    return getA().flatMap({ (a: A) -> Single<[B]> in
        Observable.from(a.bIDs)
            .flatMap({ (id: String) -> Single<B> in
                getB(id: id)
            }).toArray()
    })
}

Bの順序を保ちたい場合は concatMapを使えば良さげ。

最初にsubscribeをネストさせていたのはナイショ?
最近、RxSwift書き始めたのでもっとこう書くべきってのがあったら指摘ください..!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[はじめてのiOSアプリ]xcodeで地図アプリを作成(その2)

はじめに

iOSアプリを作ってみたいけど
何から始めて良いのかわからない

とりあえず、
「やってみました」記事を参考に
地図アプリを真似てみようと思う

という記事の2回目です。

今回は、地図を表示します。

地図を表示

  1. MapKit.framework を追加
    • [MyGpsMap]-[Build Phase]-[Link Binary With Libraries]を選択し、[+]をクリック
      LinkBinLib.png
    • 検索窓に [map] と入力し絞り込み[MapKit.framework]を[Add] で追加
      AddMapkit.png
    • 【なぜ?】
      • Apple が提供してくれる地図を簡単に使うことができるフレームワーク(ライブラリ)
      • 苦労は買ってでもする人じゃない限り使うべき
  2. Mapkit を画面に配置
    • 画面左側のファイルツリーから[Main storyboard]を選択
      MainStoryboard.png
      • 【なぜ?】
        • あとで[Main storyboard]にGUIの地図部品を配置したいから、まずは選択しておく
        • [Main storyboard]を編集することで表示内容を決定できる
    • メニューから[View]-[Show Library]を選択、表示されるウインドウで [map] と入力し [Map Kit View]を絞り込む
      ShowLib-mapkit.png
      • 【なぜ?】
        • [Map Kit View]は地図を表示するためのGUI部品
        • 今回は、機能と名称が一致しているので絞り込みが楽々!
    • 絞り込み表示された[Map Kit View]を、Xcode右画面に表示される iPhone 画面上に、ドラッグ&ドロップする
      MapKitOnStoryboard.png
      • 【なぜ?】
        • [Map Kit View]を[Main storyboard]に配置することで地図が表示できる
    • [Map Kit View]を画面いっぱいに配置
      • 【なぜ?】
        • 地図は画面いっぱいに表示された方が見やすいから
          MapKitOnStoryboard-max.png
  3. テスト実行
    • Xcode 左上の矢印アイコンをクリック
      StartSimulator.png
  • iPhone Simulatorが起動し、日本全体が表示された
    MapJapan.png

今回の到達点

  • シミュレーターを使って地図を表示できた

連載

  1. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その1)
  2. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その2)
  3. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その3)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Combineでよく見るSwiftのNever型とは

Never let me go — けして私を離さないで、僕たちはけしてと言うけれどその約束を守るのは難しい。
でも僕たちの愛する Swift では Never は絶対的な Never でその単純さが美しかった。

まぁそんなポエムはいいんですけど、iOS13から導入された Combine ではよく Never という型を見る。

@Published var x = 0
@Published var y = 0

var timesPublisher: AnyPublisher<Int, Never> {
    $x.combineLatest($y)
        .map(*)
        .eraseToAnyPublisher()
}

AnyPublisher<Int, Never> と失敗したときの型が Never になっている。これはけして失敗しないことを型で表現してる。

Publisher protocol の定義を見てみると publisher は失敗した場合の型を指定する必要がある。例え失敗しない場合でも型を指定する必要があるので失敗しないということを表す Never がここで必要になってくる。

public protocol Publisher {

    /// The kind of values published by this publisher.
    associatedtype Output

    /// The kind of errors this publisher might publish.
    ///
    /// Use `Never` if this `Publisher` does not publish errors.
    associatedtype Failure : Error

    /// This function is called to attach the specified `Subscriber` to this `Publisher` by `subscribe(_:)`
    ///
    /// - SeeAlso: `subscribe(_:)`
    /// - Parameters:
    ///     - subscriber: The subscriber to attach to this `Publisher`.
    ///                   once attached it can begin to receive values.
    func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
}

Uninhabited Types

Never という型がどういうものなのか関数を作ってみていく。

Screen Shot 2019-11-24 at 2.36.33 PM.png

型の宣言だけした関数を用意してみるとエラーで Int を返す関数は Int を返してないと言われるのに対して Never を返す関数のエラーは何やらいつものエラーと違うものになってる。

Function with uninhabited return type 'Never' is missing call to another never-returning function on all paths

uninhabited return type 'Never' というよくわからない説明をされる。
どうやら Never というのは普通の型とは違うらしい。

ここで Never の定義を見てみる。

public enum Never {}

普通の enum の定義だがそこには case が一つもなかった。
enum は init を持たないので case がないということは値を作ることができないということになる。

このように値を作れない型のことを uninhabited type という。

しかし値を作れない型が何の役に立つんだと思うかもしれない。
プログラムを書いてると値を返さない関数(無限ループ、プログラムの終了)を書きたくなることがある。Never があればそのような関数の型を正しく表現することができるようになる。

でも、値を返さない関数の時は Void で定義するじゃんと普通思う。

そこで Swift での Void が何なのか定義をみてみる。

public typealias Void = ()

Swift での Void は空のタプルの別名として定義されている。
そして Swift で関数定義する時に戻り値の型を定義しなかったら常に戻り値の型は Void になる。

func log(_ message: String) {
    print("Message: \(message)")
}

let logger: (String) -> Void = log

logger("This is void function")

空のタプルは () という型であり let unit = () として値を作ることができる。
このようにただ一つしか値がない型をユニット型という。なので () の型や値のことをユニットとよく呼ぶ。

つまり Swift での値を返さない関数と思っていた -> Void な関数は実際にはユニットを返してるのと同じ関数になる。

func f() -> Void {}
func g() { return () }

print() のように値を返さないと思っている関数は実は値を返している。

let printResult: Void = print("This is void function")
print(printResult)                  // => ()

NeverとVoidの関数の違い

値を返さない無限ループの関数を作ってみる。

func giveMeNever() -> Never {
    while true {}
}

この値を返さない関数を値を返す関数の中で呼んでみる。

func giveMeNever() -> Never {
    while true {}
}

func giveMeInt() -> Int {
    giveMeNever()
}

giveMeInt()Int の値を返してないけどエラーなくコンパイルすることができる。

giveMeNever() の代わりに Void を返す関数を呼んでみる。

Screen Shot 2019-11-24 at 7.54.45 PM.png

Cannot convert return expression of type '()' to return type 'Int'

()Int に変換できないとエラーになった。
Swift は強い型づけされた言語なので変換できないと言われる、なのに -> NeverInt を返すところで呼んでもエラーにならない。Never だけ特別扱いなのだろうか?まるで NeverInt として扱われてるみたいだ。他の型でもいいのだろうか? Double は? String は?

func giveMeDouble() -> Double {
    giveMeNever()
}

func giveMeString() -> String {
    giveMeNever()
}

エラーにならなかった。このコードは Swift では許可されたコードになる。
まるで NeverInt でもあるし Double, String でもあるようだ。

Never のように値を作ることができなくて、すべての型のサブ型のことを型理論ではボトム型という。

ボトム型

ボトム、底、底があるならこの世にはその逆の上の世界があると人は夢をみる。
型の世界にもボトムがあるようにトップがある。

ボトム型が全ての型のサブ型ならトップ型はその逆の全てのサブ型はトップ型のサブ型であるとなる。

Types.png

Swift には Any という型がある。
Any はあらゆる型の値を表すことができる型だ。例え関数でもだ。

func anyWeSayAnyType(_ x: Int) -> Any {
    switch x {
    case 4:
      return 12

    case 3:
      return 12.34

    case 2:
      return true

    case 1:
      return "This is String type but it's also Any"

    default:
      return { (x: Int, y: Int) in x * y }
    }
}

Any のようにあらゆる型の値を表せるのをトップ型という。
その逆のボトム型はあらゆる型のサブ型なので Int でも Double でも String としても扱える。

ボトム型のようでボトムじゃない Never

まるで NeverInt でもあるし Double, String でもあるようだ。

と前に言った。本当にそうなのだろうか?
コードで確かめてみよう。

Screen Shot 2019-11-25 at 1.44.30 PM.png

Never は定義した型に変換できないとエラーになる。
しかしクロージャで包んであげるとエラーにはならない。

func isNeverBottomType() {
    let x: Int = { giveMeNever() }()
    let flag: Bool = { giveMeNever() }()
    let text: String = { giveMeNever() }()
}

このように Swift 5.1 では Never は本物のボトム型ではない。
Never のような uninhabited type を関数の戻り値とする場合だけボトム型のような振る舞いをする。

一応 Swift の設計者は Never を Swift 3 で導入した時にボトム型にすることを提案はしていた。
Never as a universal "bottom" subtype

Neverによって得た型の表現力

Swift ではfatalError() という関数をよくみる。致命的なエラーだからプログラムを終了するという関数だ。

定義を見てみる。

public func fatalError(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) -> Never

Never を返してる。
しかし fatalError() は Swift 3以前は定義が違った。大体こんな定義だった。

@noreturn func fatalError(_ message: () -> String = String(), file: StaticString = #file, line: UInt = #line)

Never の代わりに @noreturn という注釈がつけられている。これはコンパイラにこの関数は戻り値がないということを教えている。
基本的には Never が使われてる意図と同じだ。

しかし @noreturn にはいくつか問題点があった。
次のような使い方をしたらどうなるだろうか?

@noreturn func square(_ x: Int) -> Int { ... }
@noreturn func runApp() throws { ... }

値を返す関数やエラーを投げる関数に @noreturn をつけたらどうすればいいだろうか?
コンパイラは最初の値を返す場合はエラーにしたらいいだろう、でもエラーを投げる場合は許可すべきだろうか?許可はするけど値は返さないとすればいいだろうか?それとも値を返さないしエラーを投げることもないとすればいいだろうか?明らかな正解はない。
注釈による戻り値がないことを表すとこのように複雑性が生まれる。

Swift はこの問題を Never を導入することによって解決した。
Never という値を作れない型で値を返せないことを型として表現した。とても単純で美しい解決方法だ。

おわりに

ここまで読んでくださってありがとうございます。

Never というとても小さく単純な型をみてきた。
小さく単純なものを定義することによってプログラミングでよくある問題を表現することができるのは美しいと思う。

Never return Never

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUIでtabItemの画像サイズを変更する方法

SwiftUI tips

SwiftUIでタブのアイテムのサイズを変更するのに少し苦戦したので共有します。
なお、大きくしたアイコンがtabの一番上に付いていてpaddingも効かないのでご了承?‍♂️
(解決策あったら米キボンヌ)

成功するパターン

Text("user")
    .tabItem {
        Image(systemName: "person")
            .font(.title)
        }
.tag(4)

IMG_0335.png

ダメなパターン

Text("user")
    .tabItem {
        Image(systemName: "person")
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(width: tabItemSize, height: tabItemSize)
        }
.tag(4)

IMG_0334.png

参考URL

https://developer.apple.com/documentation/swiftui/tabview

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftUIでtabItemのSF Symbolsのサイズを変更する方法

SwiftUI tips

SwiftUIでタブのアイテムのサイズを変更するのに少し苦戦したので共有します。
なお、大きくしたアイコンがtabの一番上に付いていてpaddingも効かないのでご了承?‍♂️
(解決策あったら米キボンヌ)

成功するパターン

Text("user")
    .tabItem {
        Image(systemName: "person")
            .font(.title)
        }
.tag(4)
.font(.system(size: 10))//とかでも行ける

IMG_0335.png

ダメなパターン

Text("user")
    .tabItem {
        Image(systemName: "person")
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(width: tabItemSize, height: tabItemSize)
        }
.tag(4)

IMG_0334.png

参考URL

https://developer.apple.com/documentation/swiftui/tabview

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift5.1のattribute全解説(全27種)

概要

Swift5.1で利用できるattributeの一覧です。(Public APIのみ)

本記事は以前作成したSwiftのattributeまとめ[Swift4対応]をSwift5.1向けに更新したものです。

attributeは全てを暗記する必要はありません。これなんだっけ、と思ったときにこの記事でさくっと見られる簡易リファレンスになればと思います。

attributeとは

  • コンパイラに対し、宣言や型の補足情報を伝えるもの
  • 属性や修飾子とも呼ばれる
  • Swift5.1では公式リファレンスに記載されているもので全27種類

attributeの記法

  • attributeの記法は以下のようになり、より詳細な情報を補足するために引数も指定することができる
  • @〜という記法はコンパイラディレクティブと呼ばれ、コンパイラに対する指示を記載する際に利用される
// 引数なしの場合
@属性名

// 引数ありの場合
@属性名引数

attribute一覧

  • autoclosure
  • escaping
  • convention
  • available
  • discardableResult
  • objc
  • nonobjc
  • objcMembers
  • GKInspectable
  • UIApplicationMain
  • NSApplicationMain
  • NSCopying
  • NSManaged
  • testable
  • IBAction
  • IBOutlet
  • IBDesignable
  • IBInspectable
  • dynamicCallable
  • dynamicMemberLookup
  • frozen
  • inlinable
  • usableFromInline
  • propertyWrapper
  • requires_stored_property_inits
  • warn_unqualified_access
  • unknown

※ドキュメントに記載のあるPublic APIのみです。

@autoclosure

  • 以下のように、呼び出し部分で引数として渡した値を、メソッド(関数)内ではクロージャーとして扱える
  • クロージャーとして渡しているため、引数が実際に評価されるのは、クロージャーの実行時(遅延評価が可能)
// 定義部分
func someMethod(closure: @autoclosure () -> Int) {
    print(closure())
}

// 呼び出し部分
someMethod(closure: 10)

利用シーンは少ないと想定するが、&&演算子や||演算子の実装などで利用

&&演算子、||演算子の実装
public static func &&(lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows -> Bool
public static func ||(lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows -> Bool

@escaping

  • クロージャをスコープ外でも保持する必要があることを示す
  • 以下の例では、completionクロージャーの実行を非同期で実行しているため、スコープ外ではクロージャーが保持されず、コンパイルエラー
// NG: コンパイルエラー
func someAsyncMethod(completion: () -> Void) {
    DispatchQueue.main.async {
       completion()
    }
}

// OK
func someAsyncMethod(completion: @escaping () -> Void) {
    DispatchQueue.main.async {
       completion()
    }
}

@convention

  • 関数のポインタに対し、その呼び出し方式を指定
// Swiftの関数ポインタ(デフォルト)
var swiftFunction: @convention(swift) (Bool) -> Bool

// Objective-Cと互換性をもつブロックポインタ
var block: @convention(block) (Bool) -> Bool

// C言語の関数ポインタ
var cFunction: @convention(c) (Bool) -> Bool

@available

  • 各OSバージョンなどの環境に対し、APIの有効性を表す
  • Swiftのバージョン(swift)
  • プラットフォームの種類(iOS,iOSApplicationExtension, macOS, macOSApplicationExtension, watchOS, watchOSApplicationExtension ,tvOS, tvOSApplicationExtension)
// 全環境(*)で利用不可(unavailable)、SomeProtocolにリネームした(renamed:)
@available(*, unavailable, renamed: "SomeProtocol")
protocol MyProtocol { ... }

// macOS 10.12で廃止(obsoleted)とメッセージを表示(message)
@available(macOS 10.12, obsoleted: 10.12, message: "macOS 10.12で廃止されました")
func someMethod() { ... }

// iOS2で導入(introduced)、iOS9で非推奨(deprecated)
@available(iOS, introduced: 2.0, deprecated: 9.0)
var some: String

例えば、iOS9で非推奨となった、UIAlertViewは以下のようにattribute指定することで、UIAlertViewを利用した際にコンパイラ警告を出し、UIAlertControllerの利用を推奨する。

スクリーンショット 2017-06-30 4.50.06.png

UIAlertView.h
@available(iOS, introduced: 2.0, deprecated: 9.0, 
message: "UIAlertView is deprecated. Use UIAlertController with a 
preferredStyle of UIAlertControllerStyleAlert instead")
open class UIAlertView : UIView { ... }

@discardableResult

  • 返り値を持つ関数やメソッドの返り値を利用しなかった場合のコンパイラ警告を無視する
  • 返り値を必ずしも利用しなくて良いメソッドを定義するのに適する
// メソッドの定義部分
func someMethod1() -> Bool { ... }

@discardableResult
func someMethod2() -> Bool { ... }

// 呼び出し部分
someMethod1() // NG:メソッドの返り値を利用していないためコンパイラ警告がでる
someMethod2() // OK

@discardableResultを指定しない場合は「Result of call to 'someMethod()' is unused」とwarningが表示される。
スクリーンショット 2017-06-30 4.57.07.png

@objc

  • Objective-Cから使用できることを明示的に宣言
  • extensionに指定すると全てのメンバに一括で指定できる
  • @objc(引数)でObjective-Cで使いたい名前を指定できる
  • Swift4以前ではNSObjectを継承したクラスやdynamicでは暗黙的に@objcが付け加えられていましたが、Swift4では付与されなくなりました(Proposal: SE-0160
// someMethodとしてObjective-Cから利用可能
@objc(someMethod)
func someMethodForObjc() { ... }

@nonobjc

  • Objective-Cから使用できないことを明示的に宣言
  • extensionに指定すると全てのメンバに一括で指定できる
@nonobjc
func SomeMethodForSwift() { ... }

@objcMembers

  • クラス全体に対し一括でObjective-Cから使用できることを明示的に宣言
  • @objcMembersを付与したクラスのサブクラスやエクステンションにも影響
  • Swift4で導入(Proposal: SE-0160
@objcMembers
class MyClass : NSObject {
    // @objcになる
    func foo() { }             

    // @objcにならない(タプルを返しているため)
    func bar() -> (Int, Int) { return (1, 1) }   
}

extension MyClass {
    // @objcになる
    func baz() { }   
}

class MySubClass : MyClass {
    // @objcになる
    func wibble() { } 
}

extension MySubClass {
    // @objcになる
    func wobble() { }   
}

@GKInspectable

  • カスタムのGameplayKitコンポーネントプロパティをSpriteKitエディタのUIに公開する
  • 暗黙的に@objcが付け加えられる
class MyComponent: GKComponent {
    @GKInspectable var speed: Float = 1.0
    @GKInspectable var friction: Float = 2.0
}

@UIApplicationMain

  • アプリケーションデリゲートであることを示す(iOSアプリ用)
  • この属性がない場合はmain.swiftを用意し、UIApplicationMain(::_:)関数を利用してデリゲート設定を行う
例(AppDelegate.swift)
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions
        launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        return true
    }
    func applicationWillResignActive(_ application: UIApplication) { }
    func applicationDidEnterBackground(_ application: UIApplication) { }
    func applicationWillEnterForeground(_ application: UIApplication) { }
    func applicationDidBecomeActive(_ application: UIApplication) { }
    func applicationWillTerminate(_ application: UIApplication) { }
}

@NSApplicationMain

  • アプリケーションデリゲートであることを示す(macアプリ用)
  • この属性がない場合はmain.swiftを用意し、UIApplicationMain(::_:)関数を利用してデリゲート設定を行う
例(AppDelegate.swift)
import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ aNotification: Notification) { }
    func applicationWillTerminate(_ aNotification: Notification) { }
}

@NSCopying

  • ストアドプロパティのセッターでコピーした値をセットする
  • Objective-Cのcopy属性と同様
  • Swift4でイニシャライザでの挙動が改善されました(Proposal: SE-0153
@NSCopying var foo: Foo

@NSManaged

  • クラスのインスタンスメソッドやストアドプロパティに対し、CoreDataで実行時に動的に実装が生成されることを宣言する
@NSManaged var name: String

@testable

  • モジュールのimport宣言に対し、internal以上のアクセスレベルで公開されているメソッドやプロパティに対しテストクラスがアクセス可能なことを宣言する
@testable import SomeModule

@IBAction

  • メソッドがInterfaceBuilder(StoryBoard)に配置したパーツのアクションに紐付けられることを示す
  • 暗黙的に@objcが付け加えられる
@IBAction func buttonTapped() { ... }

@IBOutlet

  • プロパティがInterfaceBuilder(StoryBoard)に配置したパーツに紐づけられることを示す
  • 暗黙的に@objcが付け加えられる
@IBOutlet weak var detailDescriptionLabel: UILabel!

@IBDesignable

  • UIViewまたはNSViewを継承したカスタムクラスに指定するとデザインやサブビューがInterfaceBuilder(StoryBoard)上でライブレンダリングされる
  • 暗黙的に@objcが付け加えられる
@IBDesignable class CustomView: UIView { ... }

@IBInspectable

  • プロパティに指定すると、InterfaceBuilder(StoryBoard)のAttribute Inspectorで設定でき、ライブレンダリングでデザインを確認できる
  • 対応する型(Int, CGFloat, Double, String, Bool, CGPoint, CGSize, CGRect, UIColor, UIImage)
  • 暗黙的に@objcが付け加えられる
@IBInspectable 
public var cornerRadius: CGFloat = 2.0 { 
    didSet { 
        self.layer.cornerRadius = self.cornerRadius 
    } 
}

@IBSegueAction

  • StoryboardでSegueを設定し、画面遷移する際の処理を指定できる
  • 引数のNSCoderインスタンスを利用し、遷移先のViewControllerの任意のイニシャライザを指定することが可能
@IBSegueAction
private func openDetail(coder: NSCoder, sender: Any?, segueIdentifier: String?)
    -> DetailViewController? {
    return DetailViewController(coder: coder, item: item)
}

@dynamicCallable

  • 型に@dynamicCallableを付与し、dynamicallyCall(withArguments:)メソッドもしくはdynamicallyCall(withKeywordArgument:)メソッドを実装することで、その型を関数のように直接呼び出しができるようになる
@dynamicCallable struct Sum {
    // 引数ラベルなし
    func dynamicallyCall(withArguments args: [Int]) -> Int {
        return args.reduce(0, +)
    }

    // 引数ラベルあり
    func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Int {
        return args.map { $0.value }.reduce(0, +)
    }
}

let sum = Sum()
print(sum(1, 2, 3)) // 6
print(sum(first: 1, second: 2, third: 3)) // 6

@dynamicMemberLookup

  • 型に@dynamicMemberLookupを付与し、subscript(dynamicMember:)を実装することで、コンパイル時に存在しないプロパティに.でアクセスが可能となる
@dynamicMemberLookup struct Dog {
    subscript(dynamicMember key: String) -> String {
        return key
    }

    subscript(dynamicMember key: Int) -> Int {
        return key
    }
}

let dog = Dog()
print(dog.name) // "name"
print(dog.1) // 1

@frozen

  • library evolution mode(-enable-library-evolutionオプション)でコンパイル時のみ有効
  • structもしくはenumに付与し、将来のバージョンでの型の変更を制限する
  • structではストアドプロパティ、enumではcaseの追加、削除、並び替えを制限する
  • library evolution modeでない場合には全てのstruct、enumが暗黙的に@frozenとなる
@frozen enum IceCream {
    case vanilla
    case chocolate
    case greenTea
}

@inlinable

  • 関数、メソッド、コンピューテッドプロパティ、subscript、コンビニエンスイニシャライザ、デイニシャライザに付与することで、実装をmodule interfaceの一部として公開する(インライン展開)
  • publicもしくはinternalで宣言されたものにのみ付与可能(実際にはinternalで定義された@inlinable@usableFromInlineを意味する)
@inlinable func hoge() {
    print("hoge")
}

@usableFromInline

  • @inlinableの実装でアクセスする宣言に対して必要なattribute
  • アクセス制御修飾子をサポートするすべての宣言に適用可能
  • internalで宣言されたものにのみ付与可能
@inlinable func hoge() {
    print("hoge")
    fuga()
}

@usableFromInline func fuga() {
    print("fuga")
}

@propertyWrapper

  • プロパティの値にアクセスする方法を独自のattributeとして定義できる
  • attributeを定義するにはこのattributeをclass,enum,structのいずれかに付与
  • アクセス方法はwrappedValueプロパティのget、setを定義することで記述
  • projectedValueプロパティを定義することで、$プロパティ名でアクセス可能な値を定義できる
  • イニシャライザでattributeに初期値を与えることも可能
@propertyWrapper struct SmallNumber {
    private var max: Int
    private var number: Int
    var projectedValue = false

    var wrappedValue: Int {
        get { return number }
        set {
            number = min(newValue, max)
            projectedValue = newValue > max
        }
    }

    init(wrappedValue: Int, max: Int) {
        self.number = min(wrappedValue, max)
        self.max = max
    }
}

struct SmallPoint {
    @SmallNumber(wrappedValue: 5, max: 10) var x: Int
    @SmallNumber(wrappedValue: 5, max: 10) var y: Int
}

var smallPoint = SmallPoint()
print(smallPoint.x) // 5
print(smallPoint.$x) // false

smallPoint.x = 200
print(smallPoint.$x) // true
print(smallPoint.x) // 10

@requires_stored_property_inits

  • クラス定義に付与することで、すべてのストアドプロパティのデフォルト値を要求する
  • NSManagedObjectを継承するクラスでは暗黙的に付与される
@requires_stored_property_inits
class Hoge {
    // error: Stored property 'number' requires an initial value
    let number: Int

    init(number: Int) {
        self.number = number
    }
}

@warn_unqualified_access

  • トップレベルの関数、インスタンスメソッド、classメソッド、staticメソッドに付与することで、モジュール名や型名、インスタンス変数やインスタンス定数など、メソッドを特定するものがない場合にwarningを発生させる
  • 同一スコープ内で同名の関数にアクセス可能な場合に曖昧さをなくすのに役立つ
  • Sequenceのmin()で利用
@warn_unqualified_access func min() -> Self.Element? { 
   /// 
}

@unknown

  • 将来的に新しいcaseが追加された場合に、そのケースをhandlingできるようにする
  • コンパイル時にはenumが網羅されていない旨のwarningが発生する
enum Signal {
    case red
    case yellow
    case green
    case purple // 新しいcase
}

let signal = Signal.yellow

switch signal { // warning: Switch must be exhaustive
case .red:
    print("red")
case .yellow:
    print("yellow")
case .green:
    print("green")
@unknown default:
    print("未知のケース")
}

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む