My starting point:
I get a zip archive with some compressed images from an API. I want to show the pictures in a slideshow.
The problem:
After I successfully downloaded the zip archive as Data from URLSession.shared.dataTask and uncompressed it with let decompressedData = try (data as NSData) .decompressed (using: .zlib), I got no further.
I know that I can use UIImage(data: data) to display an image from data.
But how do I get the individual data for the images from the decompressed data?
Thank you in advance,
Lucas
PS: If I haven't explained it clearly or if you need further details, please just ask
Never mind, I found the solution myself:
guard let archive = Archive(data: data, accessMode: .read) else {
return
}
for entry in archive {
var extractedData: Data = Data([])
do {
_ = try archive.extract(entry) { extractedData.append($0) }
if let image = UIImage(data: extractedData) {
self.images.append(image)
}
} catch {
print(error.localizedDescription)
}
}
Zip Foundation is required for this to work. Thanks #jn-pdx for pointing in the right direction. After a litte bit of digging I found a way to create a Archive with memory data.
Related
UPDATE
I tried the following code solution and it allows for me to save to Google Drive now, but Egnyte and Dropbox are still greyed out.
func exportPhotosToFileLocation() {
var fileURLArray = [URL]()
for data in reviewDataController.tableViewReviewData {
guard let imageData = data.image.jpegData(compressionQuality: 1.00) else {
print("ERROR: Unable to print convert image to jpegData in exportPhotosToFileLocation!")
return
}
let fileManager = FileManager.default
do {
let fileURL = fileManager.temporaryDirectory.appendingPathComponent("\(data.imageTitle)").appendingPathExtension("jpeg")
try imageData.write(to: fileURL)
fileURLArray.append(fileURL)
print("Successfully created file from jpegData in exportPhotosToFileLocation!")
} catch {
print("ERROR: Unable to create file from jpegData in exportPhotosToFileLocation!")
return
}
}
if #available(iOS 14, *) {
let controller = UIDocumentPickerViewController(forExporting: fileURLArray)
present(controller, animated: true)
}
else {
let controller = UIDocumentPickerViewController(urls: fileURLArray, in: .exportToService)
present(controller, animated: true)
}
}
Here is the developer documents for Egnyte. Unfortunately, none of it makes sense to me as a beginner.
Egnyte Developer Documentation
----------------------------------------------------------------------------------------------
ORIGINAL POST
In my app, I'm trying to allow the user to select a save location (so choose a folder). Whenever I use this code, Egnyte/Google Drive/Dropbox are all "greyed" out and inaccessible.
let supportedTypes : [UTType] = [UTType.folder]
let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes)
documentPickerController.delegate = self
self.present(documentPickerController, animated: true, completion: nil)
If I change supportedTypes to
let supportedTypes : [UTType] = [UTType.text]
It does let me access them. Does anyone have a solution for this? I obviously need the user to be able to select a folder in these applications... you can see why that is important.
This is up to the file provider extension (Google Drive, etc.). To allow picking a folder, the file provider has to lay content in its directory in a hierarchical manner... if they do this, they need to specify NSExtensionFileProviderSupportsPickingFolders in their Info.plist to tell the system it's allowed to choose folders.
Do you need to choose a save location and persist it? If yes, then you'll be blocked on the file provider implementing the necessary API. If not, the type you pass should the type of the document you are actually saving. The document will be saved once in the chosen folder (without any additional requirements on the file provider extension), and you will have to use the document picker again to save the next document.
If you are trying to select Dropbox as a location to import files from in the Apple File Importer but it does not advance to the file selection screen I found that restarting my iPhone seemed to resolve that issue.
I currently have a view controller where the user uploads an image and that image is stored in the Firebase Storage, with folders of their user id and in there their uploaded image. I want to fetch that image's url and display it on the view controller. The question I have is, do i need to store that uploaded image's download url into the realtime database ie; Users - > User Id - > Media -> Image Title -> Download URL? Or is there a way for me to refer an image view to Firebase storage and accordingly into the User Id's folder containing all the images and display this? Would appreciate it a lot if someone could help me out. Thank you!
This is my code:
let imageName = (Auth.auth().currentUser?.uid)!+"/\(imageTitle.text!)"
let imageReference = Storage.storage().reference().child(MyKeys.imagesFolder).child(imageName)
imageReference.putData(data, metadata: nil) { (metadata, err) in
if let err = err {
print ("Error")
return
}
imageReference.downloadURL(completion: { (url, err) in
if let err = err {
print ("Error")
return
}
guard let url = url else {
print ("Error")
return
}
let dataReference = Firestore.firestore().collection(MyKeys.imagesCollection).document()
let documentUid = dataReference.documentID
let urlString = url.absoluteString
let data = [
MyKeys.uid:documentUid,
MyKeys.imageUrl: urlString,
MyKeys.imageTitle: self.imageTitle.text!,
] as [String : Any]
dataReference.setData(data) { (err) in
if let err = err {
print ("Error")
return
}
}
})
}
There are two ways to read the data from Storage:
Through the Firebase SDK by calling getData or write on a reference, as shown in the documentation on downloading data.
Through a download URL for the reference, which provides public read-only access.
To call getData, write or to get a download URL, you need to have a reference to the file as shown in creating a reference.
Since you store the files under the UID of the user, you can always create a reference to any for for a user if you known their UID. So you can always perform one of the two methods above to read the data for the file, even if you didn't store the download URL.
That said, it is fairly common to store the download URL in a database, as it means you can then treat it like any other image URL. By doing so the rest of your code won't have to know anything about Cloud Storage.
So: it can work without storing the download URL in the database, but it's also fine (and common) if you do store the download URL. The choice is really up to you.
please someone explain why this happen? I was checking so I upload an image directly from firebase and I got its original size, but when I upload it using this code the size increase.
func storeImageInFirebase(){
let storeageRef = Storage.storage().reference()
let imageName = UUID().uuidString
let imagesReference = storeageRef.child("images").child(imageName + ".jpeg")
let imageData = self.imgView.image?.jpegData(compressionQuality: 1)
let metaData = StorageMetadata()
metaData.contentType = "image/jpeg"
imagesReference.putData(imageData!, metadata: metaData){ (metadate, error)
in
guard metadate != nil else{
print("Error: \(String(describing: error?.localizedDescription))")
return
}
// Fetch the download URL
imagesReference.downloadURL(completion: {(url, error)
in
if error != nil {
print("Faild to download url:", error!)
return
}else{
print("original image url ..... \(url?.absoluteString)")
// show the url in real database
var theUsedURL = self.imgURL = (url?.absoluteString)!
self.sendDataToFirebase()
}
})
}
}
enter image description here
JPEG is a lossy file format: it stores the image data in a way that compresses it down, depending on the compression factor used.
Image data in an Image View is always uncompressed: it shows the raw bytes of the image.
Most likely, you are:
reading the compressed data from the JPEG file, then
showing the uncompressed version of that data in the image view, and finally
writing the data back to storage as an uncompressed (or less compressed) JPEG file.
If you want to control how big the compressed image is, you can modify the compressionQuality in:
jpegData(compressionQuality: 1)
If you want the exact same file to be written, you should not recompress the data, but read the uncompressed data and write that exact data directly back to storage.
I have a UIImage object, say from the camera roll via PHAsset. The image is saved as a .jpg file:
asset.requestContentEditingInput(with: nil) { (input, nil) in
print(input?.fullSizeImageURL) // somefile.jpg
}
To get the file size should not data.count from this return the correct file size in bytes?
PHImageManager.default().requestImageData(for: asset, options: nil) { data, _, _, _ in
if let _data = data {
print(_data.count) // 6759240
}
}
The output for a particular image is 6759240 while fileSize() returns 2978548.0 (which is the right file size) bytes.
func fileSize(forURL url: Any) -> Double {
var fileURL: URL?
var fileSize: Double = 0.0
if (url is URL) || (url is String)
{
if (url is URL) {
fileURL = url as? URL
}
else {
fileURL = URL(fileURLWithPath: url as! String)
}
var fileSizeValue = 0.0
try? fileSizeValue = (fileURL?.resourceValues(forKeys: [URLResourceKey.fileSizeKey]).allValues.first?.value as! Double?)!
if fileSizeValue > 0.0 {
fileSize = (Double(fileSizeValue))
}
}
return fileSize
}
Does it mean someUIImage?.jpegData(compressionQuality: 1)?.count does not return the correct size of JPEG image file (if saved)?
One more thing, Is there any way to determine the image file size before writing it on the disk?
All of these is to compare the file size between the original and compressed image.
This sounds like a misunderstanding of what the various terms and calls refer to.
You have no direct access to a file stored in the user's Photo library. There may in fact be no such file; you should make no assumptions about the storage format. When you ask PHImageManager for an image's data, you are given the bitmap data, ready for use. Thus you should expect this data to be big, in exact proportion to the dimensions of the image. 6759240 is more than 6MB, which sounds about right on an older iPhone; a newer iPhone, takes 4032x3024 photos which is more than 8MB.
Then in a different part of your code you call fileSize(forURL:). Now you're looking at an actual file, in the file system, in a place where you can access it. If this is an image file, it is compressed; just how much it is compressed depends on the format. 2978548 is about 3MB which is pretty good for a JPEG compressed without too much lossiness.
Finally, you ask about UIImage jpegData(compressionQuality: 1)?.count. You do not show any code that actually calls that. But this is data ready for saving as a file directly with write(to:) and a URL, and I would expect it to be the same as fileSize(forURL:) if you were to check the very same file later.
So this isn't a question about actually populating a table with data because I already have that working properly.
I have a page that retrieves a list of nearby users - it grabs their name, distance away and profile image. The problem is that because some profile images are bigger than others the correct profile image isn't always put alongside the corresponding name and other data.
let profileImgFile = location["profilePicture"]
profileImgFile!.getDataInBackgroundWithBlock { (data: NSData?, error: NSError?) -> Void in
if error != nil {
print("\(__FUNCTION__) - Error: (error!.localizedDescription)")
} else {
let image = UIImage(data: data!)
self.userImages.append(image!)
self.userTable.reloadData()
}
}
This is how I'm retrieving the images, they're added to an array so that the table can be populated here:
cell.nameLabel.text = "\(userData[indexPath.row])"
cell.profileImage.image = userImages[indexPath.row]
cell.distanceLabel.text = ("\(userDistance[indexPath.row])")
So, I wondered if there was any way of retrieving the images one after the other. So that the next image doesn't begin downloading until the first one has finished.
If not is there any other way of fixing this problem?
Thanks!
I think you should try setting your data async with promises.
Promises in Swift
PromiseKit
And there's a lot of other resources out there.