How to update deprecated NSKeyedArchiver/NSKeyedUnarchiver code? - ios

I am using the SKCache library to cache data in my app and I have two warnings that say that NSKeyedArchiver and NSKeyedUnarchiver related code is deprecated and should be updated. I gave it my best attempt, but I wanted to check here to get the correct answer as this is an important function of my app and I do not want to mess it up.
private func save(object: SKObject) throws {
let fileManager = FileManager.default
do {
let cacheDirectory = try fileManager.url(for: .cachesDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileDirectory = cacheDirectory.appendingPathComponent("spacekit")
var fileDir = fileDirectory.absoluteString
let range = fileDir.startIndex..<fileDir.index(fileDir.startIndex, offsetBy: 7)
fileDir.removeSubrange(range)
try createFolderIfNeeded(atPath: fileDir, absolutePath: fileDirectory)
let fileFormatedName = object.key.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? object.key
let convertedFileName = convertToBase64(withString: fileFormatedName).suffix(45).map({ String($0) }).joined()
let fileName = fileDirectory.appendingPathComponent(convertedFileName)
if !fileManager.fileExists(atPath: fileName.absoluteString) || object.isUpdated {
let data = NSKeyedArchiver.archivedData(withRootObject: object)
try? data.write(to: fileName)
}
} catch {
throw Operations.saveFail
}
}
Specifically on this line:
let data = NSKeyedArchiver.archivedData(withRootObject: object)
I was able to come up with this amended version, but I'm not sure if it is correct:
let data = try NSKeyedArchiver.archivedData(withRootObject: object, requiringSecureCoding: false)
Lastly,
private func load() throws {
let fileManager = FileManager.default
do {
let cacheDirectory = try fileManager.url(for: .cachesDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileDirectory = cacheDirectory.appendingPathComponent("spacekit")
var fileDir = fileDirectory.absoluteString
let range = fileDir.startIndex..<fileDir.index(fileDir.startIndex, offsetBy: 7)
fileDir.removeSubrange(range)
try createFolderIfNeeded(atPath: fileDir, absolutePath: fileDirectory)
let paths = try fileManager.contentsOfDirectory(atPath: fileDir)
for path in paths {
if let object = NSKeyedUnarchiver.unarchiveObject(withFile: fileDir + path) as? SKObject {
if !object.isExpired {
add(object: object)
} else {
try? fileManager.removeItem(atPath: fileDir + path)
}
}
}
} catch {
throw Operations.loadFail
}
}
Specifically on this line:
if let object = NSKeyedUnarchiver.unarchiveObject(withFile: fileDir + path) as? SKObject {
I wasn't able to figure anything out on my own related to this as the new method does not seem to have a parameter for a file directory. I appreciate the help in advance.
Updated:
private func load() throws {
let fileManager = FileManager.default
do {
let cacheDirectory = try fileManager.url(for: .cachesDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
let fileDirectory = cacheDirectory.appendingPathComponent("spacekit")
var fileDir = fileDirectory.absoluteString
let range = fileDir.startIndex..<fileDir.index(fileDir.startIndex, offsetBy: 7)
fileDir.removeSubrange(range)
try createFolderIfNeeded(atPath: fileDir, absolutePath: fileDirectory)
let paths = try fileManager.contentsOfDirectory(atPath: fileDir)
for path in paths {
let objData = try Data(contentsOf: URL(fileURLWithPath: fileDir + path))
if let object = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [SKObject.self], from: objData) as? SKObject {
if !object.isExpired {
add(object: object)
} else {
try? fileManager.removeItem(atPath: fileDir + path)
}
}
}
} catch {
throw Operations.loadFail
}
}

As for the first function, you've updated it correctly.
As for the second one,
func unarchivedObject(ofClasses classes: [AnyClass], from data: Data) throws -> Any? now requires Data param.
And you have a path to the file where that Data is stored.
So you can update the code like this:
let objData = try Data(contentsOf: URL(fileURLWithPath: fileDir + path))
if let object = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [MyClass.self], from: objData) {
// check if object.isExpired, etc
}
I'd like to notice that if it's a legacy code and you have to support it, you could add some kind of unit test for this archiving/unarchiving, make it work, and then quickly update the deprecated code while being sure that the test works.

Related

Swift -Delete Custom Folder From Documents Directory

When recording videos I create a custom folder using "/MyFolder" like this:
guard let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else { return }
let dirPath = "\(documentsPath)/MyFolder/Videos_\(UUID().uuidString).mp4"
let outputFileURL = URL(fileURLWithPath: dirPath)
// ...
Now I have a function to delete just custom folders:
func deleteCustom(folder: String) {
let fileManager = FileManager.default
guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let customFolder = documentsDirectory.appendingPathComponent(folder)
guard let filePaths = try? fileManager.contentsOfDirectory(at: customFolder, includingPropertiesForKeys: nil, options: []) else { return }
for filePath in filePaths {
try? fileManager.removeItem(at: filePath)
}
}
In the function parameter should I pass in "MyFolder" or "/MyFolder"?
In the function parameter should I pass in "MyFolder" or "/MyFolder"?
"MyFolder", because appendingPathComponent adds / automatically.

Get the Attributes of every item in the Document Directory iOS Swift

I've been trying to get Attributes such as File Type, Creation Date, File Size of all the items in the Document Directory.
Here is the code that I've been using, but it returns me just "NSFileTypeDirectory"
let filemgr = FileManager.default
do
{
let attribs: NSDictionary? = try filemgr.attributesOfItem(
atPath: documentDirectoryPath!) as NSDictionary
if let fileattribs = attribs
{
let type = fileattribs["NSFileType"] as! String
print("File type \(type)")
}
}
catch
{
print(error)
}
I think its returning the attributes of Document folder.
try this
let fileManager = FileManager.default
let documentdirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
let filePath = documentdirectory?.appendingPathComponent("your file name").path
do {
let fileAttribute = try fileManager.attributesOfItem(atPath: filePath!)
let fileSize = fileAttribute[FileAttributeKey.size] as! Int64
let fileType = fileAttribute[FileAttributeKey.type] as! String
let filecreationDate = fileAttribute[FileAttributeKey.creationDate] as! Date
} catch let error {
print(error.localizedDescription)
Based on #KarthikeyanBose's code: To get the infos for all the files in the Documents directory do this:
let fileManager = FileManager.default
if let documentsURLs = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
do {
let fileNames = try fileManager.contentsOfDirectory(atPath: documentsURLs.path)
for fileName in fileNames {
let fileURL = documentsURLs.appendingPathComponent(fileName)
let fileAttribute = try fileManager.attributesOfItem(atPath: fileURL.path)
let fileSize = fileAttribute[FileAttributeKey.size] as! Int64
let fileType = fileAttribute[FileAttributeKey.type] as! String
let filecreationDate = fileAttribute[FileAttributeKey.creationDate] as! Date
let fileExtension = fileURL.pathExtension;
print("Name: \(fileName), Size: \(fileSize), Type: \(fileType), Date: \(filecreationDate), Extension: \(fileExtension)")
}
} catch {
print("Error: \(error)")
}
} //Handle this "else" error too, even though this really shouldn't happen
This code prints e.g.:
Name: Deadpool.png, Size: 39225, Type: NSFileTypeRegular, Date: 2019-05-27 11:03:03 +0000, Extension: png

How to update an image's file directory path in Swift 3.0?

Currently I am storing the file path location of an image in the Documents directory by creating the directory path and writing the image to that directory like so:
func createDirectory() -> String
{
let fileManager: FileManager = FileManager.default
let dirPaths: [URL] = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
let docsDir: URL = dirPaths[0]
let appDirPath = docsDir.appendingPathComponent("MyApp").path
do
{
try fileManager.createDirectory(atPath: appDirPath,
withIntermediateDirectories: true, attributes: nil)
}
catch let error as NSError
{
print("Error: \(error.localizedDescription)")
}
return appDirPath
}
func getPath() -> String
{
let paths: [String] = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory: String = paths[0].appending("/MyApp")
return documentsDirectory
}
func saveToDocuments()
{
let fileManager: FileManager = FileManager.default
let imageData: Data = UIImageJPEGRepresentation(globalImage, 1.0)!
let dateTime: Date = Date()
let formatter: DateFormatter = DateFormatter();
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss";
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale!
globalTimeStamp = formatter.string(from: dateTime);
globalTimeStamp = globalTimeStamp(of: " ", with: "_")
globalTimeStamp = globalTimeStamp + ".jpg"
let documentsPath: URL = try! fileManager.url(for: .documentDirectory, in: .userDomainMask,
appropriateFor: nil, create: false)
let imagePath: URL = documentsPath.appendingPathComponent("MyApp/" + globalTimeStamp)
let path = imagePath.path
let success = fileManager.createFile(atPath: path as String, contents: imageData, attributes: nil)
}
The timeStamp represents the image name, which gets appended to the path of my App's directory, and written to file as seen with createFile.
My question is, if I wanted to update the globalTimeStamp somewhere else in my app, how can I update the file path to point to that SAME image instead of having to re-create another file path that points to that SAME image?
Thanks!
I don't quiet understand why you want to save the same file again. It is already saved. If you have changed the image in the meantime, I suggest you delete the old one and save the new one. Re-saving the same image because of some time being elapsed seems unnecessary for me. Instead, I'd rename the image by moving it to the new filepath.
This is how I save and delete images in one of my apps under unique filenames which I then can pass around my app. I know I'm not implementing proper error handling yet but that code is from an unfinished app:
func saveImageFile(_ image: UIImage) -> String {
let imageName = FileManager.uniqueImageName()
try! UIImageJPEGRepresentation(image, 1.0)?.write(to: imageName.imageUrl)
return imageName
}
func deleteFile(named fileName: String?) {
guard let imagePath = fileName?.imageUrl.relativePath else { return }
guard FileManager.default.fileExists(atPath: imagePath) else { return }
try! FileManager.default.removeItem(atPath: imagePath)
}
extension FileManager {
private var imageDirectory: URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths.first!
return documentsDirectory
}
static func uniqueImageName() -> String {
//Try until we get a unique filename
while(true){
//Generate a UUID
let uuid = UUID().uuidString
//Create the path for the file we want to use
let filePath = uuid.imageUrl.relativePath
//Check if the file already exists
if !FileManager.default.fileExists(atPath: filePath){
return uuid
}
}
}
static func urlFor(imageNamed imageName: String) -> URL {
return FileManager.default.imageDirectory.appendingPathComponent(imageName).appendingPathExtension("jpeg")
}
}
extension String {
var imageUrl: URL {
return FileManager.urlFor(imageNamed: self)
}
}

How to save/retrieve dictionary from file using SwiftyJSON

I'm trying to make the conversion from Objc to swift and have had better days.
I have a class with a dictionary:
collaborationDictionary:[String:Set<String>]
I am trying to write/read this dictionary to/from a file and just can't quite seem to make it work. I have to save the dictionary using the following JSON structure and I have to use SwiftyJSON.
{ "Collaborations" : {
"5604" : [
"whiteboard.png",
"VID_20161123_135117.3gp",
"Photo_0.jpeg"]
"5603" : [
"VID_20161123_135117.3gp"],
"5537" : [
"Screenshot_20151212-132454.png",
"VID_20161202_083205.3gp",
"VID_20161123_135117.3gp",
"Photo_0.jpeg",
"Screenshot_20151212-132428.png",
"Screenshot_20151212-132520.png",
"IMG_20161017_132105.jpg",
"whiteboard.png"]}
}
I don't have any real problem with finding/retrieving the file or writing the file. I just can't quite figure out how to manually load SwiftyJSON. I need to have a JSON object called "Collaborations" at the top. It needs to contain a dictionary of collaboration IDs (5604, 5603...). Each collaboration contains an array of string (filenames). I'm including the code I'm using to read/write the file but I need help with the SwiftyJSON library.
This is the member data member I'm using to store the above data:
These are the functions I need to finish:
private var collaborationDictionary:[String:Set<String>] = [:]
func getUploadedFileSet() {
collaborationDictionary = [:]
let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)
if FileManager.default.fileExists(atPath: (jsonFileURL?.absoluteString)!) {
do {
let data = try Data(contentsOf: jsonFileURL!, options: .alwaysMapped)
let json = JSON(data: data)
// ************************************************
// NEED HELP START
// NOW WHAT???? What is the SwiftyJSON code
?????????????????????????
// NEED HELP END
// ************************************************
} catch let error {
print(error.localizedDescription)
}
}
}
func saveUploadedFilesSet() {
let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)
do {
let dirExists = FileManager.default.fileExists(atPath: (appURL?.absoluteString)!)
if !dirExists {
try FileManager.default.createDirectory(atPath: (appURL?.absoluteString)!, withIntermediateDirectories: false, attributes: nil)
}
// ************************************************
// NEED HELP START
// NOW WHAT???? What is the SwiftyJSON code
?????????????????????????
// NEED HELP END
// ************************************************
// Write to file code - haven't written it yet but that should be easy
} catch let error as NSError {
print(error.localizedDescription);
}
}
Any direction would be greatly appreciated. Thanks!
EDIT
I was able to figure out how to load the supplied JSON structure from file. Here is the code:
func getUploadedFileSet() {
let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)
if FileManager.default.fileExists(atPath: (jsonFileURL?.absoluteString)!) {
do {
let data = try Data(contentsOf: jsonFileURL!, options: .alwaysMapped)
let json = JSON(data: data)
if json != nil {
for (key, subJson) in json[kCollaborations] {
let stringArray:[String] = subJson.arrayValue.map { $0.string! }
let stringSet = Set(stringArray)
collaborationDictionary.updateValue(stringSet, forKey: key)
}
} else {
print("Could not get json from file, make sure that file contains valid json.")
}
} catch let error {
print(error.localizedDescription)
}
}
I still haven't figured out how to save the collaborationDictionary object to file. My biggest problem is figuring out how to put in the "Collaborations" key. Any ideas?
I finally got this to work. The biggest problem was that I couldn't convert collaborationDictionary to JSON. I finally had to convert it to a dictionary of arrays vs dictionary of sets. Here are the 2 methods:
// **************************************************************************
func getUploadedFileSet() {
let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)
if FileManager.default.fileExists(atPath: (jsonFileURL?.absoluteString)!) {
do {
let data = try Data(contentsOf: jsonFileURL!, options: .alwaysMapped)
let json = JSON(data: data)
if json != nil {
for (key, subJson) in json[kCollaborations] {
let stringArray:[String] = subJson.arrayValue.map { $0.string! }
let stringSet = Set(stringArray)
collaborationDictionary.updateValue(stringSet, forKey: key)
}
} else {
print("Could not get json from file, make sure that file contains valid json.")
}
} catch let error {
print(error.localizedDescription)
}
}
}
// **************************************************************************
func saveUploadedFilesSet() {
let documentsURL = URL(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let appURL = documentsURL?.appendingPathComponent(APP_DISTINGUISHED_NAME)
let jsonFileURL = appURL?.appendingPathComponent(UPLOADED_ITEMS_DB_JSON)
let adjustedJSONFileURL = URL(fileURLWithPath:(jsonFileURL?.absoluteString)!)
do {
let dirExists = FileManager.default.fileExists(atPath: (appURL?.absoluteString)!)
if !dirExists {
try FileManager.default.createDirectory(atPath: (appURL?.absoluteString)!, withIntermediateDirectories: false, attributes: nil)
}
// Convert set elements to arrays
var convertedCollaborationDictionary: [String:[String]] = [:]
for (sessionID, fileNameSet) in collaborationDictionary {
let array = Array(fileNameSet)
convertedCollaborationDictionary.updateValue(array, forKey: sessionID)
}
let json: JSON = JSON(convertedCollaborationDictionary)
let fullJSON: JSON = [kCollaborations:json.object]
let data = try fullJSON.rawData()
try data.write(to: adjustedJSONFileURL, options: .atomic)
} catch let error as NSError {
print(error.localizedDescription);
}
}
If you dig into the source, SwiftyJSON wraps JSONSerialization, which can both be initialized and converted back to Data which is knows how to read and write itself from disk:
func readJSON() -> JSON? {
guard let url = Bundle.main.url(forResource: "data", withExtension: "json"),
let data = try? Data(contentsOf: url) else {
return nil
}
return JSON(data: data)
}
func write(json: JSON, to url: URL) throws {
let data = try json.rawData()
try data.write(to: url)
}
Note that you can load your static data from anywhere including your Bundle, but you can only write to the sandbox (ie the Documents directory). You may wish to copy from your Bundle to the documents directory on first run if you are planning on reading/writing to the same file.
Also your sample JSON is bad (lint it). You need a comma after "Photo_0.jpeg"]

How to check if a file exists in the Documents directory in Swift?

How to check if a file exists in the Documents directory in Swift?
I am using [ .writeFilePath ] method to save an image into the Documents directory and I want to load it every time the app is launched. But I have a default image if there is no saved image.
But I just cant get my head around how to use the [ func fileExistsAtPath(_:) ] function. Could someone give an example of using the function with a path argument passed into it.
I believe I don't need to paste any code in there as this is a generic question. Any help will be much appreciated.
Swift 4.x version
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
if let pathComponent = url.appendingPathComponent("nameOfFileHere") {
let filePath = pathComponent.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
} else {
print("FILE PATH NOT AVAILABLE")
}
Swift 3.x version
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: path)
let filePath = url.appendingPathComponent("nameOfFileHere").path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
Swift 2.x version, need to use URLByAppendingPathComponent
let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let filePath = url.URLByAppendingPathComponent("nameOfFileHere").path!
let fileManager = NSFileManager.defaultManager()
if fileManager.fileExistsAtPath(filePath) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
Check the below code:
Swift 1.2
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let getImagePath = paths.stringByAppendingPathComponent("SavedFile.jpg")
let checkValidation = NSFileManager.defaultManager()
if (checkValidation.fileExistsAtPath(getImagePath))
{
println("FILE AVAILABLE");
}
else
{
println("FILE NOT AVAILABLE");
}
Swift 2.0
let paths = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0])
let getImagePath = paths.URLByAppendingPathComponent("SavedFile.jpg")
let checkValidation = NSFileManager.defaultManager()
if (checkValidation.fileExistsAtPath("\(getImagePath)"))
{
print("FILE AVAILABLE");
}
else
{
print("FILE NOT AVAILABLE");
}
Nowadays (2016) Apple recommends more and more to use the URL related API of NSURL, NSFileManager etc.
To get the documents directory in iOS and Swift 2 use
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory,
inDomain: .UserDomainMask,
appropriateForURL: nil,
create: true)
The try! is safe in this case because this standard directory is guaranteed to exist.
Then append the appropriate path component for example an sqlite file
let databaseURL = documentDirectoryURL.URLByAppendingPathComponent("MyDataBase.sqlite")
Now check if the file exists with checkResourceIsReachableAndReturnError of NSURL.
let fileExists = databaseURL.checkResourceIsReachableAndReturnError(nil)
If you need the error pass the NSError pointer to the parameter.
var error : NSError?
let fileExists = databaseURL.checkResourceIsReachableAndReturnError(&error)
if !fileExists { print(error) }
Swift 3+:
let documentDirectoryURL = try! FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true)
let databaseURL = documentDirectoryURL.appendingPathComponent("MyDataBase.sqlite")
checkResourceIsReachable is marked as can throw
do {
let fileExists = try databaseURL.checkResourceIsReachable()
// handle the boolean result
} catch let error as NSError {
print(error)
}
To consider only the boolean return value and ignore the error use the nil-coalescing operator
let fileExists = (try? databaseURL.checkResourceIsReachable()) ?? false
Swift 4.2
extension URL {
func checkFileExist() -> Bool {
let path = self.path
if (FileManager.default.fileExists(atPath: path)) {
print("FILE AVAILABLE")
return true
}else {
print("FILE NOT AVAILABLE")
return false;
}
}
}
Using: -
if fileUrl.checkFileExist()
{
// Do Something
}
It's pretty user friendly. Just work with NSFileManager's defaultManager singleton and then use the fileExistsAtPath() method, which simply takes a string as an argument, and returns a Bool, allowing it to be placed directly in the if statement.
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentDirectory = paths[0] as! String
let myFilePath = documentDirectory.stringByAppendingPathComponent("nameOfMyFile")
let manager = NSFileManager.defaultManager()
if (manager.fileExistsAtPath(myFilePath)) {
// it's here!!
}
Note that the downcast to String isn't necessary in Swift 2.
works at Swift 5
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileUrl = documentDirectory.appendingPathComponent("userInfo").appendingPathExtension("sqlite3")
if FileManager.default.fileExists(atPath: fileUrl.path) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
} catch {
print(error)
}
where "userInfo" - file's name, and "sqlite3" - file's extension
An alternative/recommended Code Pattern in Swift 3 would be:
Use URL instead of FileManager
Use of exception handling
func verifyIfSqliteDBExists(){
let docsDir : URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let dbPath : URL = docsDir.appendingPathComponent("database.sqlite")
do{
let sqliteExists : Bool = try dbPath.checkResourceIsReachable()
print("An sqlite database exists at this path :: \(dbPath.path)")
}catch{
print("SQLite NOT Found at :: \(strDBPath)")
}
}
Swift 5
extension FileManager {
class func fileExists(filePath: String) -> Bool {
var isDirectory = ObjCBool(false)
return self.default.fileExists(atPath: filePath, isDirectory: &isDirectory)
}
}
Very simple:
If your path is a URL instance convert to string by 'path' method.
let fileManager = FileManager.default
var isDir: ObjCBool = false
if fileManager.fileExists(atPath: yourURLPath.path, isDirectory: &isDir) {
if isDir.boolValue {
//it's a Directory path
}else{
//it's a File path
}
}
For the benefit of Swift 3 beginners:
Swift 3 has done away with most of the NextStep syntax
So NSURL, NSFilemanager, NSSearchPathForDirectoriesInDomain are no longer used
Instead use URL and FileManager
NSSearchPathForDirectoriesInDomain is not needed
Instead use FileManager.default.urls
Here is a code sample to verify if a file named "database.sqlite" exists in application document directory:
func findIfSqliteDBExists(){
let docsDir : URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let dbPath : URL = docsDir.appendingPathComponent("database.sqlite")
let strDBPath : String = dbPath.path
let fileManager : FileManager = FileManager.default
if fileManager.fileExists(atPath:strDBPath){
print("An sqlite database exists at this path :: \(strDBPath)")
}else{
print("SQLite NOT Found at :: \(strDBPath)")
}
}
This works fine for me in swift4:
func existingFile(fileName: String) -> Bool {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
if let pathComponent = url.appendingPathComponent("\(fileName)") {
let filePath = pathComponent.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath)
{
return true
} else {
return false
}
} else {
return false
}
}
You can check with this call:
if existingFile(fileName: "yourfilename") == true {
// your code if file exists
} else {
// your code if file does not exist
}
I hope it is useful for someone. #;-]
You must add a "/" slash before filename, or you get path like ".../DocumentsFilename.jpg"
Swift 4 example:
var filePath: String {
//manager lets you examine contents of a files and folders in your app.
let manager = FileManager.default
//returns an array of urls from our documentDirectory and we take the first
let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first
//print("this is the url path in the document directory \(String(describing: url))")
//creates a new path component and creates a new file called "Data" where we store our data array
return(url!.appendingPathComponent("Data").path)
}
I put the check in my loadData function which I called in viewDidLoad.
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
Then I defined loadData below.
func loadData() {
let manager = FileManager.default
if manager.fileExists(atPath: filePath) {
print("The file exists!")
//Do what you need with the file.
ourData = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as! Array<DataObject>
} else {
print("The file DOES NOT exist! Mournful trumpets sound...")
}
}

Resources