Swift Sqlite model binding - ios

I started programming on iOS and I come from ASP.NET MVC.
I am trying to find a way to bind the models to a database, or at least something similar to entity framework model binding, I want to create two models like so:
class A{
let id = Expression<Int64>("id")
let name = Expression<String?>("name")
}
class B{
let id = Expression<Int64>("id")
let name = Expression<A>("A")
}
I would like to know if this is possible or could someone point me to the right direction and when I fetch data I want to fill my models from sqlite
Thanks in advance
and sorry if I was unclear in my question.

The GRDB.swift SQLite library focuses on helping applications loading models (aka records) from the database: https://github.com/groue/GRDB.swift#records
Maybe that's just what you are looking for:
let persons = try Person.fetchAll(db) // [Person]
for luckyPerson in try Person.filter(isLucky).order(name).fetchAll(db) { person in
print(person.name)
}
if let person = try Person.fetchOne(db, key: 1) {
print(person.name)
}

Related

How to fetch relationship entities when performing a core data migration?

I am performing a core data migration. I have this subclass for my migration:
class TagtoSkillMigrationPolicyV1toV2: NSEntityMigrationPolicy {
override func createDestinationInstances( forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
// 1 - create destination instance of skill and skillmetadata
let skillEntityDescription = NSEntityDescription.entity( forEntityName: "Skill",in: manager.destinationContext)
let newSkill = Skill(entity: skillEntityDescription!, insertInto: manager.destinationContext)
let skillMetaDataDescription = NSEntityDescription.entity(forEntityName: "SkillMetaData", in: manager.destinationContext)
newSkill.metaData = SkillMetaData(entity: skillMetaDataDescription!, insertInto: manager.destinationContext)
var posts = sInstance.value(forKey: "posts")
posts.sort {
($0.timeStamp! as Date) < ($1.timeStamp! as Date)
}
if let mostRecentThumbnail = posts.first?.postImage {
let thumbnail = Utils.makeThusmbnail(url: URL(string: mostRecentThumbnail)!, for: 150.0)
newSkill.metaData?.thumbnail = thumbnail?.pngData()
}
if let name = sInstance.value(forKey: "name"){
newSkill.setValue(name, forKey: "name")
}
manager.associate(sourceInstance: sInstance, withDestinationInstance: newSkill, for: mapping)
}
}
This custom migration is from my old Tag entity to my new Skill entity. For each Tag, it creates a skill entity and a skillmetaData entity that it attaches to it. There are no entries in the .xcmappingmodel file, as I am doing everything manually - please let me know if I still need entries there.
Right now, when it runs, it gives a signal SIGABRT:
Could not cast value of type '_NSFaultingMutableOrderedSet' (0x7fff87c44e80) to 'NSArray' (0x7fff87c51fb0).
2020-04-14 15:55:40.108927-0700 SweatNetOffline[28046:3645007] Could not cast value of type '_NSFaultingMutableOrderedSet' (0x7fff87c44e80) to 'NSArray' (0x7fff87c51fb0).
When I set a breakpoint and inspect sInstance.value(forKey: "posts"), I get
▿ Optional<Any>
- some : Relationship 'posts' fault on managed object (0x6000010dcf00) <NSManagedObject: 0x6000010dcf00> (entity: Tag; id: 0xdf4b05c2e82b2c4e <x-coredata://A6586C18-60C3-40E7-B469-293A50EB5728/Tag/p1>; data: {
latestUpdate = "2011-03-13 00:17:25 +0000";
name = flowers;
posts = "<relationship fault: 0x600002607f40 'posts'>";
uuid = "4509C1B3-7D0F-4BBD-AA0B-7C7BC848DA80";
})
so its not entirely loading those posts - its giving that fault there.
The goal is to get the thumbnail from the latest of those posts. How can I access this relationship item?
I found a way to do it using
let lastThumbnail = sInstance.mutableOrderedSetValue(forKeyPath: "posts.postImage").lastObject
Reading this helped a lot. My understanding is at this point in the migration, we are dealing with NSManagedObjects and never the actual Model which would have attributes like "posts" that it usually does in core data.
In Objective-C especially and swift to some extent, NSObjects are accessed with key-value coding. They are accessed indirectly - meaning instead of reading the object itself, you are reading what the NSMutableOrderedSet wants to show you for that key - so it can do stuff to it before presenting it. Thus you can't for instance, fetch just "forKey: posts" and then expect posts to have the attributes your post usually does. Anyone is welcome to flesh out this explanation if they know more :).
One thing I was trying to do is to fetch by that key-path in a sortable way - so that I can find the post that has the most recent timestamp and then use that associated thumbnail. Instead I am using .lastObject which works because this is an NSMutableOrderedSet and not an NSMutableSet because my original model specified this relationship to be an ordered relationship. This means that it will pick the last inserted item which is ok for me for now. Still interested in a way to filter that result though if anyone has advice.

Filtering realm on pivot table relationship

Using realm I have setup 2 objects: User and Question.
Each User answers a Question, this is stored in a third object like so:
class Answer : Object {
dynamic var user: User?
dynamic var question: Question?
dynamic var answerText = ""
}
What I need to do, is given a user object and a question object, get the related Answer object. In semi-SQL pseudo-code something like:
SELECT FROM Answers WHERE user = User and question = Question
So...How do I achieve this?
Bonus points if there is a way of not having to fetch the question object and instead use the primary key of that object (i have the user object, but only a question id, so i have to resolve the question object first), so something like:
SELECT FROM Answers WHERE user = User and question.id = Question.id
Also because realm doesn't load the entire object into memory until you need it, I don't think it's capable of that.
Note: I've simplified by problem a little. There is a good reason I have the no primary answer id, so please don't tell me to add one.
You will need to change your User model to include an inverse relationship with Answer. This will automatically include all answers where user equals the current user.
Then you will need to query the users, access their answers property and filter that based on the questions.
I have created these skeleton classes so that I can test the query, I know that your User and Question class might be different, so change the primaryKey and question filter to suit your exact class definition.
class User: Object {
let answers = LinkingObjects(fromType: Answer.self, property: "user")
dynamic var userName = ""
override class func primaryKey()->String {
return "userName"
}
}
class Answer : Object {
dynamic var user: User?
dynamic var question: Question?
dynamic var answerText = ""
}
class Question: Object {
dynamic var title = ""
}
let questionId = ""
let questions = realm.object(ofType: User.self, forPrimaryKey: "userName")?.answers.filter("question.id = %#",questionId)

Connecting Realm with Swift Bond

I have read the following question on Stack Overflow (What is the best way to connect Realm and SwiftBond), which is unfortunately 2 years old. I am in the same position where I would like to create an observable instance of a Realm object, whereby the Realm object will get updated from the UI, and can then be written to Realm.
From what I have read and understood, I think that Observable(object:keyPath:) no longer exists in Bond v6, but I can't quite figure out what the alternative is, though I think it has something to do with dynamic(keyPath:ofType:).
I am struggling to find an example or any documentation which would allow me to do the Bond 6 version of the following:
class Dog: Object {
dynamic var name = ""
dynamic var birthdate = NSDate(timeIntervalSince1970: 1)
}
extension Dog {
class ObservableDog {
let name: Observable<String>
let birthdate: Observable<NSDate>
init(dog: Dog) {
name = Observable(object: dog, keyPath: "name")
birthdate = Observable(object: dog, keyPath: "birthdate")
}
}
func observableVariant() -> Dog.ObservableDog {
return ObservableDog(dog: self)
}
}
let myDog = Dog().observableVariant()
myDog.name.observe { newName in
print(newName)
}
myDog.name.bindTo(nameLabel.bnd_text)
realm.write {
myDog.name.value = "Jim"
}
I'm now on my second day of attempting this, so I'm hoping that someone will be able to help.
Thanks in advance.

How to save multiple entries in table using YapDatabase for swift

I am using YapDatabase for storing my objects.Need how to store multiple entries in a table.
For example : I need to save all students information in Student table. So how can I do that with YapDatabase using Swift.
var paths = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)
let baseDir:String = paths.count > 0 ? paths[0] as String : NSTemporaryDirectory() as String
let path = baseDir.stringByAppendingString("Database.sqlite")
yapDB = YapDatabase(path: path)
yapDataBaseConection = yapDB?.newConnection()
yapDataBaseConection?.asyncReadWriteWithBlock({ transaction in
}, completionBlock: {
});
There are plenty of examples on YapDatabase Wiki
First of all you should make a proper class that represents your student. You can read about it in this article.
Then you should save an array of your students to database. asyncReadWriteWithBlock is okay.
for student in students {
transaction.setObject(student, forKey:student.uniqueID, inCollection:"students")
}
That is all!
Another comlex task is to sort all the students and to show them in a UITableView. To make it work you should read about View and Mappings
If you are looking for some swift examples - you can check my sample project. It is swift 2.3 compatible.
Hope it helps...

Realm query Object property field by its property

I'm developing an application for iOS using Swift and chose Realm as a database solution for it. I asked one question about Realm and now I have another.
Suppose we have a schema like this:
class Person: Object {
dynamic var id: String = NSUUID().UUIDString
dynamic var name: String?
dynamic var cars: Car?
class Car: Object {
dynamic var name: String?
I have one class (Person) that contains any number of objects of another class (Car). Car that are "linked" with the Person has some properties in context of that Person (and they can be different for same Car for different Persons or for two similar Cars for one Person). Using List<...>() we can not store such properties for each Item, am I right?
If we use Car only for one Person and only once we can create another class that includes only additional properties for Cars and links them with ID of Person plus ID of Car. But it does't work if we have two similar Cars with different additional properties.
So, how I see the solution. Have one table (class) stores ID of Person, ID of one Car and additional properties for this Car. For another Car for the same Person it has the same Person ID, Car ID (same or not) and another additional properties for this instance of a Car.
There is a problem (and a question that I mean). Having that structure I want to query all Cars from that table with their additional properties that have Person ID equals to some_id. How should I do this? Or maybe what another structure (maybe using List<...>) I should have to achieve such kind of behavior?
What is FastList exactly ?
If you want Items to have a property of Lists collection.
You have to redefine your Realm model. something like this.
class Car:Object{
dynamic var createDate: NSDate = NSDate()
}
class Person:Object{
let cars = List<Car>()
}
and query by predicate like this
let realm = Realm()
var ownedCarsFilterByDate = realm.objects(Person).filter("ANY cars.createDate = '\(date)'")
Edited to updated question
Your solution is to create table class, which has 'Person' , 'Car' and 'Context Attribute'.
Your model would be like this
class PersonAndCarRelation:Object{
dynamic var person: Person?
dynamic var car: Car?
dynamic var contextAttribute = ""
}
and you can query all cars associated with person
let personID = "123456789"
let personAndCarArray = realm.objects(PersonAndCarRelation).filter("person.id == \(personID)")
for personAndCar in personAndCarArray{
let personName = personAndCar.person.name
let carName = personAndCar.car.name
let context = personAndCar.contextAttribute
println("I am \(personName). I have a \(carName) with \(context)")
}

Resources