I need to remove old, empty models from a Realm Cocoa database.
There seems to be a way to do it in Java, but not in Swift. Is that correct?
If you remove a property and initiate a migration Realm will remove the corresponding column in the table:
class Dog: Object {
dynamic var name = ""
// dynamic var age = 0
}
But, if you remove the model definition entirely, the migration does not remove the table:
// class Dog: Object {
// dynamic var name = ""
// dynamic var age = 0
// }
Here's a screenshot from Realm Browser showing the empty tables I want to delete:
You can call Migration.deleteData(_:) within your migration block to specify that the named class should be completely removed from your Realm file.
For those who are working on JavaScript:
migration: (oldRealm, newRealm) => {
if (oldRealm.schemaVersion < *version*) {
newRealm.deleteModel('MODEL-NAME')
}
}
Related
I started trying Realm for IOS, so I created two classes:
Files Model
import Foundation
import RealmSwift
class FilesModel: Object {
#objc dynamic var id : Int = 0
#objc var fileName = ""
#objc dynamic var dateOfCreation = Date()
#objc dynamic var dateOfModification = Date()
#objc dynamic var type = ""
var file = List<Data>()
}
Groups Model
import Foundation
import RealmSwift
class GroupsModel: Object {
#objc dynamic var id : Int = 0
#objc dynamic var name = ""
#objc dynamic var dateOfCreation = Date()
#objc dynamic var dateOfModification = Date()
#objc dynamic var filesCount = Int()
var files = List<FilesModel>()
override static func primaryKey() -> String? {
return "id"
}
}
Now the thing is I am copying files into groups model file Object but I need to delete the parent object.
think of it as a move I am moving files into the folder.
what I have done is I save a copy of the file into the folder and delete the file from outside the folder.
Problem
when I delete the file outside the folder it will also delete the file inside.
My understanding of the problem
classes is a reference type so I am copying reference. So when I delete the reference it will delete the file from the whole project.
I have tried many solutions like deep copy and detached.
Thanks in Advance.
The issue is this statement
I am moving files into the folder
While that may be what you want to do, the objects you're moving aren't actually being moved. When you add a managed object to a a List property, it adds a reference to the object, not the object itself.
In other words, when a FilesModel is added to a GroupsModel files List property, it's a reference to the object. If you delete the FilesModel, it will also be gone from the List. But, removing it from the List does not change the original object.
However, Lists can also contain Embedded Objects - an embedded object only ever exists within it's parent object. Perhaps that would be an option?
So like this. Here's a couple of models
class FileModel: EmbeddedObject {
#Persisted var fileName = ""
}
class GroupsModel: Object {
#Persisted var files = List<FilesModel>()
}
Then instantiate a group and add a couple of file objects to it
let group = GroupsModel()
let f0 = FileModel()
f0.fileName = "some filename"
let f1 = FileModel()
f1.fileName = "another filename"
group.files.append(objectsIn: [f0, f1])
results in a group with two files that only ever appear within that group.
Another option is to simply make a copy of the object
let objectCopy = MyObject(value: objectToCopy)
and then add the objectCopy to your Groups model. But again, by doing that Realm will instantiate the objectCopy and actually add it back to Realm (possibly overwriting the original object).
I am trying to create a macOS project using Xcode 9.4.1 with Swift 4.2. I am using a Document-Based Application since I am entering data. The project has a class that includes an array as a property along with other properties. This class will be used to enter data that will need to persist and I'm trying to use IB and bindings to get this to work.
Here is the class (The actual class has much more String, Bool and Array properties):
class MyClass: NSObject {
#objc dynamic var property1 = ""
#objc dynamic var property2 = true
#objc dynamic var property3 = true
#objc dynamic var myClass2s: [myClass2] = []
}
class myClass2: NSObject {
#objc dynamic var property4 = ""
#objc dynamic var property5 = ""
}
I can get a tableView with an arrayController to work for the String and Bool properties. The difficulty for me comes in when I need to have another tableView for the MyClass2 data to be entered, which requires another arrayController. I can't seem to figure out how to bind it all together.
EDIT 9/30
I've tried binding the details tableView's Table Content to MyClassArrayController, Controller Key: selection, Model Key Path: myClass2s. I have a second Array Controller for MyClass2 so I can add/remove data to MyClass2. When I run the project, I don't get any errors, however, I can't add data to MyClass2 objects with its table View content bound to the array myClass2s.
So, in addition to what I tried above, I bound MyClass2ArrayController's Content Array to MyClassArrayController, selection, myClass2s and so far it appears to be working now.
I'm using Realm, the project is on version 1.0.0.
When I create a list of Realm Objects (with data obtained from a web API), then try to save them to the Realm using this utility function in a struct:
static func saveRealmObjects(objects: [Object]) {
defer {
// Never entered
}
for object in objects {
let realm = try! Realm()
do {
try realm.write {
print("TEST: 1: object: \(object)")
realm.add(object)
print("TEST: 2")
}
} catch {
// Never entered
}
}
}
(Please don't judge me on the exact structure, I've been toying around seeing if anything will work).
I can tell from liberal use of print statements (mostly removed above) that the function gets to TEST: 1 okay, but fails to make it to TEST: 2, for the very first Object in the list I pass to the function.
I should note this function does work the first time I use it with the data (say after wiping the simulator and launching the app afresh), but then if I recreate the Objects and try to save them again it gets stuck.
I assumed Realm would use the private key on the Objects and overwrite any if necessary. But it seems to just get stuck.
-
Then - after it's stuck - if I try and get another set of results from Realm (using a different Realm object) I get the following error:
libc++abi.dylib: terminating with uncaught exception of type realm::InvalidTransactionException: Cannot create asynchronous query while in a write transaction
FYI I'm creating a different Realm object using try! Realm()
-
For reference, here is the Object I'm trying to save:
import Foundation
import RealmSwift
class MyObject: Object {
// MARK: Realm Primary Key
dynamic var id: String = ""
override static func primaryKey() -> String? {
return "id"
}
// MARK: Stored Properties
dynamic var date: NSDate? = nil
dynamic var numA = 0
dynamic var numB = 0
dynamic var numC = 0
dynamic var numD = 0
dynamic var numE = 0
dynamic var numF = 0
dynamic var numG = 0
dynamic var numH = 0
// MARK: Computed Properties
var computedNumI: Int {
return numD + numE
}
var computedNumJ: Int {
return numF + numG
}
}
(The variable names have been changed.)
-
Hopefully I'm doing something obviously wrong - this is my first time using Realm after all.
If you have any ideas why it's sticking (perhaps it's a threading issue?), or want more info, please answer or comment. Thank you.
Being the clever clogs I am, I've literally just found the answer by reading the documentation:
https://realm.io/docs/swift/latest/#creating-and-updating-objects-with-primary-keys
The add to Realm line needed to look like this:
realm.add(object, update: true)
Where the update flag will update Objects already saved with that primary key.
-
Although it would have been nice if it either gave some sort of obvious warning or crash upon trying to add the same object, or didn't cause other queries and writes to Realm to crash.
I have a class, it has two properties:
var fruitsPackId: Int
var fruitsPackContent: Array<Fruit>?
Once the class is being initialized, I want to append data into fruintsPackContent from a local db according the the initialized fruitsPackId. I am not sure what is the best practice on that type of case.
What I did for now, is creating fruitsPackContent as a computed property, that pulls out the data from the local db using the fruitsPackId as reference id.
However, I feel that this is just not the right way of doing it, any ideas?
My code:
class FruitsPack: NSObject {
var fruitsPackId: Int
init(fruitsPackId: Int) {
self.fruitsPackId = fruitsPackId
}
var fruitsPackContent: Array<Fruit>? {
// Pulling data from local db here...
// For this example I create a dummy array with one instance of Fruit
let fruit1 = Fruit(fruitsPackId: self.fruitsPackId, fruitName: "Banana")
var fruits = Array<Fruit>()
fruits.append(fruit1)
return fruits
}
}
class Fruit: FruitsPack {
var fruitName: String
init(fruitsPackId: Int, fruitName: String) {
self.fruitName = fruitName
super.init(fruitsPackId: fruitsPackId)
}
}
EDIT:
Using lazy variable type did the work for me:
Class initialization has nothing to do with that property
Memory is being utilized only once property is being called
The property is being filled up with data only once
An instance method is available to be used by others
New code:
class FruitsPack: NSObject {
var fruitsPackId: Int
lazy var fruitsPackContent: Array<Fruit>? = self.getFruitsPackContent(self.fruitsPackId)
init(fruitsPackId: Int) {
self.fruitsPackId = fruitsPackId
}
func getFruitsPackContent(fruitsPackId: Int) -> Array<Fruit>? {
// Pulling data from local db here...
// For this example I create a dummy array with one instance of Fruit
let fruit1 = Fruit(fruitsPackId: self.fruitsPackId, fruitName: "Banana")
var fruits = Array<Fruit>()
fruits.append(fruit1)
return fruits
}
}
class Fruit: FruitsPack {
var fruitName: String
init(fruitsPackId: Int, fruitName: String) {
self.fruitName = fruitName
super.init(fruitsPackId: fruitsPackId)
}
}
Retrieving data from a database is a relatively computationally expensive process, so I'm not personally a fan of building that into a computed property. What if you innocently had some code that did the following:
for index in 0 ..< fruitPack.fruitsPackContent.count {
print(fruitPack.fruitsPackContent[index])
}
If you had n items in the database, this code might be repeatedly retrieving the full list of all items from the database n + 1 times (once for count and again for each subscripted access). You could, theoretically, remedy this by making sure that the computed property cached the results (and you'd have to build some code that would identify when the database was updated and invalidate the cache).
It would be more prudent to make the retrieval from the database an explicit method so that the app developer knows when they're retrieving data from the database, and when they're accessing a cached array. You generally want to avoid some significant hidden performance impact resulting from innocently accessing some property.
Realm demo database has sample objects with inverse connection: objectReference has a link to RealmTestClass1.
class RealmTestClass1: Object {
dynamic var integerValue = 0
let arrayReference = List<RealmTestClass0>()
}
class RealmTestClass2: Object {
dynamic var boolValue = false
dynamic var objectReference: RealmTestClass1?
}
How to re-create that same structure? What code is needed for objectReference property to create that same link to parent object property?
Links in Realm are unidirectional. So if a to-many property Person.dogs links to a Dog instance and a to-one property Dog.owner links to Person, these links are independent from one another. Appending a Dog to a Person instance’s dogs property, doesn’t automatically set the dog’s owner property to this Person. Because manually synchronizing pairs of relationships is error prone, complex and duplicates information, Realm exposes an API to retrieve backlinks described below.
With inverse relationships, you can obtain all objects linking to a given object through a specific property. For example, calling Object.linkingObjects(_:forProperty:) on a Dog instance will return all objects of the specified class linking to the calling instance with the specified property. It’s possible to simplify this pattern by defining a read-only (computed) owners property on Dog:
class Person: Object {
// ... other property declarations
let dogs = List<Dog>()
}
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
var owners: [Person] {
// Realm doesn't persist this property because it only has a getter defined
// Define "owners" as the inverse relationship to Person.dogs
return linkingObjects(Person.self, forProperty: "dogs")
}
}
This was taken from Realm's docs on relationships, which I encourage you to read.