Im new to realm. I have read the documentation and followed the examples in creating and querying the database. Whenever I run the code below it crashes on the line
let realm = Realm
I have a view controller that I am trying to query realm like so:
// Get the default Realm
let realm = Realm()
// Query using an NSPredicate
let predicate = NSPredicate(format: "s3_url = %#", s3_url)
let medias = realm.objects(MediaRealm).filter(predicate)
everytime i run this i get Thread1: EXC_BAD_ACCESS
let realm = Realm()
import UIKit
import Alamofire
import RealmSwift
class ViewController: UIViewController {
let mediaCollection = MediaCollection()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loadInitialData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadInitialData(){
//connect to url
mediaCollection.fetch(onSuccess:{ self.onEventFetched($0) }, onError:{ self.displayAlert($0)})
}
func onEventFetched()->Void{
println("Got stuff")
println(mediaCollection.models.count)
processCollection()
}
func processCollection()->Void{
for media in mediaCollection.models{
let m = media as! Media
println(m.s3_url)
println(m.title)
if let s3_url = m.s3_url{
//query database, if it already exists dont download it
// Get the default Realm
let realm = Realm()
// Query using an NSPredicate
let predicate = NSPredicate(format: "s3_url = %#", s3_url)
let medias = realm.objects(MediaRealm).filter(predicate)
if medias.count == 0 {
println("no files found")
//downloadFile(s3_url)
}
}
}
}
What am I doing wrong?
This was caused by me adding new fields to the database and not running a migration.
Solution was to delete the app and start over.
Related
I am trying to extract all the frames from a video. Code is working fine for the video in Bundle Path, but it fails when i give a link of the video or I try to pick the video from File Manager.
Following are the issues that i have encountered:
AVAsset throws an error "Error Domn=AVFoundationErrorDomain Code=-11838 "Cannot initialize an instance of AVAssetReader with an asset at non-local URL".
When video is picked from Documents Directory, the fetched asset tracks are always 0.
Following is the code that i am using right now.
let asset = AVAsset(url: URL(string: path!)!)
self.playerController.player = AVPlayer(playerItem: AVPlayerItem(asset: asset))
if let path = path {
let asset:AVAsset = AVAsset(url: URL(string: path)!)
self.playerController.player = AVPlayer(playerItem: AVPlayerItem(asset: asset))
self.player.play()
asset.loadTracks(withMediaType: .video) { fetchedTracks, err in
do{
let reader = try AVAssetReader(asset: asset)
// read video frames as BGRA
let trackReaderOutput = AVAssetReaderTrackOutput(track: (fetchedTracks?.first)!, outputSettings:[String(kCVPixelBufferPixelFormatTypeKey): NSNumber(value: kCVPixelFormatType_32BGRA)])
reader.add(trackReaderOutput)
reader.startReading()
while let sampleBuffer = trackReaderOutput.copyNextSampleBuffer() {
print("sample at time \(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))")
if CMSampleBufferGetImageBuffer(sampleBuffer) != nil {
// process each CVPixelBufferRef here
guard let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
let ciimage = CIImage(cvPixelBuffer: imageBuffer)
self.frames.append(UIImage(ciImage: ciimage))
// see CVPixelBufferGetWidth, CVPixelBufferLockBaseAddress, CVPixelBufferGetBaseAddress, etc
}
}
self.collectionViewFrames.reloadData()
} catch let err {
print(err)
}
}
}
So I am banging my head, I realized my stand along Watch App had a STUPID long name of "App Name - WatchKit App" so I went into my Target and changed the Display Name to "App Name" removing WatchKit App. Well now my app won't validate when uploading to the Appstore. I get the message - Invalid Info.plist key. The key WKExtensionDelegateClassName in bundle App Name.app/Watch/App Name WatchKit App.app is invalid.
My Info.plist has the value of
<key>WKExtensionDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string>
I have confirmed that I have #WKExtensionDelegateAdaptor(ExtensionDelegate.self) var delegate in my #main for the SwiftUI App. And when I print a few values in my app launch I get the following confirmations:
Super Init - ExtensionDelegate
Contentview
applicationDidFinishLaunching for watchOS
Super Init - ExtensionDelegate
Optional(Wasted_Time_Watch_Extension.MeetingSetup)
Optional(Wasted_Time_Watch_Extension.MeetingStatistics)
Optional(Wasted_Time_Watch_Extension.Meeting)
applicationDidBecomeActive for watchOS
update complication
I create three classes at launch and print this in the log with print(ExtensionDelegate.shared.Setup as Any) , etc. The other lines are just confirming where I am at app startup.
This is a WatchOS8 application and I am running Xcode version Version 13.1 (13A1030d).
Update - Here's the entry in my plist
<key>WKExtensionDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string>
<key>WKWatchOnly</key>
And my App code
import SwiftUI
#if os(watchOS)
import ClockKit
#endif
struct DelegateKey: EnvironmentKey {
typealias Value = ExtensionDelegate
static let defaultValue: ExtensionDelegate = ExtensionDelegate()
}
extension EnvironmentValues {
var extensionDelegate: DelegateKey.Value {
get {
return self[DelegateKey.self]
}
set {
self[DelegateKey.self] = newValue
}
}
}
#main
struct WastedTimeWatchApp: App {
#WKExtensionDelegateAdaptor(ExtensionDelegate.self) var delegate
let prefs: UserDefaults = UserDefaults(suiteName: suiteName)!
#SceneBuilder var body: some Scene {
WindowGroup {
NavigationView {
ContentView()
.environment(\.extensionDelegate, delegate)
}
}
}
}
class ExtensionDelegate: NSObject, WKExtensionDelegate, ObservableObject {
#Environment(\.extensionDelegate) static var shared
// variables removed to simplify posting
override init() {
print("Super Init - ExtensionDelegate")
super.init()
}
func applicationDidFinishLaunching() {
print("applicationDidFinishLaunching for watchOS")
ExtensionDelegate.shared.meetingSetup = MeetingSetup()
print(ExtensionDelegate.shared.meetingSetup as Any)
ExtensionDelegate.shared.meetingStatistics = MeetingStatistics()
print(ExtensionDelegate.shared.meetingStatistics as Any)
ExtensionDelegate.shared.meeting = Meeting()
print(ExtensionDelegate.shared.meeting as Any)
}
func applicationDidBecomeActive() {
print("applicationDidBecomeActive for watchOS")
print("update complication")
let server = CLKComplicationServer.sharedInstance()
for complication in server.activeComplications ?? [] {
server.reloadTimeline(for: complication)
}
}
func applicationDidBecomeInactive() {
print("update complication")
let server = CLKComplicationServer.sharedInstance()
for complication in server.activeComplications ?? [] {
server.reloadTimeline(for: complication)
}
print("applicationDidBecomeInactive for watchOS")
}
}
I figured this out... I had duplicated the plist entry in both the WatchKit App and WatchKit Extension plist file. Removed it from the list WatchKit Extension plist and all is working fine.
I've created an app using Swift3 and Xcode8, and using FMDB as my database, when it runs on the simulator it can get the data from data.db,but when it runs on the generic device (which is my phone), there's no data in the tableView, also could't insert records. I added data.db into my project, but when I changed records on simulator, records in data.db didn't change, but I printed the path, it pointed to simulator folder, database in that folder will changed along the modify in simulator and that folder's path changes almost every time. I'm so confused, is that because I didn't connected to my database in fact?
Here's the Utility.Swift which holds common and often reused function
import UIKit
class Utility: NSObject {
class func getPath(_ fileName: String) -> String {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(fileName)
print(fileURL.path)
return fileURL.path
}
class func copyFile(_ fileName: NSString){
let dbPath: String = getPath(fileName as String)
let fileManager = FileManager.default
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
}
let alert: UIAlertView = UIAlertView()
if (error != nil) {
alert.title = "Error Occured"
alert.message = error?.localizedDescription
}
else {
alert.title = "Successfully Copy"
alert.message = "Your database copy successfully"
}
alert.delegate = nil
alert.addButton(withTitle: "Ok")
alert.show()
}
}
class func invokeAlertMethod(_ strTitle: NSString, strBody: NSString, delegate: AnyObject?) {
let alert: UIAlertView = UIAlertView()
alert.message = strBody as String
alert.title = strTitle as String
alert.delegate = delegate
alert.addButton(withTitle: "Ok")
alert.show()
}
}
and StudentDataBase.swift contains Query languages
import UIKit
let sharedInstance = StudentDataBase()
class StudentDataBase : NSObject {
var database: FMDatabase? = nil
class func getInstance() -> StudentDataBase{
if((sharedInstance.database) == nil)
{
sharedInstance.database = FMDatabase(path: Utility.getPath("data.db"))
}
return sharedInstance
}
func addStuData(_ student: Student) -> Bool{
sharedInstance.database!.open()
let isInserted = sharedInstance.database!.executeUpdate("INSERT INTO [Student info] (StudentID, FirstName, LastName, PhoneNumber) VALUES (?, ?, ?, ?)", withArgumentsIn: [student.studentID, student.fstName, student.lstName, student.phoneNum])
sharedInstance.database!.close()
return isInserted
}
func updateStuData(_ student: Student) -> Bool {
sharedInstance.database!.open()
let isUpdated = sharedInstance.database!.executeUpdate("UPDATE [Student info] SET FirstName=?, LastName=?, PhoneNumber=? WHERE StudentID=?", withArgumentsIn: [student.fstName, student.lstName, student.phoneNum, student.studentID])
print(student)
print(isUpdated)
sharedInstance.database!.close()
return isUpdated
}
func deleteStuData(_ student: Student) -> Bool {
sharedInstance.database!.open()
let isDeleted = sharedInstance.database!.executeUpdate("DELETE FROM [Student info] WHERE StudentID=?", withArgumentsIn: [student.studentID])
sharedInstance.database!.close()
return isDeleted
}
func getAllStuData() -> [Student] {
sharedInstance.database!.open()
let resultSet: FMResultSet! = sharedInstance.database!.executeQuery("SELECT * FROM [Student info]", withArgumentsIn: nil)
var marrStudentInfo : [Student] = []
if (resultSet != nil) {
while resultSet.next() {
let student : Student = Student()
student.studentID = resultSet.string(forColumn: "StudentID")
student.fstName = resultSet.string(forColumn: "FirstName")
student.lstName = resultSet.string(forColumn: "LastName")
student.phoneNum = resultSet.string(forColumn: "PhoneNumber")
marrStudentInfo.append(student)
}
}
sharedInstance.database!.close()
return marrStudentInfo
}
}
also in AppDelegate.swift, I've written:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Utility.copyFile("data.db")
return true
}
I'm new to Swift and FMDB, please explain and I'll try my best.Thank you very much!!!!
Edit:
After I downloaded the contents in my phone and the database is blank. And after I throwaway the error, it shows
Inserted failed:Optional("no such table: Student info")
SQLite offers good error reporting and you should avail yourself of it. So, for example, if any of these executeUpdate calls fail, you should examine the lastErrorMessage (before you close the database) so you know why it failed.
Personally, I'd suggest using executeQuery(_:values:) and executeUpdate(_:values:) rather than executeQuery(_:withArgumentsIn:) and executeUpdate(_:withArgumentsIn:) because the former methods throw errors (which shifts to a more active error handling paradigm from the more passive one in which you have to manually remember to check for errors). But regardless of how you do your error handling, do it, or else we're all guessing.
Personally, I'd suggest using Xcode's "Devices" window and download your app's bundle (see https://stackoverflow.com/a/38064225/1271826) and look at the database. I bet it's a blank database with no tables defined at all. In terms of why, it could be because you did some earlier testing and there's an old version of the database in the Documents folder. Try completely deleting the app from the device and re-running and see if that fixes it.
Other, more obscure sources of problem include filename capitalization (e.g. Data.db vs data.db). The macOS file system will often handle that gracefully because it's not (generally) case sensitive. But iOS devices are case sensitive.
But, like I said, we're flying blind until (a) you add some error handling to your code so you can see why its failing; and (b) look at the database on the device to see what it looks like.
I keep receiving this error, CFNetwork SSLHandshake failed (-9807), in the debug window and have no data displayed when trying to populate a UITableViewController with Firebase data. I have tried this potential solution iOS 9 ATS and Firebase REST but still have the issue.
The code I am using is (Credit to #DavidEast)
class TableViewController1: UITableViewController {
// your firebase reference as a property
var ref: Firebase!
// your data source, you can replace this with your own model if you wish
var items = [FDataSnapshot]()
override func viewDidLoad() {
super.viewDidLoad()
// initialize the ref in viewDidLoad
ref = Firebase(url:"https://the-lighthouse-app.firebase.io/states")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// listen for update with the .Value event
ref.observeEventType(.Value) { (snapshot: FDataSnapshot!) in
var newItems = [FDataSnapshot]()
// loop through the children and append them to the new array
for item in snapshot.children {
newItems.append(item as! FDataSnapshot)
}
// replace the old array
self.items = newItems
// reload the UITableView
self.tableView.reloadData()
}
}
}
I know there are many related posts but I could notice that none of them explain the way to do it, most says to look the documentation, I looked already but seems to have a lack of information on bringing a pre populated database to Swift
I have an old database .db with more than 60k lines three columns that I want to bring to my swift SQLite app using FMDB wrapper, the app is working in the iPhone, I tried to just drag the contacts.db to the app but I can not access it. The app when run in the device always start a new database.
I copied the database to the supporting files folder and tried in the app folder as well, could not access from neither
Is there anybody who alredy did that and willing to show me how to do it?
In summary, what I am looking is to insert my contacts.db into the app (bundle) so I can access in the device not in the simulator. I don't need to add delete or edit the contact.db in the device, the database is loaded with all information needed, the user will only for search and be able to display results.
class ViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var address: UITextField!
var databasePath = NSString()
override func viewDidLoad() {
super.viewDidLoad()
if let resourceUrl = NSBundle.mainBundle().URLForResource("contacts", withExtension: "db") {
if NSFileManager.defaultManager().fileExistsAtPath(resourceUrl.path!)
}
let filemgr = NSFileManager.defaultManager()
let dirPaths =
NSSearchPathForDirectoriesInDomains(.DocumentDirectory,
.UserDomainMask, true)
let docsDir = dirPaths[0] as! String
databasePath = docsDir.stringByAppendingPathComponent(
"contacts.db")
if !filemgr.fileExistsAtPath(databasePath as String) {
let contactDB = FMDatabase(path: databasePath as String)
if contactDB == nil {
println("Error: \(contactDB.lastErrorMessage())")
}
if contactDB.open() {
let sql_stmt = "CREATE TABLE IF NOT EXISTS CONTACTS (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, ADDRESS TEXT, PHONE TEXT)"
if !contactDB.executeStatements(sql_stmt) {
println("Error: \(contactDB.lastErrorMessage())")
}
contactDB.close()
} else {
println("Error: \(contactDB.lastErrorMessage())")
}
}
}
Swift 2 Example
A few assumptions:
You have added libsqlite3.tbd to your project correctly.
You have added an sqlite database to your project correctly.
You have copied the required FMDB files to your project correctly.
You have a ViewController called ViewController.swift
In Xcode open your ViewController.swift file and find the following code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Add the following code below the comment and please remember to change the name of the database in lines 4 & 5.
// Start of Database copy from Bundle to App Document Directory
let fileManager = NSFileManager.defaultManager()
let documentsPath = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0])
let destinationSqliteURL = documentsPath.URLByAppendingPathComponent("sqlite.db")
let sourceSqliteURL = NSBundle.mainBundle().URLForResource("sqlite", withExtension: "db")
if !fileManager.fileExistsAtPath(destinationSqliteURL.path!) {
// var error:NSError? = nil
do {
try fileManager.copyItemAtURL(sourceSqliteURL!, toURL: destinationSqliteURL)
print("Copied")
print(destinationSqliteURL.path)
} catch let error as NSError {
print("Unable to create database \(error.debugDescription)")
}
}
// Let's print the path to the database on your Mac
// so you can go look for the database in the Finder.
print(documentsPath)
let db = FMDatabase(path: destinationSqliteURL.path)
// Let's open the database
if !db.open() {
print("Unable to open database")
return
}
// Let's run some SQL from the FMDB documents to make sure
// everything is working as expected.
if !db.executeUpdate("create table test(x text, y text, z text)", withArgumentsInArray: nil) {
print("create table failed: \(db.lastErrorMessage())")
}
if !db.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["a", "b", "c"]) {
print("insert 1 table failed: \(db.lastErrorMessage())")
}
if !db.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["e", "f", "g"]) {
print("insert 2 table failed: \(db.lastErrorMessage())")
}
if let rs = db.executeQuery("select x, y, z from test", withArgumentsInArray: nil) {
while rs.next() {
let x = rs.stringForColumn("x")
let y = rs.stringForColumn("y")
let z = rs.stringForColumn("z")
print("x = \(x); y = \(y); z = \(z)")
}
} else {
print("select failed: \(db.lastErrorMessage())")
}
db.close()
You should be able to run the project now and have your database copied.
Previous answer in case it helps someone
The documentation for FMDB is pretty helpful on this topic. Using the example there, the code below should work assuming the following things:
You are using Swift 1.2 or later.
You have properly added FMDB to your project.
An SQLite database called contacts.db was properly added into the project.
In Build Phases, then Link Binary With Libraries that you have added libsqlite3.dylib.
//
// ViewController.swift
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var address: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let filemgr = NSFileManager.defaultManager()
let documentsFolder = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
let path = documentsFolder.stringByAppendingPathComponent("contacts.db")
let database = FMDatabase(path: path)
if !database.open() {
println("Unable to open database")
return
}
if !database.executeUpdate("create table test(x text, y text, z text)", withArgumentsInArray: nil) {
println("create table failed: \(database.lastErrorMessage())")
}
if !database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["a", "b", "c"]) {
println("insert 1 table failed: \(database.lastErrorMessage())")
}
if !database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["e", "f", "g"]) {
println("insert 2 table failed: \(database.lastErrorMessage())")
}
if let rs = database.executeQuery("select x, y, z from test", withArgumentsInArray: nil) {
while rs.next() {
let x = rs.stringForColumn("x")
let y = rs.stringForColumn("y")
let z = rs.stringForColumn("z")
println("x = \(x); y = \(y); z = \(z)")
}
} else {
println("select failed: \(database.lastErrorMessage())")
}
database.close()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
To test, just use the Build and Run button and you should see run the simulator and the following results in Xcode's console:
x = a; y = b; z = c
x = e; y = f; z = g
To show the Console, type Shift + CMD +R or go to View -> Debug Area -> Activate Console
First, this is not Core Data related if you are using FMDB.
Second, it is actually fairly simple.
In Xcode you include the SQL file as a resource that gets loaded into your application when it is built.
Then you locate the file using [[NSBundle mainBundle] URLForResource:#"file" withExtension:#"sqlite"]. From there you proceed with FMDB as normal.
If you want to write to that file then you will need to copy it to a writeable location in your application sandbox.
Update 1
You are locating the file in the bundle but you are not copying it out of the bundle and into the documents directory before attempting to open it. You need to copy it out of the bundle if it does not currently exist in the documents directory.
If you use Core data you should not use FMDB, use wrapper only for apps using SQLite/database. If you are using Swift it is a good idea to use wrapper if you are not familiar with C++ and databases.