Swiftのネストを浅くしたい

投稿者: Anonymous

以下のSwiftコードのネストを浅くしたいのですが、いい案はないでしょうか?

task = URLSession.shared.dataTask(with: URL(string: url)!) { (data, res, err) in
    if let obj = try! JSONSerialization.jsonObject(with: data!) as? [String: Any] {
        if let items = obj["items"] as? [[String: Any]] {
            self.repo = items
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }
}

解決

はじめまして、URLSessionで非同期なので、タイミングによってはselfが解放されている可能性まで考えてのコードです。
guard文の多用になってあまりすっきりはしていないかも知れませんが、あえて guard let a = b, let c = dの様な一つのguard文で複数のチェックを行う事は今回は避けています。
エラー処理の構文まで含めると1ネスト減らすことが出来ていて、
ロジック自体は2ネスト減らすことができそうです。

guard let url: URL = URL(string: url) else {
    print("URL not constructed")
    return
}
task = URLSession.shared.dataTask(with: url) { [weak self] (data, res, err) in
    guard let weakSelf = self else {
        print("self already deallocated")
        return
    }
    guard let jsonData: Data = data else {
        print("received data not exist"
        return
    }
    guard let obj = try? JSONSerialization.jsonObject(with: data!) as? [String: Any] {
        print("JSON Serialization failed")
        return
    }
    guard let items = obj["items"] as? [[String: Any]] else {
        print("items is not much type")
        return
    }
    weakSelf.repo = items
    DispatchQueue.main.async { [weak self] in
        guard let weakSelf = self else {
            print("self is already deallocated")
            return
        }
        weakSelf.tableView.reloadData()
    }
}

try!は続くメソッドや関数が例外を発生させた時にエラーでアプリケーションそのものが終了してしまうため、try?というエラーが起きた時はnilを返すtryに書き替えてあります。
これにより、objというローカル変数はそれ以降必ずあることが保証されるので、guard文によるアンラップチェックを多用してデーターが十全であることをアンラップが必要な度にしつこいほど確認・保証する作りになっています。これによりなにをアンラップした時にエラーになったか?、が続く処理ですぐにかけるので、エラーの発生箇所と対処箇所がすぐ近くに書けるのもメリットの一つだと思います

また、URLSessionにデーターをリクエストしているので、指定したURLからデーターが帰ってきた時には、呼び出したクラスが解放しようとした後かも知れない場合も考慮し、クロージャーの先頭に
[weak self] (引数があればここに書く) inという「このブロックの中ではselfを弱参照してメモリーリークを防ぐ」と共に、
guard let weakSelf = self else { return }
という一文で弱参照しているselfが解放されていない(nilでない)ことも確認しています。

コード全体のネストは一段しか減らせていないですが、オプショナル型のアンラップ時のnilによるアプリのクラッシュを可能な限り防ぐ方向に振っています。

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *