📡

[swift] [初心者向け] 通信処理の実行順序よくある間違いと無名関数による処理の分割方法

2022/01/06に公開

こちらのケースについいて、関数に切り分けたケースを考えてみます。

https://zenn.dev/yoneapp/articles/4f3efc0b70118c

override func viewDidLoad() {
    super.viewDidLoad()

    getUsers() // (a)
    showUsers() // (b)
}

func getUsers() {
    let db = Firestore.firestore()

    db.collection("users").getDocuments() { (querySnapshot, err) in
        if let err = err {
            print("Error getting documents: \(err)")
        } else {
             userDocuments = querySnapshot!.documents // (c)
        }
    }
}

func showUsers() {
  // ここに userDocuments を使うコードが書いてある
}

こちらも (a) -> (c) -> (b) という順番で実行されるのを期待される方が多いです。
実際には (a) -> (b) -> (c) の順に実行されて失敗します。

単純に修正するとこうなります。

override func viewDidLoad() {
    super.viewDidLoad()

    getUsers() // (a)
}

func getUsers() {
    let db = Firestore.firestore()

    db.collection("users").getDocuments() { (querySnapshot, err) in
        if let err = err {
            print("Error getting documents: \(err)")
        } else {
             userDocuments = querySnapshot!.documents // (c)
             showUsers() // (b)
        }
    }
}

func showUsers() {
  // ここに userDocuments を使うコードが書いてある
}

おまけでクロージャーを使うと、こういう風に書くことも出来ます。

override func viewDidLoad() {
    super.viewDidLoad()

    getUsers { [weak self] in
        self?.showUsers()
    }
}

func getUsers(completion: @escaping () -> Void) {
    let db = Firestore.firestore()

    db.collection("users").getDocuments { (querySnapshot, err) in
        if let err = err {
            print("Error getting documents: \(err)")
        } else {
             userDocuments = querySnapshot!.documents
             completion()
        }
    }
}

func showUsers() {
  // ここに userDocuments を使うコードが書いてある
}

このように書くと completion に

{ [weak self] in
    self?.showUsers()
}

が入っている状態なので completion() のように () を付けて
実行すると showUsers() が実行されます。

getDocuments() には

{ (querySnapshot, err) in
    if let err = err {
        print("Error getting documents: \(err)")
    } else {
         userDocuments = querySnapshot!.documents
         completion()
    }
}

が与えられていて、非同期で実行されますが
丁度似た感じで、無名関数(クロージャー)をつかって completion を表現し
このように関数を切り分けることが出来ます。

Discussion