Swift 4 Sqlite database no such table error - swift

I'm working at the very limit of my newbie understanding here and I'm struggling to understand what I'm doing wrong. So, I've created a database in DB Browser for SQLite, I've saved as a .db file and I've also exported to a .sql file. It is a single table with a numeric key and the name of a city. It's the basis of a bigger database I have planned. I've added all the FMDB stuff as per the GitHub page instructions. I've added the database to my project (dragged it from Finder into the left hand pane of Xcode). I've tried both individually and together the .db and .sql files and ensured they were added to target. I've then added the following code to viewDidLoad()' in theViewController` where I want to create an array from the database contents.
// test db code
let fileURL = try! FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("Wanderer.db")
let database = FMDatabase(url: fileURL)
guard database.open() else {
print("Unable to open database")
return
}
do {
let cities:FMResultSet = try database.executeQuery("SELECT City from Cities", values: nil)
while cities.next() {
if let result = cities.string(forColumn: "City") {
cityData.append(result)
}
}
} catch {
print("OOPS, some sort of failure")
}
//end test db code
When I run my app on the Simulator and the relevant page loads, I get the following error in my debug console:
2018-10-29 20:00:43.194628+0000 Wanderer[9708:849273] DB Error: 1 "no
such table: Cities"
2018-10-29 20:00:43.194808+0000 Wanderer[9708:849273] DB Query: SELECT
City from Cities
2018-10-29 20:00:43.194917+0000 Wanderer[9708:849273] DB Path:
/Users/robmunro2/Library/Developer/CoreSimulator/Devices/98971BF4-8901-4E3D-8D71-1CF3E4117885/data/Containers/Data/Application/7421C242-AA18-409E-BC61-ABB5FA5D2A98/Library/Application
Support/Wanderer.db OOPS, some sort of failure
What on earth am I doing wrong!?

Related

Cannot create sqlite file with SQLite.swift "cannot open file at line..."

I'm attempting to create a sqlite DB file within my iOS project. I'm using the code as documented for R/W
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true
).first!
let db = try Connection("\(path)/db.sqlite3")
but I end up with a cannot open file at line 45340 of [d24547a13b].
The closest resource I've found is Why do I get Unable to Open Database file? but the code there seems to be the same as what I have.
edit: More logs
[logging-persist] os_unix.c:45340: (0) open
- Undefined error: 0
DB file needs to be created under the documents directory. See Brando Flores' answer: https://stackoverflow.com/a/70514807/346676
do {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let dbFile = "\(path.first ?? "")/db.sqlite3"
print(dbFile) // show full file path
db = try Connection(dbFile)
}
catch {
print(error)
}

Swift 5: How to save results of NSFetchRequest to File

I am new to programming in general and have started with Swift. I have a feeling what I'm attempting to do is a bit outside of my scope, but I've come so far so here's the ask:
I am adding a tracker to a program for macOS X I've already created. The end user inputs a number and hits "Add to tracker" which then takes that number, the timestamp from the button click and writes that to the appropriate entity in Core Data. Everything works perfectly, my NSTable displays the data and I my batch delete works, but I cannot for the life of me work out the best way to take the results from the NSFetchRequest and print them to a text file.
Here is the code for my fetch request that occurs when the "print" button is hit:
#IBAction func printTracker(_ sender: Any) {
fetchRequest.propertiesToFetch = ["caseDate","caseNumber"]
fetchRequest.returnsDistinctResults = true
fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType
do {
let results = try context.fetch(fetchRequest)
let resultsDict = results as! [[String:String]]
} catch let err as NSError {
print(err.debugDescription)
}
}
After the "resultsDict" declaration is where I just can't seem to come to a workable solution for getting it to string, then to txt file.
If I add a print command to the console as is, I can see that resultsDict pulls correctly with the following format:
[["caseNumber": "12345", "caseDate": "3/22/21, 5:48:18 PM"]]
Ideally I need it in plaintext more like
"3/22/21, 5:48:18 PM : 12345"
Any advice or help on the conversion would be greatly appreciated.
A simple way if there is not a huge amount of data returned is to create a string from the fetched data and then write that string to disk
First create the string by getting the values from the dictionary and adding them in the right order into a string and joining the strings with a new line character
let output = results.reduce(into: []) { $0.append("\($1["caseDate", default: ""]) : \($1["caseNumber", default: ""])") }
.joined(separator: "\n")
Then we can write them to file, here I use the Document directory as the folder to save the file in
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let path = paths[0].appendingPathComponent("results.txt")
do {
try String(output).write(to: path, atomically: true, encoding: .utf8)
} catch {
print("Failed to write to file, error: \(error)")
}

Copy TPK file from AppGroup Container to Documents

I have a file that exists within the AppGroup Shared Container and I was wondering if it was possible to copy the file from the Shared Container into the application bundle.
I am getting the file path as follows :
let filePath = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.com.sharedBasemap")!.URLByAppendingPathComponent("localLayer.tpk")!.path
The reason I am trying to do this is it seems that the ArcGIS SDK will not recognize the TPK file from within the App Group so I am wondering if it will recognize it if I copy it into the app bundle.
EDIT: Based on Leo's comment it appears that you can not copy to the bundle, so I am trying to copy to the App Support folder.Here is my code now, I see the "file exists" message but then it is displaying the Oops message indicating it can not move the file :
let filePath = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.com.sharedBasemap")!.URLByAppendingPathComponent("localLayer.tpk")!.path!
let appSupportFolder = String(NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)[0]) + "localLayer.tpk"
let fileManager = NSFileManager.defaultManager()
if NSFileManager.defaultManager().fileExistsAtPath(filePath){
print("File exists at \(filePath)")
do {
try fileManager.copyItemAtPath(filePath, toPath: appSupportFolder)
}
catch let error as NSError {
print("Ooops! Something went wrong: \(error)")
}
} else {
print("File does not exist")
}
EDIT 2: I have modified the code again to just move the TPK file into the documents directory.I believe that piece is working but I receive an error message when trying to load the TPK file into ArcGIS.At this point in time, I am thinking that the issue is related to the ArcGIS SDK and that it does not support loading a TPK file from anywhere except the application bundle.
let destPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first!
let fullDestPath = NSURL(fileURLWithPath: destPath).URLByAppendingPathComponent("localLayer.tpk")
let fullDestPathString = fullDestPath!.path!
im pretty sure the appSupportFolder doesn't exist by default -- nobody creates it unless needed -- try to verify that first and create it if needed
pseudocode if(!fileExists(supportFolder)) { createDirectory(supportFolder) }

Using Xcode 7/Swift 2 writeToPath to Resources file the call does not fail but no data is written

I am using Xcode 7.3.1 and Swift 2.0. I am using the following code sample:
func writeToResourcesDataDir() {
if let path = NSBundle.mainBundle().pathForResource("TestData", ofType: ".json") {
let str = "Test String"
do {
try str.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding)
print("writeToFile successful")
} catch {
print("writeToFile failed")
}
} else {
print("Path does not exist")
}
}
Running under Xcode in the see the "writeToFile successful" message.But, also using the simulator, I can display the TestData in the Resources directory and the file does not have the string.I also used a terminal window in Mac to look at the files in the Resources directory and the TestData file is empty (0 bytes).I know I am in the correct Resources directory because there is another file in the directory that has correct data that is used for running the other parts of the program.
I have spent several days now looking at other google entries about data from writeToFile not working and I have tried out every fix or things to try I have found.
Can anyone help?
I added code to accept the boolean return from the call to writeToFile and it returns a false. I'm not sure why a false is returned but the catch isn't invoked.I am not sure how to get the error code that goes with this writeToFile in Swift 2.0.
I am also wondering if this is a write permissions problem.Should I be using the Documents directory instead of the Data directory?
Try something like this. This is swift 2.3 and xcode 8.
let filename = "yourjsonfile"
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
let filePath = documentDirectoryURL.URLByAppendingPathComponent(filename)
let fileExist = filePath?.checkResourceIsReachableAndReturnError(nil)
if (fileExist == true) {
print("Found file")
} else {
print("File not found")
}

Swift write/save/move a document file to iCloud drive

I've been trying for over two days to write a file to iCloud drive. I have tried writing a simple text file directly, locally then moving it, using UIDocumentMenuViewController, etc. I'm not getting any errors with my code and stepping through debugger, it looks successful, but when I check to see if the file exists or at least the iCloud directory, there is nothing there. I tried on both the simulator and my iPhone, triggering iCloud synching, and everything else I can think of.
My main goal is to simply write a text file to the iCloud drive, which later will be "numbers" file
I have set up my plist file and my entitlements:
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.paul.c.$(PRODUCT_NAME:rfc1034identifier)</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>myCloudTest</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
I have also bumped up by bundle version as stated at: Save iOS 8 Documents to iCloud Drive
I have tried dozens of tutorials with no luck. My latest code is based off of this sample: https://medium.com/ios-os-x-development/icloud-drive-documents-1a46b5706fe1
Here is my code:
#IBAction func ExportFile(sender: AnyObject) {
var error:NSError?
let iCloudDocumentsURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)?.URLByAppendingPathComponent("myCloudTest")
//is iCloud working?
if iCloudDocumentsURL != nil {
//Create the Directory if it doesn't exist
if (!NSFileManager.defaultManager().fileExistsAtPath(iCloudDocumentsURL!.path!, isDirectory: nil)) {
//This gets skipped after initial run saying directory exists, but still don't see it on iCloud
NSFileManager.defaultManager().createDirectoryAtURL(iCloudDocumentsURL!, withIntermediateDirectories: true, attributes: nil, error: nil)
}
} else {
println("iCloud is NOT working!")
// return
}
if ((error) != nil) {
println("Error creating iCloud DIR")
}
//Set up directorys
let localDocumentsURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: .UserDomainMask).last as! NSURL
//Add txt file to my local folder
let myTextString = NSString(string: "HELLO WORLD")
let myLocalFile = localDocumentsURL.URLByAppendingPathComponent("myTextFile.txt")
let written = myTextString.writeToURL(myLocalFile, atomically: true, encoding: NSUTF8StringEncoding, error: &error)
if ((error) != nil){
println("Error saving to local DIR")
}
//If file exists on iCloud remove it
var isDir:ObjCBool = false
if (NSFileManager.defaultManager().fileExistsAtPath(iCloudDocumentsURL!.path!, isDirectory: &isDir)) {
NSFileManager.defaultManager().removeItemAtURL(iCloudDocumentsURL!, error: &error)
}
//copy from my local to iCloud
if (error == nil && !NSFileManager.defaultManager().copyItemAtURL(localDocumentsURL, toURL: iCloudDocumentsURL!, error: &error)) {
println(error?.localizedDescription);
}
Thank You for taking time for this.
Cheers,
Paul
I ran some code on my iphone after the code above:
var error:NSError?
let iCloudDocumentsURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil) //?.URLByAppendingPathComponent("myCloudTest")
var fileManager: NSFileManager = NSFileManager()
var fileList: NSArray = fileManager.contentsOfDirectoryAtURL(iCloudDocumentsURL!, includingPropertiesForKeys: nil, options: nil, error: &error)!
var filesStr: NSMutableString = NSMutableString(string: "Files in iCloud folder \n")
for s in fileList {
println(s)
}
and it prints out the path to my text file:
file:///private/var/mobile/Library/Mobile%20Documents/iCloud~com~paul~c~myApp/MyTextFile.txt
My file is there, I just can't see it on iCloud drive.
I had this problem. I followed the advice here and I found that my Info.plist key was not correct. Once I changed it to iCloud.MY_BUNDLE_IDENTIFIER (i.e. copy the string from the CFBundleIdentifier key higher in Info.plist) it all started working.
Removing the .com from your key may fix your issue.
FWIW:
I also found out that the name of the project within the bundle ID is important.
My project bundle ID was something like the following: aaa-bbb-ccc-ddd
I could not get the iCloud working.
Then I renamed it to: aaa-bbb.ccc-ddd
It started working.
I believe I've found a way to get everything back in sync without constantly having to "bump" my bundle number. I've tried this multiple times while making changes within the "capabilities" area of key-value storage/iCloud Documents/CloudKit and it seems to work each time.
Sign out of iCloud on your Mac
Sign out of iCloud on your Simulator
Sign back into iCloud on your Mac
Sign back into iCloud on your Simulator
Do a clean build from XCode (Shift-Cmd-K)
This appears to reset the synchronization of the folder structures when you're App is writing to your iCloud Documents directory - without having to touch your bundle number. It takes a little longer to do it, but I'm a little OCD and kinda prefer my initial App launch to start with a 1!
You need to do 2 things
Do what #rick Andrews said: "Once I changed it to iCloud.MY_BUNDLE_IDENTIFIER (i.e. copy the string from the CFBundleIdentifier key higher in Info.plist)"
Store your filed inside the containers subfolder Documents
struct iCloudStore {
public var containerUrl: URL! {
return fileManager.url(forUbiquityContainerIdentifier: nil)!
}
public var documents: URL! {
return containerUrl.appendingPathComponent("Documents", isDirectory: true)
}
private let fileManager: FileManager = FileManager.default
func store(url: URL) {
// move ulr into the documents folder as a file
let fileID = "\(UUID().uuidString).<#extension#>"
let icloudFile = documents.appendingPathComponent(fileID, isDirectory: false)
try fileManager.copyItem(at: url, to: icloudFile)
}
}
Maybe there's a rule for container's name.
I tried the following names.
○:iSheet
×:sheetFiles
×:com.myname.sheetFiles