I am a beginner to Swift and FMDB, I got the code below from resources in the internet, and tried my best to understand the code. I have put comments below statements stating what I think it is doing. The ones with question marks I do not understand.
class ViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var specialty: UITextField!
//Defines name and specialty as contents of text fields
var dbpath = String()
//defines the database path
func getPath(fileName: String) -> String {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
//finds document and returns an array of paths
let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
print(fileName)
//finds path to fileName with URLByAppendingPathComponent
print("File Path Is : \(fileURL)")
return fileURL.path!
//returns the fileURL in path format?????
}
//Button "Add Shop" definition
override func viewDidLoad() {
super.viewDidLoad()
let dirPaths =
NSSearchPathForDirectoriesInDomains(.DocumentDirectory,
.UserDomainMask, true)
//creates search paths for directories, then ?????
let docsDir = dirPaths[0]
let dbPath: String = getPath("shopdata.db")
//assigns string "shopdata.db" to dbPath
let fileManager = NSFileManager.defaultManager()
//easier access for NSFileManager, returns shared file for the process when called
if !fileManager.fileExistsAtPath(dbPath as String) {
//if there is already a database, do the following
let contactDB = FMDatabase(path: dbPath as String)
//contact database with path identified in function getPath
if contactDB == nil {
print("Error: \(contactDB.lastErrorMessage())")
//If there is no database
}
if contactDB.open() {
let sql_stmt = "CREATE TABLE IF NOT EXISTS CONTACTS (ID INTEGER PRIMARY KEY AUTOINCREMENT, SPECIALTY TEXT, NAME TEXT)"
if !contactDB.executeStatements(sql_stmt)
//executes a create table statement as defined above
{
print("Error: \(contactDB.lastErrorMessage())")
//if cannot execute statement, display error from fmdb
}
contactDB.close()
//close connection
} else {
print("Error: \(contactDB.lastErrorMessage())")
//if contact cannot be made, display error from fmdb
}
}
}
#IBAction func addShop(sender: AnyObject) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This Function will get the file path of the give fileName from DocumentDirectory and return it back.
func getPath(fileName: String) -> String {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
//finds document and returns an array of paths
let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
print(fileName)
//finds path to fileName with URLByAppendingPathComponent
print("File Path Is : \(fileURL)")
return fileURL.path!
//returns the fileURL in path format?????
}
And these line of code is not needed in here at all. This code also get the file path from DocumentDirectory of the application. Which is done in the getPath: function.
let dirPaths =
NSSearchPathForDirectoriesInDomains(.DocumentDirectory,
.UserDomainMask, true)
//creates search paths for directories, then ?????
let docsDir = dirPaths[0]
DocumentDirectory is where the application save the database.
Sorry for bad English. Hope it helps :)
Related
The below code create the file and puts the data in it but it saves as the following:
["123", "456", "", "789"] How do I get a new item for each paragraph? the idea is the song lyrics would save to the file based on where the verses start and end. I am new to swift so there may be a better way to do this
#IBOutlet weak var songName: UITextField!
#IBOutlet weak var newLyrics: UITextView!
#IBAction func saveSong(_ sender: Any) {
let sName = songName.text!
let fileManager = FileManager.default
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let path = documentDirectory.appending("/Lyrics/\(sName).plist")
let lyrics = newLyrics.text.components(separatedBy: "\n\n")
print(lyrics)
if let tDocumentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
let filePath = tDocumentDirectory.appendingPathComponent("Lyrics")
if !fileManager.fileExists(atPath: filePath.path) {
do {
try fileManager.createDirectory(atPath: filePath.path, withIntermediateDirectories: true, attributes: nil)
} catch {
NSLog("Couldn't create document directory")
}
}
NSLog("Document directory is \(filePath)")
}
if(!fileManager.fileExists(atPath: path)){
print(path)
var lyricArray = [String]()
lyricArray.append("\(lyrics)")
// any other key values
let lyricData = NSArray(array: lyricArray)
let answer = lyricData.write(toFile: path, atomically: true)
print("File Created? \(answer)")
} else {
print("File Exists")
}
I expect the output to be
Item0 "123"
Item1 "456"
actual output is
Item0 ["123", "456", "", "789"]
You are appending the description of an array to lyricArray with string interpolation (\(...)).
The description of an array is one literal string with format "[\"item1\", \"item2\"]".
You have to append the contents of the array
Replace
lyricArray.append("\(lyrics)")
with
lyricArray.append(contentsOf: lyrics)
or drop lyricArray completely and simply write
let lyricData = NSArray(array: lyrics)
or – highly recommended –
do {
let lyricData = try PropertyListSerialization.data(fromPropertyList: lyrics, format: .xml, options: 0)
} catch { print(error) }
Notes:
Don't use NSArray to handle property lists in Swift. Use PropertyListSerialization.
Never print meaningless literal strings in a catch clause like Couldn't do that. Print the error.
And why do get the path to the documents folder twice with different API (okay, first the path and then the URL)?
I am fairly new to Swift 3 and ios, having previously written the program for Android which the database is accessed.
The current problem is that the database is prepopulated with 10 tables, however, when I try to access the dbase there are no tables, either in the simulator or device, so cannot access the data. I have searched the forum and internet but can only find information to check if the database exists. The sqlite wrapper is FMDB that is being used.
code:
override func viewDidLoad() {
super.viewDidLoad()
let filemgr = FileManager.default
let dirPaths = filemgr.urls(for: .documentDirectory, in: .userDomainMask)
let destPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
databasePath = dirPaths[0].appendingPathComponent("level24.db").path
print(databasePath)
let fullDestPath = URL(fileURLWithPath: destPath).appendingPathComponent("level24.db")
let bundleDatabasePath = Bundle.main.path(forResource: "level24", ofType: ".db")
var level2DB = FMDatabase(path: databasePath as String)
if filemgr.fileExists(atPath: fullDestPath.path){
print("Database file is exist")
print(filemgr.fileExists(atPath: bundleDatabasePath!))
}else{
filemgr.replaceItemAt(databasePath, withItemAt: bundleDatabasePath.path)
}
level2DB = FMDatabase(path: databasePath as String)
if (level2DB?.open())!{
print("Database is open")
var querySQL = "SELECT * FROM sqlite_master WHERE name = '\(tblName)' and type='table'"
let results:FMResultSet? = level2DB?.executeQuery(querySQL, withArgumentsIn:nil)
if (results == nil){
print("Results = \(results)")
do{
try filemgr.copyItem(atPath: bundleDatabasePath!, toPath: fullDestPath.path)
}catch{
print("\n",error)
}
}
print("Results after = \(results)")
let querySQL2 = "SELECT QUESTION FROM tblSani WHERE _ID = 5"
let results2:FMResultSet? = level2DB?.executeQuery(querySQL2, withArgumentsIn:nil)
print(results2?.string(forColumn: "QUESTION") as Any)
}
}
output is:
/Users/***/Library/Developer/CoreSimulator/Devices/4F511422-2F86-49BF-AB10-5CA74B9A7B40/data/Containers/Data/Application/58DD87EB-C20F-4058-B9BC-CCD7ECEEFA98/Documents/level24.db
Database is open
Results after = Optional(<FMResultSet: 0x608000245220>)
2017-04-05 21:09:26.833 Level 2[3161:210849] DB Error: 1 "no such table: tblSani"
2017-04-05 21:09:26.833 Level 2[3161:210849] DB Query: SELECT QUESTION FROM tblSani WHERE _ID = 5
2017-04-05 21:09:26.834 Level 2[3161:210849]DBPath: /Users/***/Library/Developer/CoreSimulator/Devices/4F511422-2F86-49BF-AB10-5CA74B9A7B40/data/Containers/Data/Application/58DD87EB-C20F-4058-B9BC-CCD7ECEEFA98/Documents/level24.db
nil
It would be appreciated if you could help me to find a solution to the problem.
after working at this for many hours I have come up with this solution:
This is put into the AppDelegate.swift and will check at start of app
code:
var filemgr = FileManager.default
static let dirPaths = FileManager().urls(for: .documentDirectory, in: .userDomainMask)
static let destPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
var databasePath = dirPaths[0].appendingPathComponent("level24.db").path
static let fullDestPath = URL(fileURLWithPath: destPath).appendingPathComponent("level24.db")
static let bundleDatabasePath = Bundle.main.path(forResource: "level24", ofType: ".db")
//function to check if dbase exists
func checkdbase(){
print(databasePath)
print("Full path = \(AppDelegate.fullDestPath)")
var level2DB = FMDatabase(path: databasePath as String)
if filemgr.fileExists(atPath: AppDelegate.fullDestPath.path){
print("Database file is exist")
print(filemgr.fileExists(atPath: AppDelegate.bundleDatabasePath!))
print("bundle = \(AppDelegate.bundleDatabasePath)")
let level2DB = FMDatabase(path: databasePath as String)
if (level2DB?.open())!{
print("Database is open")
// use a select statement for a known table and row
let querySQL2 = "SELECT * FROM tblSani WHERE _ID = 5"
let results:FMResultSet? = level2DB?.executeQuery(querySQL2, withArgumentsIn:nil)
if results?.next()==true{
print("Database has tables")
}else{
print("Database no tables")
removeDB()
}
}
}else{
removeDB()
}
}
//function to remove existing dbase then unpack the dbase from the bundle
func removeDB(){
do{
try filemgr.removeItem(atPath: AppDelegate.fullDestPath.path)
print("Database removed")
}catch {
NSLog("ERROR deleting file: \(AppDelegate.fullDestPath)")
}
do{
try filemgr.copyItem(atPath: AppDelegate.bundleDatabasePath!, toPath: AppDelegate.fullDestPath.path)
print("Databse re-copied")
}catch{
print("\n",error)
}
}
call the function from the AppDelegate 'didFinishLaunchingWithOptions'
checkdbase()
If you are able to improve this answer please do to help others who may have had the same problem
I am having some trouble compiling this on xcode.
Line 6: " let docsDir = dirPaths[0] as! String" returns an error of "Forced Cast of 'String' to the same type has no effect."
What is as! String doing? as it tells me to delete it.
Second question is line 8 where stringByAppendingPathComponent seems to have been removed by swift but after reading some questions on stack, it shows that NSString works with it. How would I implement the NSString change to the code?
The last question I would like to ask is I don't get minority of this code, is there anywhere I could learn such things such as what is "defaultManager" doing after the class NSFileManager or just line 2 and 3 in general.
let filemgr = NSFileManager.defaultManager()
let dirPaths =
NSSearchPathForDirectoriesInDomains(.DocumentDirectory,
.UserDomainMask, true)
let docsDir = dirPaths[0] as! String
let databasePath = docsDir.stringByAppendingPathComponent(
"shopdata.db")
if !filemgr.fileExistsAtPath(databasePath as String) {
let contactDB = FMDatabase(path: databasePath as String)
if contactDB == nil {
print("Error: \(contactDB.lastErrorMessage())")
}
if contactDB.open() {
let sql_stmt = "CREATE TABLE IF NOT EXISTS CONTACTS (ID INTEGER PRIMARY KEY AUTOINCREMENT, SPECIALTY TEXT, NAME TEXT)"
if !contactDB.executeStatements(sql_stmt) {
print("Error: \(contactDB.lastErrorMessage())")
}
contactDB.close()
} else {
print("Error: \(contactDB.lastErrorMessage())")
}
}
Try to use this code the path of file
func getPath(fileName: String) -> String {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let fileURL = documentsURL.URLByAppendingPathComponent(fileName)
print("File Path Is : \(fileURL)")
return fileURL.path!
}
And then call this function like this
let dbPath: String = getPath("shopdata.db")
let fileManager = NSFileManager.defaultManager()
if !fileManager.fileExistsAtPath(dbPath) {
// Your remaining Code here
}
Hope it help :) (Sorry for bad English)
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...")
}
}
I'm trying to get path to Documents folder with code:
var documentsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory:0,NSSearchPathDomainMask:0,true)
but Xcode gives error: Cannot convert expression's type 'AnyObject[]!' to type 'NSSearchPathDirectory'
I'm trying to understand what is wrong in the code.
Apparently, the compiler thinks NSSearchPathDirectory:0 is an array, and of course it expects the type NSSearchPathDirectory instead. Certainly not a helpful error message.
But as to the reasons:
First, you are confusing the argument names and types. Take a look at the function definition:
func NSSearchPathForDirectoriesInDomains(
directory: NSSearchPathDirectory,
domainMask: NSSearchPathDomainMask,
expandTilde: Bool) -> AnyObject[]!
directory and domainMask are the names, you are using the types, but you should leave them out for functions anyway. They are used primarily in methods.
Also, Swift is strongly typed, so you shouldn't just use 0. Use the enum's value instead.
And finally, it returns an array, not just a single path.
So that leaves us with (updated for Swift 2.0):
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
and for Swift 3:
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
Swift 3.0 and 4.0
Directly getting first element from an array will potentially cause exception if the path is not found. So calling first and then unwrap is the better solution
if let documentsPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
//This gives you the string formed path
}
if let documentsPathURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
//This gives you the URL of the path
}
The modern recommendation is to use NSURLs for files and directories instead of NSString based paths:
So to get the Document directory for the app as an NSURL:
func databaseURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
if let documentDirectory: NSURL = urls.first as? NSURL {
// This is where the database should be in the documents directory
let finalDatabaseURL = documentDirectory.URLByAppendingPathComponent("items.db")
if finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) {
// The file already exists, so just return the URL
return finalDatabaseURL
} else {
// Copy the initial file from the application bundle to the documents directory
if let bundleURL = NSBundle.mainBundle().URLForResource("items", withExtension: "db") {
let success = fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL, error: nil)
if success {
return finalDatabaseURL
} else {
println("Couldn't copy file to final location!")
}
} else {
println("Couldn't find initial database in the bundle!")
}
}
} else {
println("Couldn't get documents directory!")
}
return nil
}
This has rudimentary error handling, as that sort of depends on what your application will do in such cases. But this uses file URLs and a more modern api to return the database URL, copying the initial version out of the bundle if it does not already exist, or a nil in case of error.
Xcode 8.2.1 • Swift 3.0.2
let documentDirectoryURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
Xcode 7.1.1 • Swift 2.1
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
Usually I prefer to use this extension:
Swift 3.x and Swift 4.0:
extension FileManager {
class func documentsDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as [String]
return paths[0]
}
class func cachesDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true) as [String]
return paths[0]
}
}
Swift 2.x:
extension NSFileManager {
class func documentsDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as [String]
return paths[0]
}
class func cachesDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true) as [String]
return paths[0]
}
}
More convenient Swift 3 method:
let documentsUrl = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first!
For everyone who looks example that works with Swift 2.2, Abizern code with modern do try catch handle of error
func databaseURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
if let documentDirectory:NSURL = urls.first { // No use of as? NSURL because let urls returns array of NSURL
// This is where the database should be in the documents directory
let finalDatabaseURL = documentDirectory.URLByAppendingPathComponent("OurFile.plist")
if finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) {
// The file already exists, so just return the URL
return finalDatabaseURL
} else {
// Copy the initial file from the application bundle to the documents directory
if let bundleURL = NSBundle.mainBundle().URLForResource("OurFile", withExtension: "plist") {
do {
try fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL)
} catch let error as NSError {// Handle the error
print("Couldn't copy file to final location! Error:\(error.localisedDescription)")
}
} else {
print("Couldn't find initial database in the bundle!")
}
}
} else {
print("Couldn't get documents directory!")
}
return nil
}
Update
I've missed that new swift 2.0 have guard(Ruby unless analog), so with guard it is much shorter and more readable
func databaseURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
// If array of path is empty the document folder not found
guard urls.count != 0 else {
return nil
}
let finalDatabaseURL = urls.first!.URLByAppendingPathComponent("OurFile.plist")
// Check if file reachable, and if reacheble just return path
guard finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) else {
// Check if file is exists in bundle folder
if let bundleURL = NSBundle.mainBundle().URLForResource("OurFile", withExtension: "plist") {
// if exist we will copy it
do {
try fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL)
} catch let error as NSError { // Handle the error
print("File copy failed! Error:\(error.localizedDescription)")
}
} else {
print("Our file not exist in bundle folder")
return nil
}
return finalDatabaseURL
}
return finalDatabaseURL
}
Xcode 8b4 Swift 3.0
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
Usually i prefer like below in swift 3, because i can add file name and create a file easily
let fileManager = FileManager.default
if let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
let databasePath = documentsURL.appendingPathComponent("db.sqlite3").path
print("directory path:", documentsURL.path)
print("database path:", databasePath)
if !fileManager.fileExists(atPath: databasePath) {
fileManager.createFile(atPath: databasePath, contents: nil, attributes: nil)
}
}
Copy and paste this line in App delegate like this and it will print path like this
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
print(NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last! as String)
return true
}
Copy the path and paste it in go To Folder in finder by right clicking on it then enter
Open the file in Xcode