Okay, so basically I'm creating a messenger app which I want to be able to upload and load a profile picture. Currently I can successfully upload a picture to firebase, but as stated in the title, it fails to download the picture URL.
(Tutorial that I am watching: https://www.youtube.com/watch?v=Hmr8PsG9E2w&list=PL5PR3UyfTWvdlk-Qi-dPtJmjTj-2YIMMf&index=18&ab_channel=iOSAcademy) 15-20 min into the video.
Where the error occurs:
strongSelf.storage.child("images/"+fileName).downloadURL(completion: {url, error in
guard let url = url else {
print("Failed to get download url")
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
let safeEmail = DatabaseManager.safeEmail(emailAddress: email)
let filename = safeEmail + "_profile_picture.png"
let path = "images/"+filename
Error message in console
What I've tried:
Changing and double checking that the path leads to the picture.
Trying different tactics of calling path like ("(path)") instead of (path)
Other random stuff that I found looking for this problem on the web
What my conclusion so far is that the program does not seem to indentify the correct path to the picture, but I've made sure that it looks identical to the video.
Would be awesome if anyone knows or has any idea how to deal with this issue? This is my first post so please tell me if more information is needed regarding the code is needed.
Cheers // Jakob
Adding extra to comments:
So the (error) is : failedToGetDownloadUrl
So this is where I suspect there is something going wrong:
strongSelf.storage.child("images/\(fileName)").downloadURL(completion: { url, error in
guard let url = url else {
print("Failed to get download url")
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
let urlString = url.absoluteString
print("download url returned: \(urlString)")
completion(.success(urlString))
})
})
}
And here are the Path to the images:
let safeEmail = DatabaseManager.safeEmail(emailAddress: email)
let filename = safeEmail + "_profile_picture.png"
let path = "images"+filename
And this is my firebase storage:
Related
I am using firebase dynamic urls to shorten my url. I read the whole documentation and i dont understand why the code is not working.
here is my code:
let link = "https://mywebsite.com/share/?shop="+concatenate(getKey!,"&title="+shopName.text!.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!,"&desc="+summary.text!.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!,"&img="+profileImgURL)
linkBuilder.shorten() { link, warnings, error in // i get an error here sayiny "cannot find linkBuilder in scope"
guard let link = link, error != nil else { return }
print("The short URL is: \(link)")
}
what am i missing or doing wrong?
You forgot to define what linkBuilder is. Put this after let link =:
let dynamicLinksDomainURIPrefix = "https://example.com/link"
let linkBuilder = DynamicLinkComponents(link: link, domainURIPrefix: dynamicLinksDomainURIPRefix)
Documentation
I am trying to download a plist file from a remote location and use it in the iOS app I am creating. The file is going to be used for calendar details within the app's calendar. The goal is obviously that I can update the remote file instead of having to push updates to the app itself every time we need to make changes to calendar details.
I started with the code used in this example: Download File From A Remote URL
Here is my modified version:
// Create destination URL
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("2017.plist")
//let destinationFileUrl = URL(string: Bundle.main.path(forResource: String(currentYear), ofType: "plist")!)
//Create URL to the source file you want to download
let fileURL = URL(string: "https://drive.google.com/open?id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.moveItem(at: tempLocalUrl, to: destinationFileUrl)
print("File was replaced")
print(NSArray(contentsOf: tempLocalUrl))
//print(tempLocalUrl)
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
}
task.resume()
I originally tried to overwrite the file that is bundled with the app to being with, that resulted in errors. So I instead tried to just save it in the app's documents folder and that removed that error. I had to make sure and remove any previous version of the file because it was giving me a file already exists error after the first run.
While it says everything is working (The outputs for both successful download and replaced file happen) when I print the contents of the array from the downloaded URL it just gives me nil.
This is my first attempt to use any kind of external resources in an app. Before I have always kept everything internal, so I am sure there is something glaringly obvious I am missing.
Update 1:
I realized I didn't have the correct URL to use to download a file from a Google drive. That line of code has been changed to:
let fileURL = URL(string: "https://drive.google.com/uc?export=download&id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
So now I actually am downloading the plist like I originally thought I was. Even removing the deletion issue mentioned in the first comment, I still can't get the downloaded file to actually replace the existing one.
Update 2:
I have reduced the actual file manipulation down to the following:
do {
try FileManager.default.replaceItemAt(destinationFileUrl, withItemAt: tempLocalUrl)
print("File was replaced")
print(NSArray(contentsOf: destinationFileUrl))
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
After the replacement is performed the output of the file shows the correct new contents that were downloaded from the internet.
Later in the code when I try and access the file it seems to be nil in content again.
Look at your download completion code. You:
Delete the file at the destination URL (in case there was one
leftover)
MOVE the temp file to the destination URL (removing it from the temp
URL)
Try to load the file from the temp URL.
What's wrong with this picture?
You are trying to get the contents of the moved file. You already moved the file to destination url and then you are trying to get the contents of the file from temporary location.
For getting file data, Please try the following :
let fileData = try! String(contentsOf: destinationFileUrl, encoding: String.Encoding.utf8)
print(fileData)
So the following code is being used to attach an image from local storage url of an image. I check in Terminal to see if the image is stored and it does store the image without any issues. So ruling out any issues with the url itself.
do {
let attachment = try UNNotificationAttachment(identifier: imageTag, url: url, options: nil)
content.attachments = [attachment]
} catch {
print("The attachment was not loaded.")
}
Other code that goes with the creation of UserNotification works fine as it triggers at the correct specified time.
The code always goes to the catch block. Can anybody please point me to the mistake if there is any in the implementation. Please help. Thank you.
Edit: with print(error.localizedDescription) error message is Invalid attachment file URL.
Edit2 : with print(error) error message is Error Domain=UNErrorDomain Code=100 "Invalid attachment file URL" UserInfo={NSLocalizedDescription=Invalid attachment file URL}
I have found the real issue behind it. In apple documentation it is written that the url should be a file url and because of which you might be facing issue.
To solve this I have added image to temporary directory and then added to UNNotificationAttachment .
Please find the code below. (For my use case I was getting an imageURL)
extension UNNotificationAttachment {
/// Save the image to disk
static func create(imageFileIdentifier: String, data: NSData, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {
let fileManager = FileManager.default
let tmpSubFolderName = ProcessInfo.processInfo.globallyUniqueString
let tmpSubFolderURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(tmpSubFolderName, isDirectory: true)
do {
try fileManager.createDirectory(at: tmpSubFolderURL!, withIntermediateDirectories: true, attributes: nil)
let fileURL = tmpSubFolderURL?.appendingPathComponent(imageFileIdentifier)
try data.write(to: fileURL!, options: [])
let imageAttachment = try UNNotificationAttachment.init(identifier: imageFileIdentifier, url: fileURL!, options: options)
return imageAttachment
} catch let error {
print("error \(error)")
}
return nil
}
}
data in the argument of this function is Data of image . Below is how did I call this method.
let imageData = NSData(contentsOf: url)
guard let attachment = UNNotificationAttachment.create(imageFileIdentifier: "img.jpeg", data: imageData!, options: nil) else { return }
bestAttemptContent?.attachments = [attachment]
I also found important and quite weird behaviour of initialization of UNNotificationAttachment object. It was happening to me that I was getting error:
"Invalid attachment file URL"
But it was not happening always. It was happening in case when I used for some notifications same image for attachment. When I made a standalone copy of image for each attachment, it never happened. Then I checked directory when images should be copied ( because I wanted to clean it up ), but I was surprised that there were no images.
It seems that UNNotificationAttachment initialization process is deleting files at given URLs. So when you try to reuse some images, they can be deleted ( probably asynchronously, because I was checking existence of that images and it always returned me true - that file exists ). But UNNotificationAttachment ended up with error you can see above. In my opinion only logic explanation of this error is that file at given URL is deleted during the process of UNNotificationAttachment initialization.
Apple actually makes a statement in their documentation (https://developer.apple.com/documentation/usernotifications/unnotificationattachment)
Apple Docs for UNNotificationAttachment:
...
The system validates attachments before displaying the associated notification.
...
Once validated, attached files are MOVED into the attachment data store so that they can be accessed by all of the appropriate
processes. Attachments located inside an app’s bundle are copied
instead of moved.
So, above answers with copying an attachment (image) first into a temporary location before adding as an attachment seem to be the expected solution.
In Swift 5. The below code works for me. Hope this helps somebody.
let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
let imageURL = URL(fileURLWithPath: paths.first!).appendingPathComponent("\(fileName).jpg")
let image = UIImage(contentsOfFile: imageURL.path)
let imageData = image?.pngData()
if let unwrappedImageData = imageData, let attachement = try? UNNotificationAttachment(data: unwrappedImageData, options: nil) {
content.attachments = [ attachement ]
}
When I use method .fileExists(atPath:)to judge whether the file is exist in file system, the method always return false to me. I checked the file system and the file do exist. Here is my code:
let filePath = url?.path
var isDir : ObjCBool = false
if(self.fileManager.fileExists(atPath: filePath!, isDirectory: &isDir)){
let result = NSData(contentsOfFile: filePath!)
}
or
let filePath = url?.path
if(self.fileManager.fileExists(atPath: filePath!)){
let result = NSData(contentsOfFile: filePath!)
}
the if clause will always be skipped.
I assume your url is an URL type. If so try this out:
let filePath = url?.path // always try to work with URL when accessing Files
if(FileManager.default.fileExists(atPath: filePath!)){ // just use String when you have to check for existence of your file
let result = NSData(contentsOf: url!) // use URL instead of String
}
Saying enough, you should change your implementation like this:
if(FileManager.default.fileExists(atPath: (url?.path)!)){ // just use String when you have to check for existence of your file
let result = NSData(contentsOf: url!) // use URL instead of String
}
EDIT: 1
There is even more better way, you can call it swift-way (:D). You don't have to explicitly check for file existence.
guard let result = NSData(contentsOf: fileURL) else {
// No data in your fileURL. So no data is received. Do your task if you got no data
// Keep in mind that you don't have access to your result here.
// You can return from here.
return
}
// You got your data successfully that was in your fileURL location. Do your task with your result.
// You can have access to your result variable here. You can do further with result constant.
print(result)
Update for Swift 3.0+ without the Objective-Cish NS prefix:
do {
let result = try Data(contentsOf: fileURL)
print(result)
} catch {
print(error)
}
in swift 3
just in case anyone gets confused like i did, here's the full snippets:
let str = "file:///Users/martian2049/Library/Developer/CoreSimulator/Devices/67D744AA-6EEC-4AFD-A840-366F4D78A18C/data/Containers/Data/Application/DD96F423-AF9F-4F4D-B370-94ADE7D6D0A5/Documents/72b8b0fb-7f71-7f31-ac9b-f9cc95dfe90d.mp3"
let url = URL(string: str)
print(url!.path,"\n")
if FileManager.default.fileExists(atPath: url!.path) {
print("FILE Yes AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
this prints
/Users/martian2049/Library/Developer/CoreSimulator/Devices/67D744AA-6EEC-4AFD-A840-366F4D78A18C/data/Containers/Data/Application/DD96F423-AF9F-4F4D-B370-94ADE7D6D0A5/Documents/72b8b0fb-7f71-7f31-ac9b-f9cc95dfe90d.mp3
FILE Yes AVAILABLE
notice how the 'file://' got chopped off?
I want to share my experience, in case anyone else gets baffled by this.
Tested on iOS 10-11, Xcode 9.2 and Swift 3.2.
Short answer: if you save a file path to disk, you may solve by not including the Documents directory in it.
Instead, every time you need to retrieve the file with the saved path, get the Documents directory and append the path.
For an iOS app, I was saving an image to .../Documents/Pictures through the relative URL, let's say url.
As the image was saved, a path, let's say url.path, was saved too in a Core Data entity.
When I later tried retrieving the image through FileManager.default.fileExists(atPath: url.path), it always returned false.
I was testing the app on my iPhone. It turned out that, for some reason, every time I ran the app from Xcode, the app identifier folder changed!!
So:
App opened from Xcode -> Image saved -> app closed -> app opened from physical device ->
fileExists -> TRUE
App opened from Xcode -> Image saved -> app closed -> app opened from Xcode -> fileExists -> FALSE
You can check if this is your case by getting and printing the Document folder path (or URL, it doesn't matter) and comparing it with the saved path (or URL). If you get something like this:
/var/mobile/Containers/Data/Application/5D4632AE-C432-4D37-A3F7-ECD05716AD8A/Documents..
/var/mobile/Containers/Data/Application/D09904C3-D80D-48EB-ACFB-1E42D878AFA4/Documents..
you found the issue.
Just use path instead of absoluteString to remove file://
FileManager.default.fileExists(atPath: URL.init(string: "your_url")!.path)
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
var path = paths[0] as String;
path = path + "/YourFilePath"
if((NSFileManager.defaultManager().fileExistsAtPath(path))) {
let result = NSData(contentsOfFile: filePath!)}
Try the above code and check again
I had the same problem this worked for me
filePath.replacingOccurrences(of: "file://", with: "")
First, what does your file path looks like? If the path begins with a ~,then it must be expanded with expandingTildeInPath;
Check if the path is inaccessible to your app. iOS App can only visits its sandbox directories.
I am using SwiftyDropbox API to upload images to dropbox. I have image in project directory and trying to upload it like this :
// Verify user is logged into Dropbox
if let client = Dropbox.authorizedClient {
let imagePath : NSString = NSBundle.mainBundle().pathForResource("abc", ofType: "png")!
print("Path :--> \n", imagePath)
let url : NSURL = NSURL(string: imagePath as String)!
client.files.upload(path: "", body: url).response{response, error in
if let metadata = response {
print("*** Upload file ****")
print("Uploaded file name: \(metadata.name)")
self.delegate?.imageSavedSuccessfully()
}
else{
print(error!)
}
}
And the error I get is (including screenshot too.):
precondition failed: " must match pattern "\A(?:/.*)\z":
Any hint or guide what I am doing wrong? Thank you in advance.
For your file upload, you're specifying the path "", but that represents the root folder, not a file path.
Instead, the path should be the full path where you want to upload the file, including the file name. For example, to upload the file as "abc.png" in the root of the Dropbox account, you'd specify the path as "/abc.png".