how to write bytes data in file swift - ios

data coming from server is in ex.2035bytes I want to write this data in to file I am writing but data is not showing
Alamofire.request(url , method: .post, parameters: Parameters as? [String: Any] , encoding: URLEncoding.httpBody, headers: [
"Content-Type": "application/x-www-form-urlencoded"
]).responseData{ (response) in
print(response)
print(response.result.value!)
print(response.result.description)
guard let jsonData = response.result.value ,response.result.isSuccess else {
didFail(response.result.error!)
return
}
guard let id = ApplicantModel.shared.applicationId else {
return
}
let file = "application_\(id)" //this is the file. we will write to and read from it
let documentsPath1 = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
//let logsPath = documentsPath1.appendingPathComponent("f")
let fileURL = documentsPath1.appendingPathComponent("file")
let data: Data = response.result.value!
// print(logsPath)
//writing
do {
try FileManager.default.createDirectory(at: documentsPath1 as URL, withIntermediateDirectories: true, attributes: nil)
//try data.write(to: fileURL!, atomically: false, encoding: .utf8)
// try data.write(to: fileURL!, options: Data.WritingOptions.atomic)
try data.write(to: fileURL!) )
}
catch {/* error handling here */}
let json = JSON(jsonData)
didFinish(json)
}
here I am creating file and writing data in to it but its not showing

You can use this function to write data. Code is self explanatory, but I tried to make it more clear.
func writeToFile(data: Data, fileName: String){
// get path of directory
guard let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
return
}
// create file url
let fileurl = directory.appendingPathComponent("\(fileName).txt")
// if file exists then write data
if FileManager.default.fileExists(atPath: fileurl.path) {
if let fileHandle = FileHandle(forWritingAtPath: fileurl.path) {
// seekToEndOfFile, writes data at the last of file(appends not override)
fileHandle.seekToEndOfFile()
fileHandle.write(data)
fileHandle.closeFile()
}
else {
print("Can't open file to write.")
}
}
else {
// if file does not exist write data for the first time
do{
try data.write(to: fileurl, options: .atomic)
}catch {
print("Unable to write in new file.")
}
}
}

You have a problem with creating the directory and naming the file you need to change it like this
let file = "application_\(id)"
let documentsPath1 = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let logsPath = documentsPath1.appendingPathComponent("file")
do
{
try FileManager.default.createDirectory(atPath: logsPath!.path, withIntermediateDirectories: true, attributes: nil)
let fileURL = logsPath?.appendingPathComponent(file)
try data.write(to: fileURL!)
}
catch let error as NSError
{
NSLog("Unable to create directory \(error.debugDescription)")
}

I think you create a wrong folder. It due to you can't save that file in your folder. You can look at my example. Which I create 1 folder to contain files.
Step1: Get document path
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
Step2: create a destination URL. Where you would like to store
let url = paths[0].appendingPathComponent(self.fileImagejpeg)
Step 3: Write data in files
try? data.write(to: url)
You can use try catch to get log when write failure

Related

Saving video in locally (directory) in Swift?

I try to save given video locally after then I need those saved videos for playing video in my app. I can't handle the saving video. Here is my saving try :
func saveVideoDocumentDirectory(url : URL){
let fileManager = FileManager.default
let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(".MOV")
do{
let videoData = try Data(contentsOf: url)
fileManager.createFile(atPath: paths as String, contents: videoData, attributes: nil)
}catch{
//
}
}
here is the get file try
func getVideo(){
let fileManager = FileManager.default
let videoPAth = (self.getDirectoryPath() as NSString).appendingPathComponent(".MOV")
if fileManager.fileExists(atPath: videoPAth){
print(videoPAth)
play(url: URL(string: videoPAth)!)
}else{
print("No Video")
}
}
here is my play video func :
func play(url : URL)
{
let player = AVPlayer(url: url)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
present(playerViewController, animated: true)
{
playerViewController.player!.play()
}
}
Instead of Filemanager.createFile(), try using write instead.
let videoData = try Data(contentsOf: url)
try videoData.write(to: paths, options: .atomic)
Also, I recommend creating a folder first (from this answer).
extension URL {
static func createFolder(folderName: String) -> URL? {
let fileManager = FileManager.default
// Get document directory for device, this should succeed
if let documentDirectory = fileManager.urls(for: .documentDirectory,
in: .userDomainMask).first {
// Construct a URL with desired folder name
let folderURL = documentDirectory.appendingPathComponent(folderName)
// If folder URL does not exist, create it
if !fileManager.fileExists(atPath: folderURL.path) {
do {
// Attempt to create folder
try fileManager.createDirectory(atPath: folderURL.path,
withIntermediateDirectories: true,
attributes: nil)
} catch {
// Creation failed. Print error & return nil
print(error.localizedDescription)
return nil
}
}
// Folder either exists, or was created. Return URL
return folderURL
}
// Will only be called if document directory not found
return nil
}
}
Then, you can save like this:
guard let folderURL = URL.createFolder(folderName: "StoredVideos") else {
print("Can't create url")
return
}
let permanentFileURL = folderURL.appendingPathComponent(nameOfYourFile).appendingPathExtension("MOV")
let videoData = try Data(contentsOf: url)
try videoData.write(to: permanentFileURL, options: .atomic)
This will save you the hassle of NSSearchPathForDirectoriesInDomains.

iOS file path is changing at every launch/rerun the application

I have an issues in changing the file path at every launch of the app.
I have a file("AppConstant.json") in application bundle, and this file I need to copy into application document directory. I am successfully saving "AppConstant.json" file inside the created user folder "MyFolder" on Document directory.
But the problem is when I relaunch the application second time, it's not showing the same path. Also I am using relativepath, but still it not getting.
here is the code
// calling the directory
let stringAppConstant = copyFileFromBundleToDocumentDirectory(resourceFile: "AppConstant", resourceExtension: "json")
// saving or get exit file path
func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) -> String
{
var stringURLPath = "Error_URLPath"
let fileManager = FileManager.default
let docURL = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
let fileName = "\(resourceFile).\(resourceExtension)"
guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
return stringURLPath
}
if !fileManager.fileExists(atPath: newDestPath.path) {
do {
try fileManager.createDirectory(atPath: newDestPath.path,withIntermediateDirectories: true, attributes: nil)
print("Created folder successfully in :::", newDestPath.path)
} catch {
print("Error in creating folder :::",error.localizedDescription);
}
}
else {
print("Folder is already exist!")
}
if fileManager.fileExists(atPath: fullDestPath.path) {
print("File is exist in ::: \(fullDestPath.path)")
stringURLPath = fullDestPath.path
}
else {
do {
try fileManager.copyItem(atPath: sourcePath, toPath: fullDestPath.path)
print("Saved file successfully in :::", fullDestPath.path)
stringURLPath = fullDestPath.path
} catch {
print("Error in creating file ::: \(error.localizedDescription)")
}
}
return stringURLPath
}
Please help me, where I need to save the path in Sandbox. Is this right way what I implemented.
I am running in device and simulator, both path are different while relaunch
this is the path for first time launch:
/var/mobile/Containers/Data/Application/81B568A7-0932-4C3E-91EB-9DD62416DFE8/Documents/MyFolder/AppConstant.json
relaunch the application I am getting new path:
/var/mobile/Containers/Data/Application/3DAABAC3-0DF5-415B-82A5-72B204311904/Documents/MyFolder/AppConstant.json
NOTE: I create a sample project and I use this same code and it's working. But in existing project it's not working. I am using the same bundle id and profile only for both sample and project. Checked the file added reference, settings, version all are same.
Any idea?
The behavior that the container path changes periodically is normal.
These lines
let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
let fileName = "\(resourceFile).\(resourceExtension)"
guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
return stringURLPath
}
contain a lot of mistakes
URL(string is the wrong API for file paths, it's URL(fileURLWithPath).
The second parameter of path(forResource:ofType:) must not have a leading dot.
The API absoluteString is wrong as parameter of URL(fileURLWithPath
Not a real mistake but don't use NSURL in Swift.
It's highly recommended to use always the URL related API to concatenate paths and get the documents folder from FileManager. Further it's good practice to make the method throw the real error rather than returning a meaningless literal string. And NSSearchPathForDirectoriesInDomains is outdated and should not be used in Swift.
func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) throws -> URL
{
let sourceURL = Bundle.main.url(forResource: resourceFile, withExtension: resourceExtension)!
let fileManager = FileManager.default
let destFolderURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("MyFolder")
let fullDestURL = destFolderURL.appendingPathComponent(resourceFile).appendingPathExtension(resourceExtension)
if !fileManager.fileExists(atPath: destFolderURL.path) {
try fileManager.createDirectory(at: destFolderURL, withIntermediateDirectories: true, attributes: nil)
print("Created folder successfully in :::", destFolderURL.path)
try fileManager.copyItem(at: sourceURL, to: fullDestURL)
print("Saved file successfully in :::", fullDestURL.path)
} else {
print("Folder already exists!")
if fileManager.fileExists(atPath: fullDestURL.path) {
print("File exists in ::: \(fullDestURL.path)")
} else {
try fileManager.copyItem(at: sourceURL, to: fullDestURL)
print("Saved file successfully in :::", fullDestURL.path)
}
}
return fullDestURL
}
Edit 1:
Hi I created the new project and use the same code I posted in main, and it's working. But in the real project it not working.
Not sure what exactly going on in your project, try to debug it. It's part of development as well. :)
If you are in hurry to fix this issue in this weekend try to use the following code snippet.
// collect data from bundle
let constFileURL = Bundle.main.url(forResource: "AppConst", withExtension: "json")!
let data = try! Data(contentsOf: constFileURL)
// try to write data in document directory
do {
let constFileURL = try saveFileInDocumentDirectory(filePath: "MyFolder/AppConst.json", data: data)
// use your `constFileURL`
} catch (let error as FileOperationError) {
switch error {
case .fileAlreadyExists(let url):
let data = try! Data(contentsOf: url)
print(String(data: data, encoding: .utf8))
case .IOError(let error):
print("IO Error \(error)")
}
} catch {
print("Unknown Error \(error)")
}
// Helpers
enum FileOperationError: Error {
case fileAlreadyExists(url: URL)
case IOError(Error)
}
func saveFileInDocumentDirectory(filePath: String, data: Data) throws -> URL {
// final destination path
let destURLPath = fullURLPathOf(filePath, relativeTo: .documentDirectory)
// check for file's existance and throw error if found
guard FileManager.default.fileExists(atPath: destURLPath.path) == false else {
throw FileOperationError.fileAlreadyExists(url: destURLPath)
}
// Create Intermidiate Folders
let intermidiateDicPath = destURLPath.deletingLastPathComponent()
if FileManager.default.fileExists(atPath: intermidiateDicPath.path) == false {
do {
try FileManager.default.createDirectory(at: intermidiateDicPath, withIntermediateDirectories: true, attributes: nil)
} catch {
throw FileOperationError.IOError(error)
}
}
// File Writing
do {
try data.write(to: destURLPath, options: .atomic)
} catch {
throw FileOperationError.IOError(error)
}
return destURLPath
}
func fullURLPathOf(_ relativePath: String, relativeTo dic:FileManager.SearchPathDirectory ) -> URL {
return FileManager.default.urls(for: dic, in: .userDomainMask).first!.appendingPathComponent(relativePath)
}
Original Answer
Why don't you just return "MyFolder/\(fileName)" on successful file operation? If you need to access the path later you can always do that using FileManager APIs.
let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let constFilePath = docDir.appendingPathComponent("MyFolder/\(fileName)")
// Access const file data
do {
let fileData = try Data(contentsOf: constFilePath)
// Use you data for any further checking
} catch {
// Error in reading file data
print("Error in file data access : \(error)")
}

Change file extension to .zip and unzip file from document folder

Using Alamofire for downloading. File is being downloaded in app document folder, in attachment, there is .hub file.
I need to change .hub file to .zip than i need to unzip this file for audio file.
Code for downloading ->
func getAudioFileFromServer(url: String, uuid: String) {
let fileURL = URL(string: url)
var request = URLRequest(url:fileURL!)
request.setValue("myapikey", forHTTPHeaderField: "x-api-key")
let destination = DownloadRequest.suggestedDownloadDestination()
Alamofire.download(request, to: destination).validate().responseData { response in
debugPrint(response)
print(response.destinationURL!)
}
}
Response from server ->
file:///var/mobile/Containers/Data/Application/FC5F17C4-E8D3-4406-926A-97EB9447D87B/Documents/'bac6151ffbe74140a31408938c91fa33.hub'
To rename a file, use the function moveItem(atPath:toPath:) of FileManager
To unzip, the easiest way would be to integrate some zip library: https://github.com/ZipArchive/ZipArchive or https://github.com/marmelroy/Zip
After downloading the file change the extension to .zip with "saveFileInDirectory" method and with success block we can get the main file.
self.saveFileInDirectory(data: responce.result.value, fileName: "\(name!).zip", successblock: { (path) in
print(path!)
var filepath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0]
let url = URL(fileURLWithPath: filepath)
do {
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
let done = SSZipArchive.unzipFile(atPath: path!, toDestination: url.path)
if done{
let items = try FileManager.default.contentsOfDirectory(atPath: url.path)
print(items)
let destinationUrl = url.appendingPathComponent(items[0])
print(destinationUrl)
}
} catch let error as NSError{
print(error)
}
})
func saveFileInDirectory(data: Data?, fileName: String?, successblock: #escaping (_ path: String?) -> Void) { // To add the image to cache for given identifier.
let paths = NSSearchPathForDirectoriesInDomains( .documentDirectory, .userDomainMask, true)[0] as String
let path = paths.appending("/\(fileName!)")
if (FileManager.default.fileExists(atPath: path)) {
try! FileManager.default.removeItem(atPath: path)
} else {
do {
try data?.write(to: URL(fileURLWithPath: path, isDirectory: false))
successblock(path)
} catch {
successblock(nil)
print("Error while caching the data in cache folder.")
}
}}

Swift downloadTask with request file download not working

I'm trying to download a file from a server, by sending the id of the file. I tried several things but the file is getting downloaded as CFNetworkDownload.tmp file.
I want it to save as the file that exists. The file type can be PNG,JPEG,PDF,DOCX,PPTX,XLSX. Tried many things but in vain. I'm sure it must be something simple i'm missing to understand here
Tried the below. Difference being in most of the examples, the file name is in the URL. But I send id and get file in response.
How to download file in swift?
How To Download Multiple Files Sequentially using NSURLSession downloadTask in Swift
Below is my code.
func downloadFile(id : String, fileName : String) -> Void {
let session = URLSession.shared
let url = URL(string: qaDownloadURL+id)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
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("Success: \(statusCode)")
}
do {
// let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
//
// self.savePath = documentsUrl!.absoluteString + "/" + fileName
//
// let fileURL = URL(fileURLWithPath: self.savePath)
//
// let dataFromURL = NSData(contentsOf: tempLocalUrl)
// dataFromURL?.write(to: fileURL, atomically: true)
var documentsDirectory: String?
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if paths.count > 0
{
documentsDirectory = paths.first!
}
self.savePath = documentsDirectory!// + "/" + fileName
let fileURL = URL(fileURLWithPath: self.savePath)
let dataFromURL = NSData(contentsOf: tempLocalUrl)
dataFromURL?.write(to: fileURL, atomically: true)
// try FileManager.default.copyItem(at: tempLocalUrl, to: fileURL)
DispatchQueue.main.async {
let documentController = UIDocumentInteractionController.init(url: fileURL)
documentController.delegate = self
documentController.presentPreview(animated: true)
}
} catch (let writeError) {
print("error writing file \(self.savePath) : \(writeError)")
}
} else {
print("Failure: %#", error?.localizedDescription);
}
}
task.resume()
}
You cannot write data into a location which represents a directory, you need to specify the full path including the file name.
Using modern URL related API you can replace the entire do block with
do {
let documentFolderURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURL = documentFolderURL.appendingPathComponent(fileName)
try FileManager.default.copyItem(at: tempLocalUrl, to: fileURL)
DispatchQueue.main.async {
let documentController = UIDocumentInteractionController.init(url: fileURL)
documentController.delegate = self
documentController.presentPreview(animated: true)
}
}
or use URLSessionDataTask which returns the raw data rather than downloading the file to a temporary location and save the Data directly for example
let task = session.dataTask(with: request) { (data, response, error) in
guard error == nil else {
print(error!)
return
}
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Success: \(statusCode)")
}
do {
let documentFolderURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURL = documentFolderURL.appendingPathComponent(fileName)
try data!.write(to: fileURL)
DispatchQueue.main.async {
let documentController = UIDocumentInteractionController.init(url: fileURL)
documentController.delegate = self
documentController.presentPreview(animated: true)
}
} catch {
print("error writing file \(fileName) : \(error)")
}
}
task.resume()
If this does not work the error is related to somewhere else.

Swift data write to Document directory maintaining directory structure

I am downloading file from firebase. let say the request url is following
social-cam-storage/albm-72/owner-2/1484043313786.jpeg
i can download the file using the following code
func downloadFile(url : String) {
let storageR = FIRStorage.storage().reference(withPath: url)
let maxSize : Int64 = 3 * 1024 * 1024 // 3MB
storageR.data(withMaxSize: maxSize) { (data, error) in
if error != nil {
print(error.debugDescription)
return
}
print(data!)
}
}
Now i need to store this data maintaining the directory structure of the url
I have tried
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
print(FileManager.default.createFile(atPath: "\(documentsURL.absoluteString)/\(url)", contents: data!, attributes: nil))
but i am getting false
so how to fix this or is there any other way to save??
Have you tried something like this? :
If you have the exact path already as a string:
try? data.write(to: URL(fileURLWithPath: path), options: [.atomic])
If you need the path there are a few methods:
func saveFile() {
let filePath = getDocumentsURL().absoluteString.appending(path)
try? data.write(to: URL(fileURLWithPath: filePath), options: [.atomic])
}
func getDocumentsURL() -> URL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return documentsURL
}
You could also just try saving the filename, and then loading later when you need it:
func fileInDocumentsDirectory(_ filename: String) -> String {
let fileURL = getDocumentsURL().appendingPathComponent(filename)
return fileURL.path
}
// To save file
func saveFile(data: Data) {
let fileName:String = "createUniqueFileName"
let filePath = fileInDocumentsDirectory(fileName)
saveData(data, filePath)
}
// To load file with saved file name
func loadFile(fileName: String) {
if let loadedData = loadData(fileName) {
// Handle data however you wish
}
}
func saveData(_ data: Data, path: String ) {
try? data.write(to: URL(fileURLWithPath: path), options: [.atomic])
}
func loadData(_ path: String) -> Data? {
let data:Data? = try? Data(contentsOf: URL(fileURLWithPath: path))
return data
}
Have you tried using the built in "download to file" API in Firebase Storage?
// Create a reference to the file you want to download
let fileURL = storage.reference(withPath: url)
// Create local filesystem URL
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = ...
// Download to the local filesystem
let downloadTask = islandRef.write(toFile: fileURL) { url, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Local file URL is returned
}
}

Resources