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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Google Cloud Storage

Google Cloud Storageは、グーグル社が提供しているクラウドベースのデベロッパー・企業向けストレージサービス。可用性に優れ、APIで操作可能なため、データのアーカイブ保存やアプリケーションのコンテンツ提供など様々な用途に活用できます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

3079閲覧

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

tomoya_ios

総合スコア6

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Google Cloud Storage

Google Cloud Storageは、グーグル社が提供しているクラウドベースのデベロッパー・企業向けストレージサービス。可用性に優れ、APIで操作可能なため、データのアーカイブ保存やアプリケーションのコンテンツ提供など様々な用途に活用できます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2019/09/07 07:55

編集2019/09/07 09:11

前提・実現したいこと

【前提】
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

1 2Firestore 3 -- channels 4 -- channelID 5 -- channelID 6 --channelName 7 --messages 8 --messageId 9 --messageId 10 -- senderName 11 -- senderId 12 -- imageURL "https://firestorage...../image.jpeg" 13

CloudStorage

1CloudStorage 2 --image 3 --UUID 4 --image1 5 --image2

エラーメッセージ

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

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

Swift

1func saveImage(_ image: UIImage, complition: @escaping (URL) -> ()) { 2 guard let data = image.jpegData(compressionQuality: 0.5) else { return } 3 let metaData = StorageMetadata() 4 metaData.contentType = "image/jpeg" 5 6 let ref = Storage.storage() 7 .reference(withPath: "image") 8 .child([UUID().uuidString, "(Date().timeIntervalSince1970)"].joined(separator: "/")) 9 10 ref.putData(data, metadata: metaData) { (metaData, error) in 11 if error != nil { 12 return 13 } 14 15 ref.downloadURL { (url, error) in 16 print(url) 17 // この時点でURLの頭が http:/firebase....という不自然な形をしています 18 guard let url = url else { return } 19 if error != nil { 20 return 21 } 22 23 complition(url) 24 } 25 } 26 }

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

Swift

1func saveMessage(_ url: URL) { 2 ref.addDocument(data: ["senderID": sender.senderId, 3 "senderName": sender.displayName, 4 "sentDate": Date.timeIntervalBetween1970AndReferenceDate, 5 "content": "", 6 "imageURL": url.absoluteString]) { [weak self] error in 7 guard let self = self else { return } 8 if error != nil { 9 //print("error: (error!.localizedDescription)") 10 return 11 } 12 self.view?.scrollToBottom() 13 } 14 } 15 16 func updateMessages() { 17 ref.addSnapshotListener { snapshot, error in 18 if error != nil { 19 //print("error: (error!.localizedDescription)") 20 return 21 } 22 23 snapshot?.documentChanges.forEach { [weak self] change in 24 guard let self = self else { return } 25 self.handleDocumentChange(change) 26 } 27 } 28 } 29 30 func handleDocumentChange(_ change: DocumentChange) { 31 switch change.type { 32 case .added: 33 self.judgeMessageType(change: change) { result in 34 switch result { 35 case .text: 36 let message = Message(text: change.document.data()["content"] as! String, 37 sender: sender, 38 messageId: "", 39 sentDate: Date()) 40 self.messages.append(message) 41 case .photo: 42 guard let url = (change.document.data()["imageURL"] as! String).toURL() else { return } 43 print(url) 44 let message = Message(image: getImage(url: url), 45 sender: sender, 46 messageId: "", 47 sentDate: Date()) 48 49 self.messages.append(message) 50 51 default: 52 break 53 } 54 } 55 self.view?.reloadData() 56 default: 57 break 58 } 59 } 60 61 func judgeMessageType(change: DocumentChange, complition: (MessageObjectType) -> ()) { 62 print("judgeMessageType") 63 let document = change.document 64 65 if document.data()["content"] as? String != "" { 66 complition(.text) 67 } else if document.data()["imageURL"] as? String != "" { 68 complition(.photo) 69 } 70 } 71func getImage(url: URL) -> UIImage { 72 let ref = Storage.storage().reference(forURL: url.absoluteString) 73// アプリがクラッシュした際にここで止まります。 74 var image: UIImage! 75 76 ref.getData(maxSize: 1 * 1024 * 1024) { (data, error) in 77 guard let data = data else { return } 78 print("data not nil") 79 if error != nil { 80 print(error!.localizedDescription) 81 return 82 } 83 84 image = UIImage(data: data) 85 } 86 87 return image 88 }

試したこと

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

Swift

1let 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") 2 var image: UIImage! 3 4 ref.getData(maxSize: 1 * 1024 * 1024) { (data, error) in 5 guard let data = data else { return } 6 print("data not nil") 7 if error != nil { 8 print(error!.localizedDescription) 9 return 10 } 11 12 image = UIImage(data: data) 13 } 14 15 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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

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

投稿2019/09/07 09:13

tomoya_ios

総合スコア6

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問