FMDB & Swift, " Optional("no such table: Student info") " in real device but it can be done in simulator - swift3

I'm newbie, plz help me to solve this out, I still have lots of other things to work on, really thank you thank you very much!
This is a further question after How to use FMDB on the generic iOS device instead of simulator?
When I execute the app on my device and the error threw out: "no such table: Student info", I've print all the path and they all pointed to the same file so I assumed the database has already copied? Console shows like this:
file:///var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/
file:///var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
/var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
file:///var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/
file:///var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
/var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
/var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
<NSFileManager: 0x17401c1b0>
2017-03-13 16:43:25.446039 Test1.3[16360:5045427] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2017-03-13 16:43:25.457278 Test1.3[16360:5045427] [MC] Reading from public effective user settings.
Insert failed:
Optional("no such table: Student info")
The Data.db is in my bundle resources in target; and the contents in my device is a blank Data.db;
The 2nd question: If you look at the Utility.Swift in the previous question, although the app works good on simulator but after it was loaded, there should be an alertView said "Your database copy successfully", but it didn't. Following is that part of the code:
class func copyFile(_ fileName: NSString){
let dbPath: String = getPath(fileName as String)
let fileManager = FileManager.default
print(dbPath)
print(fileManager)
if !fileManager.fileExists(atPath: dbPath) {
let documentsURL = Bundle.main.resourceURL
let fromPath = documentsURL!.appendingPathComponent(fileName as String)
var error : NSError?
do {
try fileManager.copyItem(atPath: fromPath.path, toPath: dbPath)
}
catch let error1 as NSError {
error = error1
}
if(error != nil){
self.invokeAlertMethod("Error Occured", strBody: "\(error?.localizedDescription)" as NSString, delegate: nil)
}
else{
self.invokeAlertMethod("Successed", strBody: "Your database copy successfully", delegate: nil)
}
}
}

Okay for answering this question I went through your demo.
Where I found couple of mistakes. Let me go through one by one.
1) Your class Utility have a getPath method. What it does it will
keep copying db every time although db is already present in documents
directory and your documents directory db will be replaced with the sample structure. You should always check that if db is already present in documents directory or not.
2) Your db was getting copied into documents directory but structure
wasn't. There was no Student info table in db of documents directory.
3) Please avoid using space or any special characters in table names.
So what I did just corrected your method getPath in utility class.
Please replace your method with this one
class func getPath(_ fileName: String) -> String {
let bundlePath = Bundle.main.path(forResource: "Data", ofType: ".db")
let destPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let fileManager = FileManager.default
let fullDestPath = URL(fileURLWithPath: destPath).appendingPathComponent("Data.db")
if fileManager.fileExists(atPath: fullDestPath.path){
print("Database file is exist")
print(fileManager.fileExists(atPath: bundlePath!))
}else{
do{
try fileManager.copyItem(atPath: bundlePath!, toPath: fullDestPath.path)
}catch{
print("\n",error)
}
}
print(fullDestPath.path)
return fullDestPath.path
}
After changing this piece of code I tried to run in my device and inserted couple of records.
Let me know if you have any more questions.
If you find this answer helpful just accept it.

First trying delete your app and then reinstall it.
OR
I have created a project over FMDB in Swift which you can use to solve your issue. FMDB Wrapper class you can use in Objective C project as well.
https://github.com/LalitKY/Swift-FMDB
Hope this helps.

Related

Loading a file into a C++ object from an iOS App / issues with <iostream> on iOS

I'm working on integrating a C++ library (the GRT, a machine learning toolkit, to be specific) inside of an iOS app.
I've built the GRT as a framework, including using some Objective-C++ wrapper functions to call between my app and the framework.
At the moment, I'm trying to troubleshoot something involving file loading. Specifically, I'm trying to load a file from my app bundle into a GRT module.
Here's where I get the file I want access to, and initialize the GRT wrapper:
func loadTrainingData(){
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileUrl = documentsUrl.appendingPathComponent("train.grt")
let pipeline = GestureRecognitionPipeline()
let test:Bool = pipeline.load(fileUrl.path)
print(test)
}
And here's the Obj-C++ wrapper code that's called when pipeline.load is called:
- (BOOL)load:(NSString *) path
{
BOOL result = self.instance->load(std::string(path.UTF8String));
if (result) {
std::cout << "GRT config";
std::cout << self.instance->getModelAsString();
std::cout << "GRT info: " << self.instance->getInfo();
}
return result;
}
Finally, here's the actual C++ code that's part of the GRT library, where file loading is handled:
bool GestureRecognitionPipeline::load(const std::string &filename){
std::fstream file;
//Clear any previous setup
clear();
file.open(filename.c_str(), std::iostream::in );
if( !file.is_open() ){
errorLog << __GRT_LOG__ << " Failed to open file with filename: " << filename << std::endl;
return false;
}
...
}
Currently, I'm always failing to have my pipeline object successfully import a file. I don't think it's necessarily something to do with the way I'm accessing the file on the iOS side (though, I could be wrong). Does anyone have any insight? Any appreciated - thanks!
EDIT: I was able to verify that I am loading my file is being loaded properly by this check:
let path = Bundle.main.path(forResource: "acc-orientation", ofType: "grt")
print(path as Any!)
But, I'm still getting the same issues as before.
EDIT 2 I verified that the path is being loaded correctly in the the Obj-C++ wrapper too; which leads me to think it may be something related to the way that is handled in iOS....totally lost here now...
EDIT 3 As suggested by a colleague, I tried using the absoluteString of the file url to pass to my wrapper and the underlying C++ code, since the C++ doesn't have access to the sandboxed environment of iOS. Still the same result:
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileUrl = documentsUrl.appendingPathComponent("acc-orientation.grt")
let pipeline = GestureRecognitionPipeline()
let test:Bool = pipeline.load(fileUrl.absoluteString)
EDIT 4 As suggested in the comments, I tried using fileSystemRepresentation, but this also didn't bring success.
- (BOOL)load:(NSURL *) url {
BOOL result = self.instance->load(std::string([url fileSystemRepresentation]));
...
}
EDIT 5: I made a simple test project that attempts to only access the file and load it using Swift->Obj-C++->C++ (no framework files, in other words). Here's a link where it can be downloaded. Any help appreciated!
Well, you are almost there. I have downloaded your sample project and got it working. Your problem has to do with the actual location of the file you want to open. At the moment you are trying to open the file from the Documents folder but you never actually copy the file from the App Bundle to the Documents folder. So there are two solutions:
Solution 1: App Bundle
Alter the loadTrainingData method in ViewController.swift to access the file from the App Bundle:
func loadTrainingData(){
let path = Bundle.main.url(forResource: "acc-orientation", withExtension: "grt")
let wrapper = Wrapper()
let test:Bool = wrapper.load(path)
print(test)
}
Solution 2: Documents folder
Copy the file from your App Bundle to your Documents folder right after the first launch. Therefore, copy the following code snippet to your AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
do {
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("acc-orientation.grt")
let bundleURL = Bundle.main.url(forResource: "acc-orientation", withExtension: "grt")
try FileManager.default.copyItem(at: bundleURL!, to: url)
} catch {
print("File already exists")
}
return true
}
With either of these solutions your FileLoader::load method will return true.
Hope that helps.

MPMediaItemPropertyTitle with MPMediaPlayer and Podcast causes Fatal Error

My MPMediaplayer is working pretty well for Music, but when I start working with Podcasts things are different.
I'm trying to get two things:
1) The name the Podcast Title ("This American Life")
2) The Episode Title ("My Holiday")
This line of code works fine to get the Podcast Title:
let podTitle:String = (myMP.nowPlayingItem?.podcastTitle)!
However this line should get the Episode Title:
let episode:String = myMP.nowPlayingItem?.value(forProperty: "MPMediaItemPropertyTitle") as! String
but causes a crash with this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
How can I get the Episode Title for a given Podcast?
MPMediaItemPropertyTitle is not the string property key; it is the name of a constant whose value is the property key. So, where you have
let episode:String =
myMP.nowPlayingItem?.value(forProperty: "MPMediaItemPropertyTitle") as! String
...remove the quotation marks:
let episode:String =
myMP.nowPlayingItem?.value(forProperty: MPMediaItemPropertyTitle) as! String
I think what you are doing is still very foolish (you are asking to crash), but at least this way you stand a chance of success. What I do is actually more like this:
let temp = myMP.nowPlayingItem?.value(forProperty: MPMediaItemPropertyTitle)
let episode = temp as? String ?? ""
That way you always end up with a string, which may be empty if there's a problem, and you won't crash.

sharing a url with whatsapp/Facebook

I'm trying to share a message string from my app in Whatsapp. my code for this is below. my messageString is my message. If messageString does not include a website link this works with no issue. However I now need to include a link in what I'm sharing. Now when I share I just get a blank message in Whatsapp. I have other share functions in the app such as email/sms which display messageString with a url but my Whatsapp one no longer does. How can I fix this?
let urlStringEncoded = messageString.addingPercentEncoding(withAllowedCharacters: .urlUserAllowed)
let url = URL(string: "whatsapp://send?text=\(urlStringEncoded!)")
if UIApplication.shared.canOpenURL(url!) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url!)
}
}
To answer my own question. The problem turned out to be the encoding of the URL with included an Equals Sign. I was unable to send a string containing = with or with out a url.
the problem was fixed by by changing .withAllowedCharacters to
let urlStringEncoded = messageString.addingPercentEncoding(withAllowedCharacters: .alphanumerics)

Web scraping in apple swift 3

I'm a Swift beginner and I'm trying to figure out how to retrieve text from a web article, create a new text file and save the text data into it (Using Swift Playgrounds). Is this possible?
The only thing I could find online regarding the subject was this, and I don't think it is even written for Swift 3:
P.S. If my question needs more details, please let me know instead of putting it on hold. Thanks!
import Cocoa
var url = NSURL(string: "http://finance.yahoo.com/news/tv-news-ces-2017-120931816.html")
if url != nil {
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
print(data)
if error == nil {
var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding) as NSString!
print(urlContent)
That's Swift 2.3. In Swift 3 use URL instead of NSURL and use URLSession rather than NSURLSession, etc. You'd also use String rather than NSString. E.g.
let url = URL(string: "http://finance.yahoo.com/news/tv-news-ces-2017-120931816.html")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
let string = String(data: data, encoding: .utf8)
print("\(string)")
}
task.resume()
If you're going to do this in a playground, remember that this runs asynchronously, so you'll need to set needsIndefiniteExecution.
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
To actually parse the HTML, you should consider using a HTML parser like TFHpple (written in Objective-C, but still works great from Swift) or NDHpple (a Swift version, in which I don't have as much confidence as TFHpple, but probably would work fine).
You might want to see How to Parse HTML on iOS. It's dated, but walks you through the concepts (making sure you're not violating ToS of the web site, how to use the parsers, etc.).
If you want to save this to a file, you can do something like:
let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("test.dat")
try! data.write(to: fileURL)
You can use whatever file extension you want.

NSUserdefaults lost its memory

I am using Swift 3 and facebook login, and save some basic values i get from the facebookSDKGraphrequest in user defaults like this:
// save basic settings in standard user defaults: age, gender, first name
let defaults = UserDefaults.standard
defaults.set(firstName!, forKey: "firstName")
defaults.set(gender!, forKey: "gender")
let currentUserAge = calculateAge(birthday: birthDay!)
defaults.set(currentUserAge, forKey: "age")
I only need to run that code once when the user logs in, then I save the info into userDefaults, and I'm set.
so the issue i Have is that recently, the last few times I build and open my app on the phone, my userDefaults was empty.
when I run this code in another class,
let defaults = UserDefaults.standard
var name = defaults.object(forKey: "firstName") as? String
var age = defaults.integer(forKey: "age")
var gender = defaults.object(forKey: "gender") as? String
I was getting nil for all those values. So now I'm paranoid because if this happens in a user's app, it will crash.
Can someone explain why UserDefaults would lose its memory?
I did nothing to delete or reset the values. I went through probably 100 builds and this last time, UserDefaults' values were nil.
NSUserDefaults does not store immediately its content to disk. synchronize method is called periodically to write data to disk. It seems it is not called in your case. Try call defaults.synchronize when you have finished setting up your values.
(source : https://developer.apple.com/reference/foundation/userdefaults/1414005-synchronize)