I am trying to download a video from my firebase storage. The way I am doing that is by using the .downloadURLWithCompletion function. When ever the function executes, I receive this error
Error Domain=FIRStorageErrorDomain Code=-13010 "Object videos/video1.m4v
does not exist." UserInfo={object=videos/video1.m4v
, bucket=**********.appspot.com, ResponseBody={
"error": {
"code": 404,
"message": "Not Found"
}
}, data=<7b0a2020 22657272 6f72223a 207b0a20 20202022 636f6465 223a2034 30342c0a 20202020 226d6573 73616765 223a2022 4e6f7420 466f756e 64220a20 207d0a7d>, NSLocalizedDescription=Object videos/video1.m4v
does not exist., ResponseErrorDomain=com.google.HTTPStatus, ResponseErrorCode=404}
I have changed my storage settings on firebase to allow unauthenticated access:
I have also checked to make sure that the storage link is correct:
Here is the code that is accessing the Firebase storage:
import UIKit
import AVKit
import AVFoundation
import FirebaseStorage
class VideoViewController: UIViewController
{
var videoUrl:NSURL!
var storageRef:FIRStorageReference!
override func viewDidLoad()
{
let storage = FIRStorage.storage()
storageRef = storage.referenceForURL("gs://**********.appspot.com")
let videosRef = storageRef.child("videos")
let videoName = NSUserDefaults.standardUserDefaults().objectForKey("videoName") as! String
videosRef.child(videoName).downloadURLWithCompletion { (URL, error) -> Void in
if (error != nil)
{
print(error!)
}
else
{
self.videoUrl = URL
do
{
try self.playVideo()
}
catch
{
print("Error")
}
}
}
super.viewDidLoad()
// Do any additional setup after loading the view.
}
So, I tried using a direct link and it worked!
override func viewDidLoad()
{
let storage = FIRStorage.storage()
storageRef = "gs://*************.appspot.com"
let videosRef = "videos"
let videoName = NSUserDefaults.standardUserDefaults().objectForKey("videoName") as! String
storage.referenceForURL("\(storageRef)/\(videosRef)/\(videoName)").downloadURLWithCompletion { (URL, error) in
if (error != nil)
{
print(error!)
}
else
{
self.videoUrl = URL
do
{
try self.playVideo()
}
catch
{
print("Error")
}
}
}
Of course, using a direct link for something like this isn't exactly the best way to get data. So next I compared the two links generated by printing them out. Here is how I printed the first link:
var videoUrl:NSURL!
var storageRef:FIRStorageReference!
override func viewDidLoad()
{
let storage = FIRStorage.storage()
storageRef = storage.referenceForURL("gs://*********.appspot.com")
let videosRef = storageRef.child("videos")
let videoName = NSUserDefaults.standardUserDefaults().objectForKey("videoName") as! String
print(videosRef.child(videoName))
and it printed
gs://***********.appspot.com/videos/video1.m4v
And the second link:
var videoUrl:NSURL!
var storageRef:String!
override func viewDidLoad()
{
let storage = FIRStorage.storage()
storageRef = "gs://***********.appspot.com"
let videosRef = "videos"
let videoName = NSUserDefaults.standardUserDefaults().objectForKey("videoName") as! String
print("\(storageRef)/\(videosRef)/\(videoName)")
What it printed
gs://***********.appspot.com/videos/video1.m4v
Now, I also tried printing the value of videoName to make sure that it was correct and every time that I printed it out it was video1.m4v
I banked out the link to my firebase storage, but I can assure you that the link is correct all around.
Can someone explain to me why I am getting this error? To me everything looks to be in place.
Thanks!
Try this -- if there is an issue with the underlying representation of a ref this may help:
instead of:
videosRef.child(videoName).downloadURLWithCompletion { (URL, error) -> Void in
do:
storage.referenceForURL(String(videosRef.child(videoName))).downloadURLWithCompletion { (URL, error) -> Void in
that is, does referenceForURL of the stringValue do something different than a direct call. It shouldn't -- if it does, it might have something to do with your videoName. Maybe it ends with a slash? Can you post the value of your videoName?
So, if I understand correctly, you want to download the image without passing the full URL path?
If so, I think downloadURLWithCompletion requires the full URL path.
I can't test this, since I don't have my data set up this way (I just store the full URLs to media files in firebase storage to my firebase database), but try this:
videosRef.child(videoName).dataWithMaxSize(INT64_MAX, completion: { (data, error) in
if let error = error {
print("Error downloading: \(error)")
return
}
cell.imageView?.image = UIImage.init(data: data!)
})
In your firebase storage, you haven't placed your video file inside a folder called videos.
And despite this you try to access to .../videos/filename which doesn't exist. Either try to remove the /videos from: gs://***********.appspot.com /videos /video1.m4v
or
Either create a folder called videos inside your firebase storage and then add the same video inside it with the same name (since you cant drag and drop files into other folders), or remove the:
let videosRef = "videos"
from your path.
Hope it helps.
Related
This question already has answers here:
Loading/Downloading image from URL on Swift
(39 answers)
Closed 6 months ago.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imageOfDog: UIImageView!
struct dataForLoading: Codable {
let message: String
}
override func viewDidLoad() {
super.viewDidLoad()
// load url
let url = "https://dog.ceo/api/breeds/image/random"
guard let loadUrl = URL(string: url) else { return }
// use loaded url in urlSession
URLSession.shared.dataTask(with: loadUrl) {(data, response, error) in
if error != nil{
print("if error printed")
print(error!.localizedDescription)
}
// decode
guard let data = data else { return }
do {
let jsonData = try JSONDecoder().decode(dataForLoading.self, from: data)
DispatchQueue.main.async {
self.imageOfDog.image = UIImage(named: jsonData.message)
}
}
catch let jsonError {
print(jsonError)
}
}.resume()
}
}
i am currentlt using. https://dog.ceo/api/breeds/image/random. this api
for loading random image
i am new to loading Api i am trying to load API through URLSession
when i run project i get below error
Random dog image[5960:196973] [framework] CUIThemeStore: No theme registered with id=0
i think i am not able to decode it properly how can i load image through API
At First Api Generates an url from image like these. {"message":"https://images.dog.ceo/breeds/elkhound-norwegian/n02091467_5985.jpg","status":"success"}
so my idea is to get first API and in Api whaterver url is coming pass it to imageview
The error occurs cause of UIImage(named: jsonData.message) . You can call this only if the image is exist in Assets Folder. You have to use UIImage(data: data)
Example of usage
if let imageURL = URL(string: jsonData.message){
if let data = try? Data(contentsOf: imageURL){
self.imageOfDog.image = UIImage(data: data)
}
}
I need to display the images that have been stored on the storage on Firebase. Right now, I only tracked the images using the link generated by function downloadURL:
func UploadImage(imageData: Data, path: String, completion: #escaping (String) -> ()){
let storage = Storage.storage().reference()
let uid = Auth.auth().currentUser?.uid
storage.child(path).child(uid ?? "").putData(imageData, metadata: nil) { (_, err) in
if err != nil{
completion("")
return
}
// Downloading Url And Sending Back...
storage.child(path).child(uid ?? "").downloadURL { (url, err) in
if err != nil{
completion("")
return
}
completion("\(url!)")
}
}
}
So all I can get is a hyperlink that is like: https://firebasestorage.googleapis.com/v0/b/getting-started-20f2f.appspot.com/o/profile_Photos%2FGQ1KR9H1mLZl2NAw9KQcRe7d72N2?alt=media&token=473ce86c-52ba-42ec-be71-32cc7dc895d7.
I refer to the official documentation, it seems that only when I have the name of the image file can I download it to an ImageView or UIImageView object. However, the link does not make any sense to me, so what can I do?
EDIT
I actually tried a solution provided by the official documentation:
func imageDownloader(_ imageURL: String) {
let store = Database.database().reference()
let uid = Auth.auth().currentUser?.uid
let imageRef = store.child(imageURL)
var myImageView = UIImageView()
imageRef.getData(completion: { (error, data) in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Data for "images/island.jpg" is returned
let image = UIImage(data: data!)
}
})
}
But it suggests that I need to change something because Cannot convert value of type 'DataSnapshot' to expected argument type 'Data'.
If you're storing the image paths in Firestore, actually the exact file name does not matter if there is only one file available under the fork. So you just need to specify the path.
To then download the image from Storage, construct the path and download:
let uid = Auth.auth().currentUser?.uid
Storage.storage().reference().child("the\path\to\your\uid\collection").child(uid).getData(maxSize: 1048576, completion: { (data, error) in
if let data = data,
let img = UIImage(data: data) {
// do something with your image
} else {
if let error = error {
print(error)
}
// handle errors
}
})
You are uploading to Storage.storage(), but then in your imageDownloader, you're attempting to use Database.database(), which has a similar-looking API, but is, in fact, different.
Make sure to use Storage.storage() and that the closure parameters are in the order data, error in.
Finally, right now in your imageDownloader, it doesn't look like you're doing anything yet with var myImageView = UIImageView(), but keep in mind that you won't have access to the UIImage until the async getData completes.
Store your images at Firebase Storage & then retrieve using this code.
Storage.storage().reference.child("ProfilePhotos").child("ImageName").downloadURL {(url, _) in
DispatchQueue.main.async {
guard let url = url else { return }
imageView.setImage(with: url, placeholder: UIImage(named: "dummyImage"))
}
}
I am trying to build and iOS app with similar behaviour to Pages / Numbers / Keynote. Each of these apps is a Document Based App, where the user is first presented with a UIDocumentBrowserViewController where the user choses a document to open in the app. In Numbers for example a user can select a .numbers file and it will open, or a user can select a .csv and it will import this csv file into a numbers file which is saved along side the original csv in the same location.
In my app I want the user to select a .csv file, and then I'll import it into my own document format (called .pivot) and save this alongside the csv file (just like numbers.) This works fine in the simulator but when I run my code on a device I get an error when calling save(to:for:completionHandler:) on my custom Pivot document.
My document browser code is as follows.
class DocumentBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
allowsDocumentCreation = false
allowsPickingMultipleItems = false
}
func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) {
guard let sourceURL = documentURLs.first else { return }
if sourceURL.pathExtension == "csv" {
// Create a CSV document so we can read the CSV data
let csvDocument = CSVDocument(fileURL: sourceURL)
csvDocument.open { _ in
guard let csv = csvDocument.csvData else {
fatalError("CSV is nil upon open")
}
// Create the file at the same location as the csv, with the same name just a different extension
var pivotURL = sourceURL.deletingLastPathComponent()
let pivotFilename = sourceURL.lastPathComponent .replacingOccurrences(of: "csv", with: "pivot")
pivotURL.appendPathComponent(pivotFilename, isDirectory: false)
let model = PivotModel()
model.csv = csv
let document = PivotDocument(fileURL: pivotURL)
document.model = model
document.save(to: pivotURL, for: .forCreating, completionHandler: { success in
// `success` is false here
DispatchQueue.main.async {
self.performSegue(withIdentifier: "presentPivot", sender: self)
}
})
}
}
}
}
My first UIDocument subclass to load a csv file is as follows.
import SwiftCSV // This is pulled in using SPM and works as I expect, so is unlikely causing this problem
class CSVDocument: UIDocument {
var csvData: CSV?
override func contents(forType typeName: String) throws -> Any {
return Data()
}
override func load(fromContents contents: Any, ofType typeName: String?) throws {
guard let data = contents as? Data else {
fatalError("No file data")
}
guard let string = String(data: data, encoding: .utf8) else {
fatalError("Cannot load data into string")
}
csvData = try CSV(string: string)
}
}
My second UIDocument subclass for my custom Pivot document is as follows. By overriding the handleError() function I can see the save fails with an error in the NSCocoaErrorDomain, with code of 513.
class PivotDocument: UIDocument {
var model: PivotModel!
var url: URL!
override func contents(forType typeName: String) throws -> Any {
let encoder = JSONEncoder()
return try encoder.encode(model)
}
override func load(fromContents contents: Any, ofType typeName: String?) throws {
guard let data = contents as? Data else {
fatalError("File contents are not Data")
}
let decoder = JSONDecoder()
model = try decoder.decode(PivotModel.self, from: data)
}
override func handleError(_ error: Error, userInteractionPermitted: Bool) {
let theError = error as NSError
print("\(theError.code)") // 513
print("\(theError.domain)") // NSCocoaErrorDomain
print("\(theError.localizedDescription)") // “example.pivot” couldn’t be moved because you don’t have permission to access “CSVs”.
super.handleError(error, userInteractionPermitted: userInteractionPermitted)
}
}
The fact that this works in the simulator (where my user has access to all the file system) but doesn't on iOS (where user and app permissions are different) makes me think I have a permission problem. Do I need to declare some entitlements in my Xcode project for example?
Or am I just misusing the UIDocument API and do I need to find a different implementation?
I found the function I was looking for that replicates the functionality of the iWork apps!
UIDocumentBrowserViewController has this function importDocument(at:nextToDocumentAt:mode:completionHandler:). From the docs:
Use this method to import a document into the same file provider and directory as an existing document.
For example, to duplicate a document that's already managed by a file provider:
Create a duplicate of the original file in the user's temporary directory. Be sure to give it a unique name.
Call importDocument(at:nextToDocumentAt:mode:completionHandler:), passing in the temporary file's URL as the documentURL parameter and the original file's URL as the neighborURL parameter.
So documentBrowser(_:didPickDocumentsAt:) is now:
let pivotFilename = sourceURL.lastPathComponent .replacingOccurrences(of: "csv", with: "pivot")
let path = FileManager.default.temporaryDirectory.appendingPathComponent(pivotFilename)
if FileManager.default.createFile(atPath: path.path, contents: nil, attributes: nil) {
self.importDocument(at: path, nextToDocumentAt: sourceURL, mode: .copy) { (importedURL, errorOrNil) in
guard let pivotURL = importedURL else {
fatalError("No URL for imported document. Error: \n \(errorOrNil?.localizedDescription ?? "NO ERROR")")
}
let model = PivotModel()
model.csv = csv
let document = PivotDocument(fileURL: pivotURL)
document.model = model
DispatchQueue.main.async {
self.performSegue(withIdentifier: "presentPivot", sender: self)
}
}
}
else {
fatalError("Could not create local pivot file in temp dir")
}
No more permissions errors. Hope this helps someone else in the future.
Currently, I can fetch the download url by file name via firebase storage reference.
I would like to retrieve all download URLS in a specific child without using a file name and only using the last child name.
Simply adding every download url in a list/array
How can I accomplish this with my given reference.
func getDownloadURL() {
let ref = Storage.storage().reference()
let fileName = "Lessons_Lesson1_Class1.mp3"
let starsRef = ref.child("Daily Meditations").child("Lessons").child("Lesson 1").child(fileName)
// Fetch the download URL
starsRef.downloadURL { url, error in
if let error = error {
// Handle any errors
print(error)
} else {
// Get the download URL for 'Lessons_Lesson1_Class1.mp3'
print(url)
}
}
}
Firebase Refrence Docs
let stg = Storage.storage().reference()
let path = "Daily Meditations/Lessons/Lesson 1"
stg.child(path).listAll { (list, error) in
if let error = error {
print(error)
} else {
let inStorage = list.items.map({ $0.name })
print(inStorage) // an array of file names in string format
}
}
I assume spaces are allowed in path names since you're using them. To list all of the files in a path, use listAll. The method will return a StorageListResult object which I've named list.
https://firebase.google.com/docs/reference/swift/firebasestorage/api/reference/Classes/StorageListResult
So I was able to combine List all files and Download URL to achieve what I was trying to accomplish from the firebase documentation.
Here is the code:
func getDownloadURl() {
let ref = Storage.storage().reference()
let storageReference = ref.child("Lessons/Lesson 1")
storageReference.listAll { (result, error) in
if let error = error {
print(error)
}
for item in result.items {
//List storage reference
let storageLocation = String(describing: item)
let gsReference = Storage.storage().reference(forURL: storageLocation)
// Fetch the download URL
gsReference.downloadURL { url, error in
if let error = error {
// Handle any errors
print(error)
} else {
// Get the download URL for each item storage location
print(url!)
}
}
}
}
}
If anyone is using with VUE 2.6 and TS, here is my workaround
Imports
import {
getStorage,
ref,
getDownloadURL,
listAll,
StorageReference,
} from "firebase/storage";
async mounted(): Promise<void> {
const storage = getStorage();
const imageRefs = await listAll(ref(storage, "SOME BUCKET"))
.then((refs) => {
return refs.items;
})
.catch((error) => {
// Handle any errors
});
(imageRefs as StorageReference[]).forEach((item) => {
console.log(item);
getDownloadURL(item).then((downloadURL) => {
console.log(downloadURL);
this.model.listFiles.push(downloadURL);
});
});
console.log(imageRefs);
console.log(this.model.listFiles);
},
I'm trying to download a file with SwiftyDropbox but I have problemas with the path. I have a file in mi Dropbox "prueba.txt":
Dropbox file
And this is the code that I use to download in my app.
import UIKit
import SwiftyDropbox
let clientDB = DropboxClientsManager.authorizedClient
class Controller: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
DropboxClientsManager.authorizeFromController(UIApplication.shared, controller: self, openURL: {
(url: URL) -> Void in UIApplication.shared.open(url)
})
let fileManager = FileManager.default
let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destURL = directoryURL.appendingPathComponent("/test.txt")
let destination: (URL, HTTPURLResponse) -> URL = { temporaryURL, response in
return destURL
}
clientDB?.files.download(path: "/prueba.txt", overwrite: true, destination: destination)
.response{ response, error in
if response != nil{
self.cargarDatosCliente()
//print (response)
} else if let error = error{
print (error)
}
}
.progress{ progressData in
print(progressData)
}
}
}
I try different ways but always obtain the same problem with "path", always the error is path/not_found/...
I try with other path but is the same problem.
Could you help me? Where is my mistake?
Thanks!
The problem is that "/prueba.txt" is a local file path. Dropbox expects you to give it a file path for their remote server.
You can retrieve those by using listFolder and listFolderContinue.
For example, if you want to retrieve the file paths in the root folder of your app or dropbox use:
var path = ""
clientDB?.files.listFolder(path: path).response(completionHandler: { response, error in
if let response = response {
let fileMetadata = response.entries
if response.hasMore {
// Store results found so far
// If there are more entries, you can use `listFolderContinue` to retrieve the rest.
} else {
// You have all information. You can use it to download files.
}
} else if let error = error {
// Handle errors
}
})
The fileMetadata contains the path you need. For example, you can get the path to the first file like this:
let path = fileMetadata[0].pathDisplay
If you're getting metadata about files from the API, this would be the "pathLower" property of a FileMetadata object.
client?.files.download(path: fileMetadata.pathLower!, overwrite: true, destination: destination)
.response { response, error in
if let response = response {
print(response)
} else if let error = error {
print(error)
}
}