How to get image file size in Swift? - ios

I am using
UIImagePickerControllerDelegate,
UINavigationControllerDelegate,
UIPopoverControllerDelegate
these delegates for choosing image from my gallery or my camera. So, how can I get image file size after choosing an image?
I want to use this:
let filePath = "your path here"
var fileSize : UInt64 = 0
do {
let attr : NSDictionary? = try NSFileManager.defaultManager().attributesOfItemAtPath(filePath)
if let _attr = attr {
fileSize = _attr.fileSize();
print(fileSize)
}
} catch {
}
but here I need a path, but how can I get without a path, just by image file?

Please check the google for 1 kb to bytes it will be 1000.
https://www.google.com/search?q=1+kb+%3D+how+many+bytes&oq=1+kb+%3D+how+many+bytes&aqs=chrome..69i57.8999j0j1&sourceid=chrome&ie=UTF-8
So while getting the proper size I’ve added multiple scenario by adding image in App Bundle and in photos in simulator.
Well the image which I took from my Mac was of 299.0 KB.
Scenario 1: Adding image to Application Bundle
On adding image in your Xcode the size of the image will remain same in project directory. But you get it from its path the size will be reduced to 257.0 KB. Which is the actual size of the image used in the device or simulator.
guard let aStrUrl = Bundle.main.path(forResource: "1", ofType: "png") else { return }
let aUrl = URL(fileURLWithPath: aStrUrl)
print("Img size = \((Double(aUrl.fileSize) / 1000.00).rounded()) KB")
extension URL {
var attributes: [FileAttributeKey : Any]? {
do {
return try FileManager.default.attributesOfItem(atPath: path)
} catch let error as NSError {
print("FileAttribute error: \(error)")
}
return nil
}
var fileSize: UInt64 {
return attributes?[.size] as? UInt64 ?? UInt64(0)
}
var fileSizeString: String {
return ByteCountFormatter.string(fromByteCount: Int64(fileSize), countStyle: .file)
}
var creationDate: Date? {
return attributes?[.creationDate] as? Date
}
}
Scenario 2: Adding image to Photos in Simulator
On adding image to photos in simulator or device the size of the image increased from 299.0 KB to 393.0 KB. Which is the actual size of the image stored in the device or simulator’s document directory.
Swift 4 and earlier
var image = info[UIImagePickerControllerOriginalImage] as! UIImage
var imgData: NSData = NSData(data: UIImageJPEGRepresentation((image), 1))
// var imgData: NSData = UIImagePNGRepresentation(image)
// you can also replace UIImageJPEGRepresentation with UIImagePNGRepresentation.
var imageSize: Int = imgData.count
print("size of image in KB: %f ", Double(imageSize) / 1000.0)
Swift 5
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
let imgData = NSData(data: image.jpegData(compressionQuality: 1)!)
var imageSize: Int = imgData.count
print("actual size of image in KB: %f ", Double(imageSize) / 1000.0)
By adding .rounded() it will give you 393.0 KB and without using it it will give 393.442 KB. So please check the image size manually once using the above code. As the size of image may vary in different devices and mac. I've check it only on mac mini and simulator iPhone XS.

extension UIImage {
public enum DataUnits: String {
case byte, kilobyte, megabyte, gigabyte
}
func getSizeIn(_ type: DataUnits)-> String {
guard let data = self.pngData() else {
return ""
}
var size: Double = 0.0
switch type {
case .byte:
size = Double(data.count)
case .kilobyte:
size = Double(data.count) / 1024
case .megabyte:
size = Double(data.count) / 1024 / 1024
case .gigabyte:
size = Double(data.count) / 1024 / 1024 / 1024
}
return String(format: "%.2f", size)
}
}
Usage example : print("Image size \(yourImage.getSizeIn(.megabyte)) mb")

Swift 3/4:
if let imageData = UIImagePNGRepresentation(image) {
let bytes = imageData.count
let kB = Double(bytes) / 1000.0 // Note the difference
let KB = Double(bytes) / 1024.0 // Note the difference
}
Please note the difference between kB and KB. Answering here because in my case we had an issue while we considered kilobyte as 1024 bytes but server side considered it as 1000 bytes which caused an issue. Link to learn more.
PS. Almost sure you'll go with kB (1000).

Details
Xcode 10.2.1 (10E1001), Swift 5
Solution
extension String {
func getNumbers() -> [NSNumber] {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
let charset = CharacterSet.init(charactersIn: " ,.")
return matches(for: "[+-]?([0-9]+([., ][0-9]*)*|[.][0-9]+)").compactMap { string in
return formatter.number(from: string.trimmingCharacters(in: charset))
}
}
// https://stackoverflow.com/a/54900097/4488252
func matches(for regex: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regex, options: [.caseInsensitive]) else { return [] }
let matches = regex.matches(in: self, options: [], range: NSMakeRange(0, self.count))
return matches.compactMap { match in
guard let range = Range(match.range, in: self) else { return nil }
return String(self[range])
}
}
}
extension UIImage {
func getFileSizeInfo(allowedUnits: ByteCountFormatter.Units = .useMB,
countStyle: ByteCountFormatter.CountStyle = .file) -> String? {
// https://developer.apple.com/documentation/foundation/bytecountformatter
let formatter = ByteCountFormatter()
formatter.allowedUnits = allowedUnits
formatter.countStyle = countStyle
return getSizeInfo(formatter: formatter)
}
func getFileSize(allowedUnits: ByteCountFormatter.Units = .useMB,
countStyle: ByteCountFormatter.CountStyle = .memory) -> Double? {
guard let num = getFileSizeInfo(allowedUnits: allowedUnits, countStyle: countStyle)?.getNumbers().first else { return nil }
return Double(truncating: num)
}
func getSizeInfo(formatter: ByteCountFormatter, compressionQuality: CGFloat = 1.0) -> String? {
guard let imageData = jpegData(compressionQuality: compressionQuality) else { return nil }
return formatter.string(fromByteCount: Int64(imageData.count))
}
}
Usage
guard let image = UIImage(named: "img") else { return }
if let imageSizeInfo = image.getFileSizeInfo() {
print("\(imageSizeInfo), \(type(of: imageSizeInfo))") // 51.9 MB, String
}
if let imageSizeInfo = image.getFileSizeInfo(allowedUnits: .useBytes, countStyle: .file) {
print("\(imageSizeInfo), \(type(of: imageSizeInfo))") // 54,411,697 bytes, String
}
if let imageSizeInfo = image.getFileSizeInfo(allowedUnits: .useKB, countStyle: .decimal) {
print("\(imageSizeInfo), \(type(of: imageSizeInfo))") // 54,412 KB, String
}
if let size = image.getFileSize() {
print("\(size), \(type(of: size))") // 51.9, Double
}

Swift 3
let uploadData = UIImagePNGRepresentation(image)
let array = [UInt8](uploadData)
print("Image size in bytes:\(array.count)")

try this for getting size from url
func fileSize(url: URL) -> String? {
var fileSize:Int?
do {
let resources = try url.resourceValues(forKeys:[.fileSizeKey])
fileSize = resources.fileSize!
print ("\(String(describing: fileSize))")
} catch {
print("Error: \(error)")
}
// bytes
if fileSize! < 999 {
return String(format: "%lu bytes", CUnsignedLong(bitPattern: fileSize!))
}
// KB
var floatSize = Float(fileSize! / 1000)
if floatSize < 999 {
return String(format: "%.1f KB", floatSize)
}
// MB
floatSize = floatSize / 1000
if floatSize < 999 {
return String(format: "%.1f MB", floatSize)
}
// GB
floatSize = floatSize / 1000
return String(format: "%.1f GB", floatSize)
}
Use Example
let sizeInString = fileSize(url: url)
print("FileSize = "+sizeInString!)

let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
let selectedImageData: NSData = NSData(data:UIImageJPEGRepresentation((selectedImage), 1))
let selectedImageSize:Int = selectedImageData.length
print("Image Size: %f KB", selectedImageSize /1024.0)

let data = UIImageJPEGRepresentation(image, 1)
let imageSize = data?.count
Duplicate of How to get the size of a UIImage in KB?

let imageData = UIImageJPEGRepresentation(image, 1)
let imageSize = imageData?.count
UIImageJPEGRepresentation — returns the Data object for the specified image in JPEG format. The value 1.0 represents the least compression (close to original image).
imageData?.count — return data length (chars count equals bytes).
Important! UIImageJPEGRepresentation or UIImagePNGRepresentation will not return the original image. But if use given Data as source for uploading - than file size be the same as on the server (even using compression).

Swift 4.2
let jpegData = image.jpegData(compressionQuality: 1.0)
let jpegSize: Int = jpegData?.count ?? 0
print("size of jpeg image in KB: %f ", Double(jpegSize) / 1024.0)

Try this code (Swift 4.2)
extension URL {
var attributes: [FileAttributeKey : Any]? {
do {
return try FileManager.default.attributesOfItem(atPath: path)
} catch let error as NSError {
print("FileAttribute error: \(error)")
}
return nil
}
var fileSize: UInt64 {
return attributes?[.size] as? UInt64 ?? UInt64(0)
}
var fileSizeString: String {
return ByteCountFormatter.string(fromByteCount: Int64(fileSize), countStyle: .file)
}
var creationDate: Date? {
return attributes?[.creationDate] as? Date
}
}
And use example
guard let aStrUrl = Bundle.main.path(forResource: "example_image", ofType: "jpg") else { return }
let aUrl = URL(fileURLWithPath: aStrUrl)
print("Img size = \((Double(aUrl.fileSize) / 1000.00).rounded()) KB")

//Swift 4
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
///check image Size
let imgData = NSData(data: UIImageJPEGRepresentation((pickedImage), 1)!)
let imageSize: Int = imgData.count
print("size of image in KB: %f ", Double(imageSize) / 1024.0)
print("size of image in MB: %f ", Double(imageSize) / 1024.0 / 1024)
}

I make work around data units conversion :
Bytes -> KB -> MB -> GB -> ... -> Extremest Monster Data
enum dataUnits:CaseIterable {
case B //Byte
case KB //kilobyte
case MB //megabyte
case GB //gigabyte
case TB //terabyte
case PB //petabyte
case EB //exabyte
case ZB //zettabyte
case YB //yottabyte
case BD //Big Data
case BBx // Extra Big Bytes
case BBxx // 2 time Extra Big Bytes
case BBxxx // 3 time Extra Big Bytes
case BBxxxx // 4 time Extra Big Bytes
case MBB // Monster Big Bytes
}
func convertStorageUnit(data n:Double,inputDataUnit unitLevel:Int,roundPoint:Int = 2,nG:Double = 1000.0)->String{
if(n>=nG){
return convertStorageUnit(data:n/1024,inputDataUnit:unitLevel+1)
}else{
let ut = unitLevel > dataUnits.allCases.count + 1 ? "Extreme Monster Data" : dataUnits.allCases.map{"\($0)"}[unitLevel]
return "\(String(format:"%.\(roundPoint)f",n)) \(ut)"
}
}
print(
convertStorageUnit(data:99922323343439789798789898989897987945454545920,
inputDataUnit:dataUnits.allCases.firstIndex(of: .B)!,roundPoint: 0)
)
output : 8.87 PB
Note: Input data length should be less than 64-bit OR Change data type According

Try this
import Darwin
...
let size = malloc_size(&_attr)

Related

Compress image size to below 1 MB in swift iOS

In iOS swift need to compress image size to below 1 MB by following this thread How to compress of reduce the size of an image before uploading to Parse as PFFile? (Swift) tried with below code it returns nil.
extension UIImage {
// MARK: - UIImage+Compression
func compressTo(_ expectedSizeBelowMb:Int) -> Data? {
let sizeInBytes = expectedSizeBelowMb * 1024 * 1024
var needCompress:Bool = true
var imgData:Data?
var compressingValue:CGFloat = 1.0
while (needCompress && compressingValue > 0.0) {
if let data:Data = self.jpegData(compressionQuality: compressingValue) {
if data.count < sizeInBytes {
needCompress = false
imgData = data
} else {
compressingValue -= 0.1
}
}
}
if let data = imgData, data.count < sizeInBytes {
return data
}
return nil
}
}
Can anyone suggest me correct way of compressing image size to below 1 MB.
You can try this code:
extension UIImage {
func resizedTo1MB() -> UIImage? {
guard let imageData = self.pngData() else { return nil }
let megaByte = 1000.0
var resizingImage = self
var imageSizeKB = Double(imageData.count) / megaByte // ! Or devide for 1024 if you need KB but not kB
while imageSizeKB > megaByte { // ! Or use 1024 if you need KB but not kB
guard let resizedImage = resizingImage.resized(withPercentage: 0.5),
let imageData = resizedImage.pngData() else { return nil }
resizingImage = resizedImage
imageSizeKB = Double(imageData.count) / megaByte // ! Or devide for 1024 if you need KB but not kB
}
return resizingImage
}
}

Having trouble with input image with iOS Swift TensorFlowLite Image Classification Model?

I've been trying to add a plant recognition classifier to my app through a Firebase cloud-hosted ML model, and I've gotten close - problem is, I'm pretty sure I'm messing up the input for the image data somewhere along the way. My classifier is churning out nonsense probabilities/results based on this classifier's output, and I've been testing the same classifier through a python script which is giving me accurate results.
The input for the model requires a 224x224 image with 3 channels scaled to 0,1. I've done all this but can't seem to figure out the CGImage through the Camera/ImagePicker. Here is the bit of the code that processes the input for the image:
if let imageData = info[.originalImage] as? UIImage {
DispatchQueue.main.async {
let resizedImage = imageData.scaledImage(with: CGSize(width:224, height:224))
let ciImage = CIImage(image: resizedImage!)
let CGcontext = CIContext(options: nil)
let image : CGImage = CGcontext.createCGImage(ciImage!, from: ciImage!.extent)!
guard let context = CGContext(
data: nil,
width: image.width, height: image.height,
bitsPerComponent: 8, bytesPerRow: image.width * 4,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
) else {
return
}
context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height))
guard let imageData = context.data else { return }
print("Image data showing as: \(imageData)")
var inputData = Data()
do {
for row in 0 ..< 224 {
for col in 0 ..< 224 {
let offset = 4 * (row * context.width + col)
// (Ignore offset 0, the unused alpha channel)
let red = imageData.load(fromByteOffset: offset+1, as: UInt8.self)
let green = imageData.load(fromByteOffset: offset+2, as: UInt8.self)
let blue = imageData.load(fromByteOffset: offset+3, as: UInt8.self)
// Normalize channel values to [0.0, 1.0].
var normalizedRed = Float32(red) / 255.0
var normalizedGreen = Float32(green) / 255.0
var normalizedBlue = Float32(blue) / 255.0
// Append normalized values to Data object in RGB order.
let elementSize = MemoryLayout.size(ofValue: normalizedRed)
var bytes = [UInt8](repeating: 0, count: elementSize)
memcpy(&bytes, &normalizedRed, elementSize)
inputData.append(&bytes, count: elementSize)
memcpy(&bytes, &normalizedGreen, elementSize)
inputData.append(&bytes, count: elementSize)
memcpy(&bytes, &normalizedBlue, elementSize)
inputData.append(&bytes, count: elementSize)
}
}
print("Successfully added inputData")
self.parent.invokeInterpreter(inputData: inputData)
} catch let error {
print("Failed to add input: \(error)")
}
}
}
Afterwards, I process the inputData with the following:
func invokeInterpreter(inputData: Data) {
do {
var interpreter = try Interpreter(modelPath: ProfileUserData.sharedUserData.modelPath)
var labels: [String] = []
try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
let output = try interpreter.output(at: 0)
switch output.dataType {
case .uInt8:
guard let quantization = output.quantizationParameters else {
print("No results returned because the quantization values for the output tensor are nil.")
return
}
let quantizedResults = [UInt8](output.data)
let results = quantizedResults.map {
quantization.scale * Float(Int($0) - quantization.zeroPoint)
}
let sum = results.reduce(0, +)
print("Sum of all dequantized results is: \(sum)")
print("Count of dequantized results is: \(results.indices.count)")
let filename = "plantLabels"
let fileExtension = "csv"
guard let labelPath = Bundle.main.url(forResource: filename, withExtension: fileExtension) else {
print("Labels file not found in bundle. Please check labels file.")
return
}
do {
let contents = try String(contentsOf: labelPath, encoding: .utf8)
labels = contents.components(separatedBy: .newlines)
print("Count of label rows is: \(labels.indices.count)")
} catch {
fatalError("Labels file named \(filename).\(fileExtension) cannot be read. Please add a " +
"valid labels file and try again.")
}
let zippedResults = zip(labels.indices, results)
// Sort the zipped results by confidence value in descending order.
let sortedResults = zippedResults.sorted { $0.1 > $1.1 }.prefix(3)
print("Printing sortedResults: \(sortedResults)")
case .float32:
print("Output tensor data type [Float32] is unsupported for this model.")
default:
print("Output tensor data type \(output.dataType) is unsupported for this model.")
return
}
} catch {
//Error with interpreter
print("Error with running interpreter: \(error.localizedDescription)")
}
}

Reduce memory consumption while loading gif images in UIImageView

I want to show gif image in a UIImageView and with the code below (source: https://iosdevcenters.blogspot.com/2016/08/load-gif-image-in-swift_22.html, *I did not understand all the codes), I am able to display gif images. However, the memory consumption seems high (tested on real device). Is there any way to modify the code below to reduce the memory consumption?
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let url = "https://cdn-images-1.medium.com/max/800/1*oDqXedYUMyhWzN48pUjHyw.gif"
let gifImage = UIImage.gifImageWithURL(url)
imageView.image = gifImage
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}
extension UIImage {
public class func gifImageWithData(_ data: Data) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
print("image doesn't exist")
return nil
}
return UIImage.animatedImageWithSource(source)
}
public class func gifImageWithURL(_ gifUrl:String) -> UIImage? {
guard let bundleURL:URL? = URL(string: gifUrl) else {
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL!) else {
return nil
}
return gifImageWithData(imageData)
}
public class func gifImageWithName(_ name: String) -> UIImage? {
guard let bundleURL = Bundle.main
.url(forResource: name, withExtension: "gif") else {
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL) else {
return nil
}
return gifImageWithData(imageData)
}
class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(
CFDictionaryGetValue(cfProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()),
to: CFDictionary.self)
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
}
delay = delayObject as! Double
if delay < 0.1 {
delay = 0.1
}
return delay
}
class func gcdForPair(_ a: Int?, _ b: Int?) -> Int {
var a = a
var b = b
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}
if a < b {
let c = a
a = b
b = c
}
var rest: Int
while true {
rest = a! % b!
if rest == 0 {
return b!
} else {
a = b
b = rest
}
}
}
class func gcdForArray(_ array: Array<Int>) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
for i in 0..<count {
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}
let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
let gcd = gcdForArray(delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(cgImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
When I render the image as normal png image, the consumption is around 10MB.
The GIF in question has a resolution of 480×288 and contains 10 frames.
Considering that UIImageView stores frames as 4-byte RGBA, this GIF occupies 4 × 10 × 480 × 288 = 5 529 600 bytes in RAM, which is more than 5 megabytes.
There are numerous ways to mitigate that, but only one of them puts no additional strain on the CPU; the others are mere CPU-to-RAM trade-offs.
The method I`m talking about is subclassing UIImageView and loading your GIFs by hand, preserving their internal representation (indexed image + palette). It would allow you to cut the memory usage fourfold.
N.B.: even though GIFs may be stored as full images for each frame (which is the case for the GIF in question), many are not. On the contrary, most of the frames can only contain the pixels that have changed since the previous one. Thus, in general the internal GIF representation only allows to display frames in direct order.
Other methods of saving RAM include e.g. re-reading every frame from disk prior to displaying it, which is certainly not good for battery life.
To display GIFs with less memory consumption, try BBWebImage.
BBWebImage will decide how many image frames are decoded and cached depending on current memory usage. If free memory is not enough, only part of image frames are decoded and cached.
For Swift 4:
// BBAnimatedImageView (subclass UIImageView) displays animated image
imageView = BBAnimatedImageView(frame: frame)
// Load and display gif
imageView.bb_setImage(with: url,
placeholder: UIImage(named: "placeholder"))
{ (image: UIImage?, data: Data?, error: Error?, cacheType: BBImageCacheType) in
// Do something when finish loading
}

How to convert data into little endian format?

var val = 1240;
convert into little endian formate swift 3
Ex: 1500 (0x5DC) to 0xDC050000
let value = UInt16(bigEndian: 1500)
print(String(format:"%04X", value.bigEndian)) //05DC
print(String(format:"%04X", value.littleEndian)) //DC05
Make sure you are actually using the bigEndian initializer.
With 32-bit integers:
let value = UInt32(bigEndian: 1500)
print(String(format:"%08X", value.bigEndian)) //000005DC
print(String(format:"%08X", value.littleEndian)) //DC050000
If you want 1500 as an array of bytes in little-endian order:
var value = UInt32(littleEndian: 1500)
let array = withUnsafeBytes(of: &value) { Array($0) }
If you want that as a Data:
let data = Data(array)
Or, if you really wanted that as a hex string:
let string = array.map { String(format: "%02x", $0) }.joined()
let timeDevide = self.setmiliSecond/100
var newTime = UInt32(littleEndian: timeDevide)
let arrayTime = withUnsafeBytes(of: &newTime)
{Array($0)}
let timeDelayValue = [0x0B] + arrayTime
You can do something like
//: Playground - noun: a place where people can play
import UIKit
extension String {
func hexadecimal() -> Data? {
var data = Data(capacity: count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, range: NSRange(location: 0, length: utf16.count)) { match, _, _ in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)!
data.append(&num, count: 1)
}
guard !data.isEmpty else { return nil }
return data
}
}
func convertInputValue<T: FixedWidthInteger>(_ inputValue: Data) -> T where T: CVarArg {
let stride = MemoryLayout<T>.stride
assert(inputValue.count % (stride / 2) == 0, "invalid pack size")
let fwInt = T.init(littleEndian: inputValue.withUnsafeBytes { $0.pointee })
let valuefwInt = String(format: "%0\(stride)x", fwInt).capitalized
print(valuefwInt)
return fwInt
}
var inputString = "479F"
var inputValue: Data! = inputString.hexadecimal()
let val: UInt16 = convertInputValue(inputValue) //9F47
inputString = "479F8253"
inputValue = inputString.hexadecimal()
let val2: UInt32 = convertInputValue(inputValue) //53829F47

Print the size (megabytes) of Data in Swift

I have a variable fileData of Data type and I am struggling to find how to print the size of this.
In the past NSData you would print the length but unable to do that with this type.
How to print the size of a Data in Swift?
Use yourData.count and divide by 1024 * 1024. Using Alexanders excellent suggestion:
func stackOverflowAnswer() {
if let data = #imageLiteral(resourceName: "VanGogh.jpg").pngData() {
print("There were \(data.count) bytes")
let bcf = ByteCountFormatter()
bcf.allowedUnits = [.useMB] // optional: restricts the units to MB only
bcf.countStyle = .file
let string = bcf.string(fromByteCount: Int64(data.count))
print("formatted result: \(string)")
}
}
With the following results:
There were 28865563 bytes
formatted result: 28.9 MB
If your goal is to print the size to the use, use ByteCountFormatter
import Foundation
let byteCount = 512_000 // replace with data.count
let bcf = ByteCountFormatter()
bcf.allowedUnits = [.useMB] // optional: restricts the units to MB only
bcf.countStyle = .file
let string = bcf.string(fromByteCount: Int64(byteCount))
print(string)
You can use count of Data object and still you can use length for NSData
Swift 5.1
extension Int {
var byteSize: String {
return ByteCountFormatter().string(fromByteCount: Int64(self))
}
}
Usage:
let yourData = Data()
print(yourData.count.byteSize)
Following accepted answer I've created simple extension:
extension Data {
func sizeString(units: ByteCountFormatter.Units = [.useAll], countStyle: ByteCountFormatter.CountStyle = .file) -> String {
let bcf = ByteCountFormatter()
bcf.allowedUnits = units
bcf.countStyle = .file
return bcf.string(fromByteCount: Int64(count))
}}
Enter your file URL in the following code to get file size in MB, I hope this helps you.
let data = NSData(contentsOf: FILE URL)!
let fileSize = Double(data.count / 1048576) //Convert in to MB
print("File size in MB: ", fileSize)
A quick extension for getting Data size in megabytes as Double.
extension Data {
func getSizeInMB() -> Double {
let bcf = ByteCountFormatter()
bcf.allowedUnits = [.useMB]
bcf.countStyle = .file
let string = bcf.string(fromByteCount: Int64(self.count)).replacingOccurrences(of: ",", with: ".")
if let double = Double(string.replacingOccurrences(of: " MB", with: "")) {
return double
}
return 0.0
}
}
If you want to just see number of bytes, printing the data object directly can give that to you.
let dataObject = Data()
print("Size is \(dataObject)")
Should give you:
Size is 0 bytes
In other words, .count won't be necessary in newer Swift 3.2 or higher.
To get the size of a string, adapted from #mozahler's answer
if let data = "some string".data(using: .utf8)! {
print("There were \(data.count) bytes")
let bcf = ByteCountFormatter()
bcf.allowedUnits = [.useKB] // optional: restricts the units to MB only
bcf.countStyle = .file
let string = bcf.string(fromByteCount: Int64(data.count))
print("formatted result: \(string)")
}
func sizeInMB(data: Data) -> String {
let bytes = Double(data.count)
let megabytes = bytes / (1024 * 1024)
return String(format: "%.2f MB", megabytes)
}
The following takes in a Data object as an argument and calculates the size of that Data in megabytes. The size is then returned as a String with a maximum of 2 decimal places.
count should suit your needs. You'll need to convert bytes to megabytes (Double(data.count) / pow(1024, 2))

Resources