firebase-ios-sdk icon indicating copy to clipboard operation
firebase-ios-sdk copied to clipboard

FirebaseStorage - reference(forUrl: URL) Invalid url format

Open willshelley opened this issue 3 years ago • 2 comments

[REQUIRED] Step 1: Describe your environment

  • Xcode version: 14 beta
  • Firebase SDK version: 9.2
  • Installation method: Swift Package Manager
  • Firebase Component: Storage
  • Target platform(s): iOS 16 beta only

[REQUIRED] Step 2: Describe the problem

After fetching the downloadUrl from a storageRef i'm receiving a correctly formatted download URL with %2F for the path parameters. When fetching the storageRef for this url using Storage.storage().reference(forUrl: URL). The returned storageRef does not properly decode the path where the object is stored in Storage i.e. gs://someapp.appspot.com/images%2FsomeImage, Expecting gs://someapp.appspot.com/images/someImage. I'm then receiving a correct permissions error as this is not the correct path to the object.

This is only occurring when testing a real device with iOS16 beta. All other versions seem to be working.

Current workaround is to use: url.removingPercentEncoding

let downloadUrl = try await storageRef.downloadURL().absoluteString

let storageRef = db.reference(forURL: url)
let data = try await storageRef.data(maxSize: 10485760)
// Correct permissions error for trying to access a object that doesn't exist - gs://someapp.appspot.com/images%2FsomeImage

let storageRef = db.reference(forURL: url.removingPercentEncoding!)
let data = try await storageRef.data(maxSize: 10485760)
// This produces expected result - gs://someapp.appspot.com/images/someImage

willshelley avatar Jul 14 '22 14:07 willshelley

Thanks for pointing this out! I'm actually able to reproduce what you're seeing both on the iOS 16 simulator and a real device. I've traced it down to a change in URL.pathComponents's behaviour in iOS 16.

Example

let url = URL(string: "https://example.com/images%2Fexample.png")!
print("URL Components: \(url.pathComponents)")
  • iOS 15.5: URL Components: ["/", "images", "example.png"]
  • iOS 16.0 Beta 3: URL Components: ["/", "images%2Fexample.png"]

The documentation for NSURL states:

This property contains an array containing the individual path components of the URL, each unescaped using the replacingPercentEscapes(using:) method. For example, in the URL file:///directory/directory%202/file, the path components array would be @[@"/", @"directory", @"directory 2", @"file"].

In the beta documentation, NSURL is marked as modified. The method replacingPercentEscapes(using:) is also marked as deprecated.

Unfortunately I don't have a fix quite yet since I'm not sure if the functionality change is intended or a bug in the beta. I also want to be sure that there aren't any edge cases when using removingPercentEncoding, e.g., if a file has spaces in the filename.

andrewheard avatar Jul 15 '22 00:07 andrewheard

Cheers Andrew, thanks the detailed response. Appreciate that beta versions always have some foibles. For the time being we have a solution so for us it's not a priority. If we see any edge cases, we'll let you know!

willshelley avatar Jul 15 '22 08:07 willshelley

Marking to check again with the Xcode 14 GA (or later beta) in advance of Firebase 10.

paulb777 avatar Aug 19 '22 21:08 paulb777

Good news! This seems to be resolved in the GM release of iOS 16.

let url = URL(string: "https://example.com/images%2Fexample%20image.png")!
print("URL Components: \(url.pathComponents)")
  • iOS 16.0 Beta 3/5: URL Components: ["/", "images%2Fexample.png"]
  • iOS 16.0 GM: URL Components: ["/", "images", "example image.png"]

I tried a scenario similar to the one you posted using Storage as well:

let storage = Storage.storage()
let storageRef: StorageReference = storage.reference(forURL: "gs://someapp.appspot.com/images/Built with Firebase.png")
print("Storage Ref: \(storageRef)")
let downloadUrl = try await storageRef.downloadURL()
print("Download URL: \(downloadUrl.absoluteString)")
let downloadRef = storage.reference(forURL: downloadUrl.absoluteString)
print("Download Ref: \(downloadRef)")
let data = try await downloadRef.data(maxSize: 15000)
print("Image Size: \(data.count)")

Prints:

Storage Ref: gs://someapp.appspot.com/images/Built with Firebase.png
Download URL: https://firebasestorage.googleapis.com:443/v0/b/someapp.appspot.com/o/images%2FBuilt%20with%20Firebase.png?alt=media&token=39915302-a3ca-48a2-9ef6-c5c4187994b8
Download Ref: gs://someapp.appspot.com/images/Built with Firebase.png
Image Size: 13059

As a sanity check, I got the same results in a simulator and on a real device. Closing since this seems fixed but let us know if you see different results.

andrewheard avatar Sep 16 '22 22:09 andrewheard