質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.62%

【Swift】【Firebase】CloudStorageが返すdownloadURLと取得したURLが一致しない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 752

tomoya_ios

score 6

前提・実現したいこと

【前提】
MessageKitフレームワークとFirebaseを使用したメッセージアプリを作成しています。

Cloud Storageから画像URLを取得し、画像を表示したいです。
しかし、DBに保存した画像URLと取得したURLが一致しません。

cloudstorageが返すdownloadURLをそのままfirestoreに保存しているのに、URLの先頭が変化してしまう原因がわからず詰まってしまいました。

処理の順番は以下の通りです。

  1. ユーザーが画像を選択する
  2. saveImageメソッドで選択した画像をCloudStorageに保存する
  3. saveMessageメソッドで上記のURLをFireStore内のメッセージデータのimageURLプロパティに保存する。
  4. Snapchatでチャット内のメッセージを変更を監視して、画像メッセージの場合は画像メッセージオブジェクトを生成する
  5. 3の時にgetImage(url: URL)を呼び出す。引数には<imageURLプロパティの値>

----------------以下は参考までにFirebaseのDBの構成です---------------------

Firestore
   -- channels
      -- channelID
      -- channelID
              --channelName
              --messages
                     --messageId
                     --messageId
                             -- senderName
                             -- senderId
                             -- imageURL "https://firestorage...../image.jpeg"
CloudStorage
  --image
    --UUID
     --image1
     --image2

エラーメッセージ

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'URL scheme must be one of gs://, http://, or https:// '
該当のソースコード

関連するソースコード全体を添付しますが、 下記のコードが もっとも最初に不自然なURLが見つかった箇所です。

func saveImage(_ image: UIImage, complition: @escaping (URL) -> ()) {
        guard let data = image.jpegData(compressionQuality: 0.5) else { return }
        let metaData = StorageMetadata()
        metaData.contentType = "image/jpeg"

        let ref = Storage.storage()
            .reference(withPath: "image")
            .child([UUID().uuidString, "\(Date().timeIntervalSince1970)"].joined(separator: "/"))

        ref.putData(data, metadata: metaData) { (metaData, error) in
            if error != nil {
                return
            }

            ref.downloadURL { (url, error) in
                print(url)
                // この時点でURLの頭が http:/firebase....という不自然な形をしています
                guard let url = url else { return }
                if error != nil {
                    return
                }

                complition(url)
            }
        }
    }

以下に関連するメソッドを添付します。

func saveMessage(_ url: URL) {
        ref.addDocument(data: ["senderID": sender.senderId,
                               "senderName": sender.displayName,
                               "sentDate": Date.timeIntervalBetween1970AndReferenceDate,
                               "content": "",
                               "imageURL": url.absoluteString]) { [weak self] error in
                                guard let self = self else { return }
                                if error != nil {
                                    //print("error: \(error!.localizedDescription)")
                                    return
                                }
                                self.view?.scrollToBottom()
        }
    }

    func updateMessages() {
        ref.addSnapshotListener { snapshot, error in
            if error != nil {
                //print("error: \(error!.localizedDescription)")
                return
            }

            snapshot?.documentChanges.forEach { [weak self] change in
                guard let self = self else { return }
                self.handleDocumentChange(change)
            }
        }
    }

    func handleDocumentChange(_ change: DocumentChange) {
        switch change.type {
        case .added:
            self.judgeMessageType(change: change) { result in
                switch result {
                case .text:
                    let message = Message(text: change.document.data()["content"] as! String,
                                          sender: sender,
                                          messageId: "",
                                          sentDate: Date())
                    self.messages.append(message)
                case .photo:
                    guard let url = (change.document.data()["imageURL"] as! String).toURL() else { return }
                    print(url)
                    let message = Message(image: getImage(url: url),
                                          sender: sender,
                                          messageId: "",
                                          sentDate: Date())

                    self.messages.append(message)

                default:
                    break
                }
            }
            self.view?.reloadData()
        default:
            break
        }
    }

    func judgeMessageType(change: DocumentChange, complition: (MessageObjectType) -> ()) {
        print("judgeMessageType")
        let document = change.document

        if document.data()["content"] as? String != "" {
           complition(.text)
        } else if document.data()["imageURL"] as? String != "" {
           complition(.photo)
        }
    }
func getImage(url: URL) -> UIImage {
        let ref = Storage.storage().reference(forURL: url.absoluteString)
// アプリがクラッシュした際にここで止まります。
        var image: UIImage!

        ref.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
            guard let data = data else { return }
            print("data not nil")
            if error != nil {
                print(error!.localizedDescription)
                return
            }

            image = UIImage(data: data)
        }

        return image
    }

試したこと

問題は解決できませんでした。
この時はreturn imageの箇所で unexpectedly found nil が出てアプリがクラッシュします。
・URLをハードコーディング

let ref = Storage.storage().reference(forURL: "https://firebasestorage.googleapis.com/v0/b/messageapp-ba61a.appspot.com/o/image%2F705215B9-27E7-4DB4-8C42-63CE73CFDF32%2F1567562962.604121?alt=media&token=d55cdba7-77d9-4451-be05-f5c23da6e773")
        var image: UIImage!

        ref.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
            guard let data = data else { return }
            print("data not nil")
            if error != nil {
                print(error!.localizedDescription)
                return
            }

            image = UIImage(data: data)
        }

        return image

怪しいと思う部分

・ ref.putData をした際に、コールバックで帰ってくる metaData を特に活用していない。
→参考にした記事では metaData?.downloadURL としていたが、無くなったらしいので ref.downloadURL で代用したつもりです。
・saveMessageで画像URLをFirestoreに保存した際に、URLの末尾に =media&token=..... のようなパラメータ?のようなものが付随している。
・そもそもFirestoreに保存した時は https:// なのにそのプロパティの値を取得した際に https:/ となっているのだから、エスケープしないといけない?
でも一々それを行わなければいけない筈はないし・・

補足情報(FW/ツールのバージョンなど)

Swift4.2
XCode10

追記:
参考URL
画像を取得する処理は以下を参考にしました。
https://www.raywenderlich.com/5359-firebase-tutorial-real-time-chat
以下のようにURLをオプショナルで保存しているかどうかも確認しましたが、firestoreで確認してもオプショナルではありませんでした。
https://www.letsbuildthatapp.com/course_video?id=1052

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

こちらですがString型で保存していたURLをURL型に変換する処理の部分で間違えていました。
自分が使用していたのは URL(fileURLWithPath: String) でしたが、実際には URL(string: String) が正しかったです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る