Get file size of PHAsset without loading in the resource? - swift

Is there a way to get the file size on disk of a PHAsset without doing requestImageDataForAsset or converting it to ALAsset? I am loading in previews of a user's photo library - potentially many thousands of them - and it's imperative to my app that they know the size before import.

You can grab the fileSize of a PHAsset and convert it to human readable form like this:
let resources = PHAssetResource.assetResources(for: yourAsset) // your PHAsset
var sizeOnDisk: Int64? = 0
if let resource = resources.first {
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong
sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64!))
}
Then use your sizeOnDisk variable and pass it into a method like this...
func converByteToHumanReadable(_ bytes:Int64) -> String {
let formatter:ByteCountFormatter = ByteCountFormatter()
formatter.countStyle = .binary
return formatter.string(fromByteCount: Int64(bytes))
}

A safer solution:
[asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
NSNumber *fileSize = nil;
NSError *error = nil;
[contentEditingInput.fullSizeImageURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:&error];
NSLog(#"file size: %#\nerror: %#", fileSize, error);
}];
Swift version:
asset.requestContentEditingInput(with: nil) { (contentEditingInput, _) in
do {
let fileSize = try contentEditingInput?.fullSizeImageURL?.resourceValues(forKeys: [URLResourceKey.fileSizeKey]).fileSize
print("file size: \(String(describing: fileSize))")
} catch let error {
fatalError("error: \(error)")
}
}
Inspired by How to get an ALAsset URL from a PHAsset?

Please try this.
let resources = PHAssetResource.assetResources(for: YourAsset)
var sizeOnDisk: Int64 = 0
if let resource = resources.first {
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong
sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64!))
totalSize.text = String(format: "%.2f", Double(sizeOnDisk) / (1024.0*1024.0))+" MB"
}

SWIFT 5.0
Light & Easy:
private static let bcf = ByteCountFormatter()
func getSize(asset: PHAsset) -> String {
let resources = PHAssetResource.assetResources(for: asset)
guard let resource = resources.first,
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong else {
return "Unknown"
}
let sizeOnDisk = Int64(bitPattern: UInt64(unsignedInt64))
Self.bcf.allowedUnits = [.useMB]
Self.bcf.countStyle = .file
return Self.bcf.string(fromByteCount: sizeOnDisk)
}

Related

How to fetch Images from CoreData correct?

this is the code how I save images in CoreData:
lazy var dataImage = profileIcon.jpegData(compressionQuality: 1.0)
//MARK: - Saving the Workout Image
func savePUValues() {
// Kontext Identifiziern
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let context = appDelegate.persistentContainer.viewContext
let entityName = "PushUps" //Tabellenname im DatenModell
//Neuen Datensatz anlegen:
guard let newEntity = NSEntityDescription.entity(forEntityName: entityName, in: context)
else {
return
}
let newSet = NSManagedObject(entity: newEntity, insertInto: context)
let savingImage = dataImage
newSet.setValue(savingImage, forKey: "image")
// Datensatz speichern
do {
try context.save()
} catch {
print("An error appeared")
}
}
Now I want to fetch the image data and load it into a UITableView:
With this code:
func loadValues() -> [CellData] {
//Kontext identifizieren
let appDelegate = UIApplication.shared.delegate as! AppDelegate
//Anfrage stellen
let context = appDelegate.persistentContainer.viewContext
let entityName = "PushUps"
//MARK: - Request
// Use a specific fetch request
let request = NSFetchRequest<PushUps>(entityName: entityName)
// add a sort descriptor to sort the items by highScore descending
request.sortDescriptors = [NSSortDescriptor(key: "count", ascending: false)]
do {
// results is an array of PushUps instances, no type cast needed
let results = try context.fetch(request)
for r in results {
if let result = r as? NSManagedObject {
let titletext = "\(count!) Push-Ups"
var cellTitleLabel = UILabel()
cellTitleLabel.text = titletext
let workoutImage = result.value(forKey: "image") as? UIImageView
print("Image was loaded into the array")
var Data1 = CellData(imageData: workoutImage ?? oImageView, titleData: cellTitleLabel)
print("Image: \(String(describing: workoutImage)), TitLe: \(cellTitleLabel) ")
cellDataArray.append(Data1)
}
}
} catch {
print(error)
}
return cellDataArray
}
The problem is that the it's loading the image as nil.
The console is giving me this back: "Image: nil, TitLe: <UILabel: 0x115f17980; frame = (0 0; 0 0); text = '0.0 Push-Ups'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x28249cfa0>> "
So where is the problem? What is wrong with my Code?
Thanks for your help!
The problem is that you are saving Data and trying to retrieve it as a UIImageView, which is doomed to failure:
lazy var dataImage = profileIcon.jpegData(compressionQuality: 1.0)
// Data
let savingImage = dataImage
newSet.setValue(savingImage, forKey: "image")
// still Data
// ---------------------
let workoutImage = result.value(forKey: "image") as? UIImageView
// UIImageView?????
However, don't save image data into Core Data in the first place. Save to disk and store the partial path (e.g. the file name).

how to get the array count if string is empty from nsdictionary?

this is my respone
{
activeFromDate = "01/01/2017";
terminateDate = “02/05/2019”;
}
{
activeFromDate = "01/01/2013";
terminateDate = "<null>";
}
{
activeFromDate = "01/01/2017";
terminateDate = "02/05/2018";
}
{
activeFromDate = "07/01/2012";
terminateDate = "<null>";
}
{
activeFromDate = "01/01/2017";
terminateDate = "02/05/2019";
}
this is my code
let answerArray = NSMutableArray()
for i in 0..<arr.count
{
let dict = Dictionary[i] as! NSDictionary
let name1 = dict.value(forKey:"terminateDate") as! String
if(name1 == "")
{
print (answerArray.add(dict))
}
}
this is my nsdictionary response
how to get array count if terminatedDate is null and terminatedDate is greater than today date?
I have created a sample to read json file placed in your project. and have integrated the code as per your requirement.
Json File
{"Status":"success","Data":[
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2019"},
{"activeFromDate":"01/01/2013","terminateDate":null},
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2018"},
{"activeFromDate":"07/01/2012","terminateDate":null},
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2016"},
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2019"}
]
}
In your View Controller
import UIKit
class HomeViewController: UIViewController {
let answerArray = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
jsonParsingFromFile()
}
func jsonParsingFromFile()
{
let path: NSString = Bundle.main.path(forResource: "tjw", ofType: "json")! as NSString
let data : NSData = try! NSData(contentsOfFile: path as String, options: NSData.ReadingOptions.dataReadingMapped)
self.startParsing(data: data)
}
func startParsing(data :NSData)
{
let dict: NSDictionary!=(try! JSONSerialization.jsonObject(with: data as Data, options: JSONSerialization.ReadingOptions.mutableContainers)) as? NSDictionary
//print(dict)
guard let dataArr:NSArray = dict.value(forKey: "Data") as? NSArray else {
return
}
print(dataArr.count)
for i in 0..<dataArr.count{
guard let currentDict:NSDictionary = dataArr[i] as? NSDictionary else {
return
}
let activeFromDateStr:String = currentDict.value(forKey: "activeFromDate") as? String ?? ""
let terminateDateStr:String = currentDict.value(forKey: "terminateDate") as? String ?? ""
print(activeFromDateStr, terminateDateStr)
if terminateDateStr != ""{
let date2 = convertToDate(dateStr: terminateDateStr)
let today = Date()
if date2>today{
answerArray.add(currentDict)
}
}else{
answerArray.add(currentDict)
}
}
print(answerArray)
}
func convertToDate(dateStr:String)->Date{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
dateFormatter.locale = Locale(identifier: "en_IN")
let date = dateFormatter.date(from: dateStr)!
return date
}
}

Issue with file manager in swift 3.1

I have the below code that worked fine, up until the release of swift 3.1.
func loadImage() {
id = userPhotoModel.id
let fileManager = FileManager.default
let imagePath = (self.getDirectoryPath() as NSString).appendingPathComponent(photoName)
if fileManager.fileExists(atPath: imagePath){
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath))
print("name of photo retrieved: \(photoName)")
self.userPhoto.image = imageFromPath
}else{
print("No Image")
}
}
Now, swift 3.1 wants to add as! String to:
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath) as! String)
However, when I run the app, it crashes in this location with no error message as per the below image.
What is causing this?
EDIT: Here is the resizeImage func
fileprivate func resizeImage(named name: String) -> UIImage
{
var image = UIImage(named: name)
if image!.size.height > image!.size.width
{
self.userPhoto.contentMode = .scaleAspectFit
}
else
{
image = image!.resizeTo(self.userPhoto.bounds)
}
return image!
}
The issue is the confusing syntax in the line:
let imageFromPath = resizeImage(named: (contentsOfFile: imagePath))
That should simply be:
let imageFromPath = resizeImage(named: imagePath)
No cast required and correct in any Swift 3.x.

Swift - some mp3 file metadata return nil

I have code like below. For some file I got metadata like artist, title and other without any problem. For other files metadata list is nil but when I check metadata in editor like Tagger - title and other metadata exists. Furthermore when I change metadata in external editor for at least one key - my code starts work properly.
Could someone explain me where I make mistake ?
static func getBookInCatalog(url: URL) -> Book {
let book = Book(url: url)
let isDir: ObjCBool = false
var directoryContents = [URL]()
var totalTime: CMTime?
var size: UInt64 = 0
var chapters:Int = 0
do {
directoryContents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [])
} catch let error as NSError {
print(error.localizedDescription)
return book
}
for item in directoryContents {
if !isDir.boolValue {
let result = appDelegate.fileTypes.filter { $0==item.pathExtension.lowercased() }
if !result.isEmpty {
chapters += 1
let fileSize = (try! FileManager.default.attributesOfItem(atPath: item.path)[FileAttributeKey.size] as! NSNumber).uint64Value
size += fileSize
let playerItem = AVPlayerItem(url: item)
let metadataList = playerItem.asset.commonMetadata
let asset = AVURLAsset(url: item, options: nil)
let audioDuration = asset.duration
if let _ = totalTime {
totalTime = totalTime! + audioDuration
} else {
totalTime = audioDuration
}
for metadata in metadataList {
guard let key = metadata.commonKey, let value = metadata.value else{
continue
}
switch key {
case "albumName":
if book.title == nil || book.title == "" {
book.title = (value as? String)!
}
case "artist":
if book.author == nil || book.author == "" {
book.author = (value as? String)!
}
case "artwork" where value is NSData:
if book.image == nil {
book.image = UIImage(data: (value as! NSData) as Data)
}
default:
continue
}
}
}
}
}
if let imageInsideCatalog = getImageFromFolder(url: url){
book.image = imageInsideCatalog
}
if book.title == nil {
book.title = url.deletingPathExtension().lastPathComponent
}
book.chapters = chapters
book.totalTime = totalTime
book.size = size
return book
}
MP3 meta data "standards" have gone through several major iterations over the years (see http://id3.org) . Your editor may be able to read older formats (that AVURLAsset may not support) and save them using the latest/current standard which would make them compatible after any change.

Swift Save images (screenshots) to nsuserdefaults

I have a program, where the user "creates" an image, and then the program takes a screenshot of the screen. I would then like to save this screenshot to a database, prefferebly nsuserdefaults, since I am accessing it later in a table view. Any other suggestions on how to approach this, are more than welcome :)
The code is like this
let screenshot = getScreenshot() // saves the screenshot
var imagePaths = [String]()
// get the array of previous screenshots
if let _ = NSUserDefaults.standardUserDefaults().objectForKey(theKey)
{
imagePaths = NSUserDefaults.standardDefaults().objectForKey(theKey) as! [String]
}
// then I want to get a path to the image something like
let imagePath = screenshot.getPath() // although this is not a valid method, this is basically what I want
// add the imagePath
imagePaths.append(imagePath)
// finally I save the image
NSUserDefaults.standardUserDefaults().setObject(imagePaths, forKey: theKey)
You can create directory in Documents and save there screenshots as usual files. Filename can be generated from date and time for uniqueness.
func saveImage(imageData: NSData)
{
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy hh.mm.ss"
let filename = "\(dateFormatter.stringFromDate(NSDate())).png"
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
let imagesDirectory = documentsDirectory.stringByAppendingPathComponent("Images")
let filePath = imagesDirectory.stringByAppendingPathComponent(filename)
if !NSFileManager.defaultManager().fileExistsAtPath(imagesDirectory)
{
var error: NSError?
NSFileManager.defaultManager().createDirectoryAtPath(imagesDirectory, withIntermediateDirectories: false, attributes: nil, error: &error)
if error != nil
{
println("\(error!.localizedDescription)")
return
}
}
imageData.writeToFile(filePath, atomically: true)
}
func getImagesPaths() -> [String]?
{
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as! String
let imagesDirectory = documentsDirectory.stringByAppendingPathComponent("Images")
if let filenames = NSFileManager.defaultManager().contentsOfDirectoryAtPath(imagesDirectory, error: nil)
{
let imagePaths = filenames.map{"\(imagesDirectory)/\($0)"}.filter(){$0.pathExtension == "png"}
return imagePaths.count > 0 ? imagePaths : nil
}
return nil
}
To save image simply use saveImage(data). To get images paths use getImagesPaths().
If you need array of UIImage, you can get it by follow way:
var images : [UIImage] = [ ]
if let imagePaths = getImagesPaths()
{
for path in imagePaths
{
if let image = UIImage(contentsOfFile: path)
{
images.append(image)
}
}
}