How to read only first part of a file in Swift? - swift

Reading a file into memory as bytes is easy:
let fileURL: URL = ...
Data(contentsOf: fileURL)
But that returns everything. I want to look at the first 16 bytes of a file, without loading the rest into memory. What's the best way to do this in Swift?

Use the FileHandle API, which has read(upToCount:):
let handle = try FileHandle(forReadingFrom: someURL)
let first16Bytes: Data? = try handle.read(upToCount: 16)
try handle.close()
Alternatively, you can read the file asynchronously, in which case you can control how many bytes you read by moving the iterator forward (calling AsyncIterator.next), or use prefix to get the first bytes.

Related

Swift. How to download a file with http URL line by line without loading full file to memory [duplicate]

In the "Platforms State of the Union" video of WWDC2021 at 28:00 it was mentioned that
[Apple] even added support for asynchronously iterating line-by-line through a file
in Foundation for macOS 12/iOS 15 and Swift 5.5.
What is that new API, how can I now asynchronously iterate line-by-line through a file?
The main thing they added that enables this, is AsyncSequence. AsyncSequence is like Sequence, but its Iterator.next method is async throws.
Specifically, you can use URLSession.AsyncBytes.lines to get an AsyncSequence of the lines in a file.
Suppose you are in an async throws method, you can do:
let (bytes, response) = try await URLSession.shared.bytes(from: URL(string: "file://...")!)
for try await line in bytes.lines {
// do something...
}
Note that there is also FileHandle.AsyncBytes.lines, but in the documentation it says:
Rather than creating a FileHandle to read a file asynchronously, you can instead use a file:// URL in combination with the async-await methods in URLSession. These include the bytes(for:delegate:) and bytes(from:delegate:) methods that deliver an asynchronous sequence of bytes, and data(for:delegate:) and data(from:delegate:) to return the file’s entire contents at once.

Performance problem with saving images locally and writing to api in Swift 4

We are transforming images to a byte array and then saving them as a string in a JSON file. This happens locally on the file system. When reading this JSON file from the file system or writing it to the API the performance is really bad. For large quantities of images you have to wait >10 sec, before it is loaded in the ViewController. Is there a better way or library (Swift4) to deal with this? We are already compressing the quality to 0.2. Someone told us to use base64, is this the way to go?
We encode the image like this:ImagesBytes : [[UInt8]]? in to a JSON File with some TextField data.
This is the code
public var formImagesBytes : [[UInt8]]? = [] So this is in the model as a field. The model is a form with fields(strings) and an array of images which is an array of UInt8. This will be first saved locally while decoding it like this:
formImagesBytes = try container.decodeIfPresent([[UInt8]].self, forKey: .formImagesBytes)
Then we JSON encode all the data, including values of the form and image array of bytes. Then we add it with the Filemanager to the local storage on the device. We can open this file and it is including the formdata and an array of bytes in this case.
When loading the data from the local storage in the viewcontroller we again read this local file en decode it to a form model. But processing this file is taking too much time. I hope it is clear. My question is not really about the code, but about: is there a best practice or better way to write the image to this JSON file in order to let the app perform better.
Example output:
{"Images":[[255,216,255,224,0,16,74,70,73,70,0,1,1,0,0,144,0,144,0,0,255,225,0,140,69,120,105,102,0,0,77,77,0,42,0,0,0,8,0,5,1,18,0,3,0,0,0,1,0,1,0,0,1,26,0,5,0,0,0,1,0,0,0,74,1,27,0,5,0,0,0,1,0,0,0,82,1,40,0,3,0,0,0,1,0,2,0,0,135,105,0,4,0,0,0,1,0,0,0,90,0,0,0,0,0,0,0,144,0,0,0,1,0,0,0,144,0,0,0,1,0,3,160,1,0,3,0,0,0,1,0,1,0,0,160,2,0,4,0,0,0,1,0,0,1,82,160,3,0,4,0,0,0,1,0,0,0,253,0,0,0,0,255,237,0,56,80,104,111,116,111,115,104,111,112,32,51,46,48,0,56,66,73,77,4,4,0,0,0,0,0,0,56,66,73,77,4,37,0,0,0,0,0,16,212,29,140,217,143,0,178,4,233,128,9,152,236,248,66,126,255,192,0,17,8,0,253,1,82,3,1,34,0,2,17,1,3,17,1,255,196,0,31,0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,255,196,0,181,16,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,125,1,2,3,0,4,17,5,18,33,49,65,6,19,81,97,7,34,113,20,50,129,145,161,8,35,66,177,193,21,82,209,240,36,51,98,114,130,9,10,22,23,24,25,26,37,38,39,40,41,42,52,53,54,55,56,57,58,67,68,69,70,71,72,73,74,83,84,85,86,87,88,89,90,99,100,101,102,103,104,105,106,115,116,117,118,119,120,121,122,131,132,133,134,135,136,137,138,146,147,148,149,150,151,152,153,154,162,163,164,165,166,167,168,169,170,178,179,180,181,182,183,184,185,186,194,195,196,197,198,199,200,201,202,210,211,212,213,214,215,216,217,218,225,226,227,228,229,230,231,232,233,234,241,242,243,244,245,246,247,248,249,250,255,196,0,31,1,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,255,196,0,181,17,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,119,0,1,2,3,17,4,5,33,49,6,18,65,81,7,97,113,19,34,50,129,8,20,66,145,161,177,193,9,35,51,82,240,21,98,114,209,10,22,36,52,225,37,241,23,24,25,26,38,39,40,41,42,53,54,55,56,57,58,67,68,69,70,71,72,73,74,83,84,85,86,87,88,89,90,99,100,101,102,103,104,105,106,115,116,117,118,119,120,121,122,130,131,132,133,134,135,136,137,138,146,147,148,149,150,151,152,153,154,162,163,164,165,166,167,168,169,170,178,179,180,181,182,183,184,185,186,194,195,196,197,198,199,200,201,202,210,211,212,213,214,215,216,217,218,226,227,228,229,230,231,232,233,234,242,243,244,245,246,247,248,249,250,255,219,0,67,0,28,28,28,28,28,28,48,28,28,48,68,48,48,48,68,92,68,68,68,68,92,116,92,92,92,92,92,116,140,116,116,116,116,116,116,140,140,140,140,140,140,140,140,168,168,168,168,168,168,196,196,196,196,196,220,220,220,220,220,220,220,220,220,220,255,219,0,67,1,34,36,36,56,52,56,96,52,52,96,230,156,128,156,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,230,255,221,0,4,0,22,255,218,0,12,3,1,0,2,17,3,17,0,63,0,223,162,138,40,16,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,1,255,208,223,162,138,40,16,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,1,255,209,223,162,138,40,16,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,1,255,210,223,162,138,40,16,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0,81,69,20,0]],
Here is a screenshot:
Output of the Image in a ByteArray
The JSON you posted explains why it is taking so long to parse. It probably takes a while to parse these huge arrays.
Instead of having each image as an array of bytes it should just be one base64 encoded string for each image. Something like this:
public var formImagesBase64 : [String]?
Here is some Swift code that might help you:
// Encoding Image
let data = image.jpegData(compressionQuality: 1)
let b64string = data?.base64EncodedString()
// Decoding image
let decodedData = Data(base64Encoded: b64string!)
let decodedImage = UIImage(data: decodedData!)

Save and retrieve image in Swift 3.0

I am really confused of all the different possibilities Swift has to save data and retrieve data. Save and retrieve data through userDefaults is easy, but for storing a lot of images this is not the best way to do it. Then there is Core Data, which is I think to complicated to use for such a simple task. I continued to read about DocumentDirectory, I think this is a good way to save and retrieve data. However, all the tutorials are mostly from swift 2.0 and below, and I can not read that very well. Is the document directory the best way to store and retrieve images? I am making an online application which contains downloading images from users. However, the images are all the same from the same user, so I want to store the images offline to save some bandwidth.
I want to store and retrieve images with a path I can specify in a String. Every user has a unique identifier which I want to use as a path. How can I accomplish this?
Saving in your file system is definitely the best approach to use in this scenario.
Have a look to this answer:
https://stackoverflow.com/a/39896556/5109911
if let data = UIImageJPEGRepresentation(image, 0.8) {
let filename = getDocumentsDirectory().appendingPathComponent("copy.png")
try? data.write(to: filename)
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
where image is your UIImage
To Store Image in User default you can just simply create an extension of UI image view like 👇🏻 below Code Click the link to show code
https://i.stack.imgur.com/xEhcr.png
How to Use::- yourimageviewName.saveImage(),OtherImageViewName.loadImage()
https://i.stack.imgur.com/wcrNX.png

How to show pdf data from a filestream

I am working against a REST API where you can make a call and then receive a image or pdf. I am using URLSession.shared.dataTask to make the call and when there is a image the call is a success (but it takes quite a long time, more then 5 seconds) and I can show the image in a UIImageView. But when there is a pdf, I don't know how to handle the result. The API returns the image / pdf as a ”filestream”.
When I print the data to the console it prints the size (bytes) and its the same size as in the server so in some way I have the correct data, I just don't know how to view the pdf.
I am using swift 3, iOS 10, xcode 8.
First of all you may want to ask your question into two part. Please edit it and ask the second part again.
There are two parts in this topic
1. Downloading the PDF and save it in File System
2. Get the pdf that saved in File System and read it using UIWebView or UIDocumentInteractionController
So, I will explain for the first one.
The first one can be done if you use REST HTTP client : Alamofire : Elegant HTTP Networking in Swift. So no need to use URLSession for such case and you will have to write so many lines if you do. It's simple and easy. So, I want you to try it. If you need URLSession instead, leave comment.
So how to download pdf using Alamofire :
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
//.documentDirectory means it will store in Application Document Folder
let fileURL = documentsURL.appendPathComponent("data.pdf")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(urlString, to: destination).response { response in
print(response)
if response.error == nil, let filePath = response.destinationURL?.path {
// do anything you want with filePath here.
// Here what you have to do Step 2. (Read the file that you downloaded)
}
}
This download procedure doesn't include requesting download link with encoded parameters. It was just simple way.

VLCKit Subtitle not showing

I use VLCKit in swift, so i created customize video player view
and i have external subtitles for movies from links, i read files from server and convert it so string
do {
let text = try NSString(contentsOfURL: NSURL(string: self.subtitleUrl)!, encoding: NSUTF8StringEncoding)
self.mediaPlayer.openVideoSubTitlesFromFile(text as String)
}catch {
print("Error")
}
and i called a function named "openVideoSubTitlesFromFile"
in player but not working
anyone can give me a solution
This method (which will be deprecated in the next major release of VLCKit) does only accept local file paths, no remote URLs. You need to download the subtitles file, cache it locally and provide the path of the stored file to the method. Additionally, you may only use this method after playback started.