How to delete LinkingObjects from an Object in realm? - swift

Lets say I have two models/objects for realm 1.0.0 - ArticleMO, and TagMO:
class ArticleMO: Object {
dynamic var title: String = ""
let tags = List<TagMO>()
}
class TagMO: Object {
dynamic var name: String = ""
let articles = LinkingObjects(fromType: ArticleMO.self, property: "tags")
}
How can I remove all articles with a tag?
I've tried:
if let tag = realm.objects(TagMO).filter("name == '\(tagName)'").first {
realm.delete(tag.articles)
}
But it results in an error like this:
RLMArray has been invalidated or the containing object has been
deleted
All my attempts around the issue - such as iterating through the objects after trying to store/detach them, result in the same error. What am I missing?

Related

Realm Swift Migration: String Wrapper List to String List

I am trying to migrate our Realm Swift schema from using a String wrapper class to the primitive String for collections. I am running into issues extracting the string values from the wrapper during migration.
Here is the wrapper class:
class StringDB: Object {
#objc var stringVal: String = ""
convenience init(string: String) {
self.init()
stringVal = string
}
}
Then I have an example class with a List<StringDB> property:
class MyObjDB: Object {
var emails: List<StringDB> = List<StringDB>()
#objc dynamic var id: String = UUID().uuidString
convenience init(_ emails: [StringDB]) {
self.init()
for email in emails {
self.emails.append(objectsIn: emails)
}
}
override static func primaryKey() -> String? {
return "id"
}
}
that I want to convert to a List<String>.
Here is my migration code.
let config = Realm.Configuration(schemaVersion: latestSchemaVersion, migrationBlock: {
migration, version in
if version < 2 {
migration.enumerateObjects(ofType: MyObjDB.className(), { old, new in
guard let old = old, let new = new else { return }
let oldEmails = old["emails"] as! List<StringDB>
let newEmails = List<String>()
for email in oldEmails {
newEmails.append(email.stringVal)
}
new["emails"] = newEmails
})
}
})
However, the let oldEmails = old["emails"] as! List<StringDB> cast fails. I've tried also casting the collection to List<MigrationObject> and then casting the individual objects to StringDB but that cast fails as well.
I've found a workaround that may be satisfactory (I haven't confirmed yet), of converting the MigrationObject directly to string using coercion like "\(email)" and then running a regEx that will extract a desired substring from the garbage (StringDB {\n\tstringVal = email#address.com;\n}), but I have no idea yet whether that will hold up in production, and I would prefer to work with something resembling a recommended way for doing this migration.
The Realm version is not shown in the question and there are a few typo's in the code. For older Realm versions, Lists should be defined thusly:
let emails = RealmSwift.List<StringDB>() //need RealmSwift. to differentiate it from Swift List
then newer versions should be this:
#Persisted var emails = RealmSwift.List<StringDB>()
Then this is a problem as it iterates over the emails array count times and appends the entire emails list over and over for every email in that list.
for email in emails {
self.emails.append(objectsIn: emails)
}
Also, when using older Realm versons with #Objc property types, they need to include dynamic. So on the StringDB object this
#objc var stringVal: String = ""
should be this
#objc dynamic var stringVal: String = ""
Lastly, the MyObjDB needs to have somewhere to put the new email list. You can't overwrite the old one as it's not the correct type. So add a property
class MyObjDB: Object {
var emails: List<StringDB> = List<StringDB>()
let updatedEmailList = List<String>()
Then to the question: See comments in code for the flow
How about this:
migration.enumerateObjects(ofType: MyObjDB.className()) { oldItem, newItem in
//instantiate a List of the old email objects as migration objects
let oldEmailList = oldItem!["emails"] as! List<MigrationObject>
//we're going to populate a new List with the email strings
var newEmailList = List<String>()
//iterate over the old list, extracting the string from the old object as
// a string an inject it into a new List of Strings
for oldEmailObject in oldEmailList {
let oldEmail = oldEmailObject["stringVal"] as! String
newEmailList.append(oldEmail)
}
//assign the new List of strings to a new emails property
newItem!["updatedEmailList"] = newEmailList
}

How to retrieve the ‘List<myObject>' realm array to array from realm model class?

My realm model class look like
class RoomRealmModel : Object {
dynamic var id: String = ""
var details = List<RoomDetailRealmModel>()
func saveItem() {
do {
let realm = try Realm()
realm.beginWrite()
realm.add(self, update: true)
try realm.commitWrite()
} catch{}
}
}
class RoomDetailRealmModel : Object{
dynamic var detailId: String = ""
dynamic var displayText: String = ""
}
I want to retrieve 'details' from the following.
details = RLMArray<RoomDetailRealmModel> <0x600000114f40> (
[0] RoomDetailRealmModel {
text = hello;
Counters = 9;
ParentID = ;
detailId = 33;
displayText = hello ;
}
);
I always get empty like in my console
(lldb) po (destinationData?[index]?.details)!
List<RoomDetailRealmModel> <0x600000853620> (
)
I am updating ‘details’ list via realm update command. I always get realm array.But I want to retrieve array type from realm array.Please help me, how to solve this issue
If you want to obtain [myObject] instead of List you can do something like this:
var array: [myObject] = [myObject]()
for object in myObjectList {
array.append(object)
}
Where myObjectList is List.
You can simply create a regular Swift Array from a Realm List by calling the initializer of Array accepting a Sequence, since List conforms to the sequence protocol.
So you can simply do
let room = RoomRealmModel()
let roomDetailsArray = Array(room.details)

Generic Type array with UITableView and Realm

I am using realm for database. I have Favourite Object and History Object.
I want to show in TableViewController. However, I don't want to do duplicated code. For now, in FavouriteViewController , it has var favs: Results<OGFav>? and HistoryViewController, it has var history: Results<OGHistory>?
Most of the code are the same and different is data type.
Example: it only different like following
if let object:OGFav = self.favs?[indexPath.row] {
In some place , I use like
let fav:OGFav = favs[indexPath.row]
For History
if let object:OGHistory = self.history?[indexPath.row] {
History also use like below
let history:OGHistory = self.history[indexPath.row]
How can I clean the code ? I am using two viewcontroller and code are the same excepted OGFav and OGHistory.
Update:
OGFav and OGHistory have the same data.
class OGFav: Object {
dynamic var word = ""
dynamic var def = ""
}
class OGHistory: Object {
dynamic var word = ""
dynamic var def = ""
dynamic var updatedAt = NSDate()
}

How to get unique value from Realm database in swift

I do news application in swift using Realm database. In my database have same news categories. How to get unique value from Realm database?
I use primary key
class News: Object {
dynamic var newsID: String = ""
dynamic var newsTitle: String = ""
dynamic var newsFullText: String = ""
dynamic var newsImage: String = ""
dynamic var newsAutor: String = ""
dynamic var newsCommentCount: String = ""
dynamic var newsSeenCount: String = ""
dynamic var newsDate: String = ""
dynamic var newsCategory: String = ""
override static func primaryKey() -> String? {
return "newsID"
}
}
I'm try to get
let realm = try! Realm()
let menuName = realm.objects(News)
for i in menuName.filter("newsCategory") {
nameLabel.text = i.newsCategory
}
But it is not work.
Starting from Realm 3.10 it's now possible to
Add Results.distinct(by:) / -[RLMResults
distinctResultsUsingKeyPaths:], which return a Results containing only
objects with unique values at the given key paths.
Old response - before Realm 3.10
It is not possible yet to obtain a "distinct"-like functonality from a Realm query (track the open issue here)
However, there are some workarounds suggested in the thread I mentioned above (please read it to get the full context), by user apocolipse :
// Query all users
let allUsers = Realm().objects(User)
// Map out the user types
let allTypes = map(allUsers) { $0.type }
// Fun part: start with empty array [], add in element to reduced array if its not already in, else add empty array
let distinctTypes = reduce(allTypes, []) { $0 + (!contains($0, $1) ? [$1] : [] )
Or better yet, a different approach using Sets (by user jpsim):
let distinctTypes = Set(Realm().objects(User).valueForKey("type") as! [String])
Obviously the workarounds aren't as efficient as a direct DB query, so use with care (and testing under realistic load).

Filtering Realm objects with Swift

I always get the following error when trying to filter my Realm database using NSPredicate:
Property 'text' is not a link in object of type 'getType'
I want to filter my Realm database to show only the items that have some specific text in them. This is what I've tried:
let realm = try! Realm()
let predicate = NSPredicate(format: "typez.text.filter = 'special'")
let filterThis = realm.objects(Publication).filter(predicate)
print(filterThis)
The relevant portion of my model classes is:
class Publication: Object, Mappable {
dynamic var id: Int = 0
var typez = List<getType>()
dynamic var url: String?
}
class getType: Object, Mappable {
dynamic var text: String = ""
}
You mentioned that the relevant portions of you model classes look like so:
class Publication: Object, Mappable {
dynamic var id: Int = 0
var typez = List<getType>()
dynamic var url: String?
}
class getType: Object, Mappable {
dynamic var text: String = ""
}
If I understand you correctly, you want to find Publication instances that have an entry in their typez list with text equal to special. You can express that as:
let realm = try! Realm()
let result = realm.objects(Publication).filter("ANY typez.text = 'special'")
print(result)
I was not liking the accepted answer here because it doesn't actually answer the question... but then it helped me more than I realized. I will now be using closures instead of NSPredicates whenever possible. The actual answer to this question should be a slightly modified version of #NSGangster's answer:
let realm = try! Realm()
//Array of publications
let realmObjects = realm.objects(Publication)
//any publication where .text property == special will be filtered. and filter out empty array
let filterThis = realmObjects.filter({ $0.typez.filter({ $0.text == "special" } != [] ) })
print(filterThis)
.. or something close to that.
But what I was looking for was a bit different. I needed a way to filter on exact words of a multi-word string, and using an NSPredicate with "CONTAINS" would match any containing substring, e.g. a search for "red" would match "fred". Realm doesn't support "LIKE" or regex yet, so using a closure was the only thing I could get to work:
//I was going for a "related terms" result for a dictionary app
let theResults = terms.filter(
{
//Looking for other terms in my collection that contained the
//title of the current term in their definition or more_info strings
$0.definition.components(separatedBy: " ").contains(term.title) ||
$0.more_info.components(separatedBy: " ").contains(term.title)
}
)
With as much of the day as I spent searching, hopefully this helps someone else with a similar issue.
I don't usually use NSPredicate's directly, instead I do an inline predicate closure within the filter paramter.
let realm = try! Realm()
//Array of publications
let realmObjects = realm.objects(Publication)
//any publication where .text property == special will be filtered. and filter out empty array
let filterThis = realmObjects.filter({ $0.getType.filter({ $0.text == "special" } != [] ) })
print(filterThis)