
[code]

    class ViewController: UIViewController {
        //.....
        var friendId: String!
        userID = "098752"
        let Path = ref.child("user/\(userID)/friend/userId")
           Path.observeSingleEvent(of: .value, with: { (snapshot) in
               self.friendId = snapshot.value as? String
               print(self.friendId!)  //ここでは正しく表示される
           }) { (error) in
               print(error.localizedDescription)
           }
       print(self.friendId)  //ここでは値がうまくセット出来てない
    }
    
[/code]

この処理でデータの取得まではできているんですが、（print(self.friendId!)はコンソール上に正しく表示されます）この処理のあと、self.guideIdを呼び出すことができません。

この取得したself.friendIdを別のデータ取得の際のパスの指定に使いたいのですが↓

[code]

     let namePath = ref.child("user/\(self.friendId)/name")
        namePath.observeSingleEvent(of: .value, with: { (snapshot) in
            self.friendNameLabel.text = snapshot.value as? String
        }) { (error) in
            print(error.localizedDescription)
        }
    
[/code]

何が問題かわかる方いらっしゃいましたら教えていただきたいです。。。  
swiftを初めて間もないので、すごいくだらない質問になっていたらすみません、、！


CC BY-SA 4.0
2018-10-08T19:37:03.160
49068
2018-10-09T20:37:41.813
2018-10-09T18:14:15.753
30424
30424
question
swift4 firebase
Firebaseから取得したデータをクラス内のプロパティーにセットしたい


非同期メソッドから取得した結果の使い方が間違っています。

あなたのコード中のこの部分:

[code]

    ...     { (snapshot) in
        self.friendId = snapshot.value as? String
        print(self.friendId!)
    }
    
[/code]

これは`observeSingleEvent(of:with:withCancel:)`メソッドに「処理が完了したらこれを実行してくれ」と言う意味で渡されるだけですぐには実行されず、メソッドは一旦終了し、次の行の実行が始まります。

その時点では、上記のクロージャは実行されていないのですから、`print(self.friendId)`としても、値はセットされていない方が普通です。

**非同期メソッドから取得した値は、必ず完了ハンドラの中で使う** と言うのがセオリーです。

[code]

    ...     { (snapshot) in
        self.friendId = snapshot.value as? String
        print(self.friendId!)
        //`self.friendId`は、必ずこの中で使う
        //....
        //(長くなるなら別のメソッドを作って、それを呼んでも良い)
    }
    
[/code]

iOSでは、Firebaseに限らず非同期処理が非常に多用されますから、このセオリーを覚えておかないと、まともに動くアプリが作れないことになります。Firebaseに限らなければ、実は同じ非同期処理が原因でうまくいかないと言う事例はここ日本語版スタック・オーバーフローにもいくつも見つかります。

* * *

ご質問が編集されましたので、もう少し追記。

取得した`self.friendId`は「別のデータ取得の際のパスの指定に使いたい」と言うことのようですが、「長くなる」と言うほどの長さではないので、「別のデータ取得」の処理全体を完了ハンドラの中に書いてやればいいでしょう。

ご質問のタイトルにあるような「プロパティにセット」する必要さえないように思われます。

[code]

        let path = ref.child("user/\(userID)/friend/userId")
        path.observeSingleEvent(of: .value, with: { (snapshot) in
            guard let friendId = snapshot.value as? String else {
                print("friendId is not a String")
                return
            }
            print(friendId)  //ここでは正しく表示される(はず)
            //### 非同期メソッドから取得した値は、必ず完了ハンドラの中で使う
            let namePath = ref.child("user/\(friendId)/name")
            namePath.observeSingleEvent(of: .value, with: { (snapshot) in
                guard let friendName = snapshot.value as? String else {
                    print("friendName is not a String")
                    return
                }
                //### UI部品の更新は必ずメインスレッド(UIスレッド)内で行う
                DispatchQueue.main.async {
                    self.friendNameLabel.text = friendName
                }
            }) { (error) in
                print(error.localizedDescription)
            }
        }) { (error) in
            print(error.localizedDescription)
        }
    
[/code]

正しく動くアプリを作るために大変重要なことから、みんなが守っている習慣までいろんなレベルのものがごちゃ混ぜですが、いくつか気付いたことを。

  * Swiftでは大文字始まりの識別子を使うのは型名だけです  
経験のあるSwiftプログラマの間では大変よく守られているルールです。逆に言うと、これを守っていないコードを見かけたら、それだけで信頼できるコードかどうか疑ってみた方がいいでしょう。あなたの`Path`は`path`に書き換えさせてもらいました。

  * `snapshot.value as? String`の結果は`Optional<String>`型になります  
`Optional`のまま続きの処理を行うと色々弊害がありますから、`guard`文なんかを使って、早めに安全な方法でアンラップしておきましょう。

なお、元コードでは`var friendId: String!`のようにimplicitly unwrapped
`Optional`型を使っておられますが、 **implicitly
unwrapped`Optional`型の使用は可能な限り避けるべき**です。今回のように`nil`になりうるなら明示的な`Optional`型(`String?`型)を使うべきです。  
例えば追記部にあるコード `"user/\(self.friendId)/name"`
は、`self.friendId`が`Optional`型の場合、変な結果になります。(今のSwiftだと警告が出るので気付くはずですが。)

  * **UI部品の更新は必ずメインスレッド(UIスレッド)内で行う**  
これはiOSプログラミングの基本ですが、今回のような非同期メソッドの完了ハンドラはメインスレッド以外から呼ばれるのが普通です。その中からUI部品の更新を行う場合には、必ずそこを`DispatchQueue.main.async
{...}`で囲んでやってください。

(ちなみに`friendNameLabel`は`UILabel`でしょうが、それもどこにも書いてくれていませんね…。)

実際のコードはもう少し複雑なのかもしれませんが、とりあえずご質問内容だけから分かる範囲でまとめるとこんな感じになります。何かあればご質問内容を再度編集するか、コメントをつけるかしておしらせください。


CC BY-SA 4.0
2018-10-08T20:38:51.533
49069
2018-10-09T20:37:41.813
2018-10-09T20:37:41.813
13972
13972
49068
answer