Realm multiple database problems - swift

There are two databases locally, 'user.realm' and 'person.realm'. 'User' model need to be saved in 'user.realm'. 'Person' model need to saved in 'person.realm' , When the following code is used, the 'User' and 'Person' tables is created in 'user.realm'. I need 'User' model only saved to 'user.realm'.
class User:Object {
#objc dynamic var id:Int = 0
#objc dynamic var name:String = ""
}
class Person:Object {
#objc dynamic var id:Int = 0
objc dynamic var name:String = ""
}
//create "user.realm"
var config = Realm.Configuration()
config.fileURL = dataPath.appendingPathComponent("user.realm")
Realm.Configuration.defaultConfiguration = config
var realm = try! Realm()
//create "person.realm"
var config = Realm.Configuration()
config.fileURL = dataPath.appendingPathComponent("person.realm")
Realm.Configuration.defaultConfiguration = config
var realm = try! Realm()
When I execute a user database, I only need to create the "User" table in user.realm, instead of "User" and "Person" two tables are created together in "user.realm", I can't remove the Person model, because then you cannot create the "Person" table in "Person.realm". Personally, it seems that Realm is more troublesome to implement.

One of the key points to know about Realm is that it is not a relational database. While it has that 'feel' which is intentional, there are no tables as such and Realm structures are often split amongst different realms. Check out Realm vs Other Databases.
In some cases it's more efficient to store like data within the same Realm - a User and a Person look very similar so it may be better to store them within the same Realm - I just don't know the use case here.
To answer the question though, you can specifically tell Realm what object or objects to store within a realm. It's done through the config like this
let config = Realm.Configuration(objectTypes: [MyClass.self, MyOtherClass.self])
let realm = try! Realm(configuration: config)
That realm will only have MyClass and MyOtherClass in it. Applying that to your question
let config = Realm.Configuration(objectTypes: [UserClass.self])
let myUserRealm = try! Realm(configuration: config)
Will only have one object type in it, a UserClass.
That's covered in the Class Subsets section of the Realm Documentation Provide a subset of classes to a Realm

Related

Prepopulate Realm database with many to many relationships?

I'm brand new to this, so forgive me if I'm missing something obvious or not asking the right question. I plan to make an app that have a few sets of data that require many-to-many relationships. For example, if I have a model for Food items and a model for CookingMethods to cook that food item. So, each Food can have multiple CookingMethods, and each CookingMethod applies to multiple types of Food.
I think this is the right way to set up the realm data:
class Food: Object {
#objc dynamic var title: String = ""
#objc dynamic var id: Int = 0
var cookingMethods = List<CookingMethod>()
}
class CookingMethod: Object {
#objc dynamic var id: Int = 0
#objc dynamic var title: String = ""
let foods = LinkingObjects(fromType: Food.self, property: "cookingMethods")
}
Is it now possible to import a set of data (probably a csv file?) using either Realm Studio or programmatically that allows me to link this relationship? The Food would have a list of CookingMethods, and the CookingMethods would link back to multiple different Foods?
If I'm going about this all wrong please let me know, there is a spreadsheet of data that I'd like to add to my app's database as a one time thing.

How can I let the user add an attachment to my app like a pdf or jpg and then how can I store it in my Realm Database?

Here is my code for my object that I am storing in Realm Database
class Accomp2: Object {
#objc dynamic var title: String = ""
#objc dynamic var date: Date!
#objc dynamic var month:String = ""
required init() {
}
#objc dynamic var body:String = ""
#objc dynamic var type:String = "Professional"
#objc dynamic var identifier:String = ""
}
I want to be able to store the attachment in a variable called attachment, but I am not sure what type of variable that is.
With the object, I want to be able to show this attachment on a view controller as a picture.
If this is not possible with Realm Database, is there another way to store attachments?
See the Realm property Cheat Sheet
What you want is the NSData() e.g. Swift Data() object - he's some simple code as a conceptual example.
let url = //your file
let myData = Data(contentsOf: url) //please handle the optional correctly
class Accomp2: Object {
#objc dynamic var acc_id = UUID().uuidString
#objc dynamic var name = ""
#objc dynamic var value = Data()
}
let x = Accomp2()
x.name = "Some name"
x.value = myData
IMPORTANT!
While you can do this, you probably shouldn't.
Realm is not well suited for handling documents within objects. If it's a thumbnail or a couple hundred kb, it will be ok. But PDF's can get HUGE and Realm is not the right solution.
You should store files in another source that's designed for storing files - Firebase Cloud Storage is one such option and then store the URL to the file within your Realm object.

Where's the best place to call methods that interact with my database?

I'm creating an app that interacts with a Firestore database. As of now I have a singleton class, DatabaseManager that has all the methods relating to the Firestore database (i.e. get/post methods).
I have a User model called User that has properties such as name, email, photoURL, and some app-specific properties. Any user can edit their profile to update information from a view controller called EditProfileViewController.
Now my question is: is it best to call the DatabaseManager.shared.updateInfo(forUser: user) (where user is a User instance) from EditProfileViewController, User, or some other place?
Sorry if this is an obvious question, but there's going to be a lot of points in the app where I'll need similar logic so I wanted to know what's the best design. Also I'm sure this question has more to with MVC than it does Firebase/Swift.
A couple of thoughts:
Rather than accessing the singleton directly with, DatabaseManager.shared.update(for:), I might instead have a property for the database manager, initialize/inject it with the DatabaseManager.shared, and have whatever needs to interact with the database use that reference, e.g., dataManager.update(for:). The goal would be to allow your unit tests to mock a database manager if and when necessary.
I would not be inclined to have a view controller interact directly with the DatabaseManager. Many of us consider the view controller, which interacts directly with UIKit/AppKit objects, as part of the broader “V” of MVC/MVP/MVVM/whatever. We’d often extricate business logic (including interaction with the database manager) out of the view controller.
I personally wouldn’t bury it under the User object, either. I’d put it in an extension of the database manager, and called from the view model, the presenter, or whatever you personally want to call that object with the business logic.
Is there a reason you're using a singleton to contain all the Firestore logic? User model should contain the method updateInfo.
Here's an example i've used with Firestore:
class Group {
// can read the var anywhere, but an only set value in this class
private(set) var groupName: String!
private(set) var guestsInGroup: Int!
private(set) var joinedGroup: Bool!
private(set) var timeStampGroupCreated: Date!
private(set) var documentId: String!
init(groupName: String, guestsInGroup: Int, joinedGroup: Bool, timeStampGroupCreated: Date, documentId: String) {
self.groupName = groupName
self.guestsInGroup = guestsInGroup
self.joinedGroup = joinedGroup
self.timeStampGroupCreated = timeStampGroupCreated
self.documentId = documentId
}
// method to parse Firestore data to array, that table view will display
class func parseData(snapshot: QuerySnapshot?) -> [Group]{
var groups = [Group]()
guard let snap = snapshot else { return groups }
for document in snap.documents {
let data = document.data()
let groupName = data[GROUP_NAME] as? String ?? "No Group Name"
let guestsInGroup = data[GUESTS_IN_GROUP] as? Int ?? 0
let joinedGroup = data[JOINED_GROUP] as? Bool ?? false
let timeStampGroupCreated = data[TIMESTAMP_GROUP_CREATED] as? Date ?? Date()
let documentId = document.documentID
// add objects with fetched data into thoughts array
let newGroup = Group(groupName: groupName, guestsInGroup: guestsInGroup, joinedGroup: joinedGroup, timeStampGroupCreated: timeStampGroupCreated, documentId: documentId)
groups.append(newGroup)
}
return groups
}
}

How to reduce mutability with nested objects stored in Realm?

Full code on github
I am trying to rewrite my app to reduce mutability and take advantage of functional programming. I am having trouble figuring out where to start, since it seems like my architecture is to use modification in place almost everywhere. I could use some advice on a simple starting point of how to break this down into smaller pieces where I am maintaining immutability at each modification. Should I change my data storage architecture so that I am only storing/modifying/deleting the leaf objects?
Right now, from the root ViewController, I load my one monster object ExerciseProgram (which contains a RealmList of Exercise objects, which contains a RealmList of Workouts, which contains a RealmList of Sets....)
final class ExerciseProgram: Object {
dynamic var name: String = ""
dynamic var startDate = NSDate()
dynamic var userProfile: User?
var program = List<Exercise>()
var count: Int {
return program.count
}
}
Loaded here one time in MasterTableViewController.swift:
func load() -> ExerciseProgram? {
let realm = try! Realm()
return realm.objects(ExerciseProgram).first
}
and then modify the single ExerciseProgram object in place throughout the app, such as when recording a new workout.
To create a new Workout, I instantiate a new Workout object in RecordWorkoutTableViewController.swift:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if doneButton === sender {
if let date = newDate, weight = newWeight, setOne = newSetOne, setTwo = newSetTwo {
let newSets = List<WorkSet>()
newSets.append(WorkSet(weight: weight, repCount: setOne))
newSets.append(WorkSet(weight: weight, repCount: setTwo))
newWorkout = Workout(date: date, sets: newSets)
}
}
}
Which unwinds to ExerciseDetailTableViewController.swift where the storage occurs into the same monster ExerciseProgram object retrieved at the beginning:
#IBAction func unwindToExerciseDetail(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? RecordWorkoutTableViewController, newWorkout = sourceViewController.newWorkout {
let realm = try! Realm()
try! realm.write {
exercise.recordWorkout(newWorkout)
}
}
}
This behavior is replicated all over my app. If I want to edit or delete an existing workout, it's exactly the same.
The Exercise class is just this:
final class Exercise: Object {
dynamic var name = ""
dynamic var notes: String?
var workoutDiary = List<Workout>()
dynamic var goal = 0
...
func recordWorkout(newWorkout: Workout) {
workoutDiary.append(newWorkout)
}
func replaceWorkout(originalWorkout: Workout, newWorkout: Workout) {
workoutDiary[workoutDiary.indexOf(originalWorkout)!] = newWorkout
}
}
From what I can tell, looking at that schema, no, you shouldn't change it. If it's representing the types of information and their relations properly and it's already working in your app, then there's no need to change it.
If you feel it is overly complex or confusing, then it may be necessary to go back and look at your data model design itself before actually doing more work on the code itself. Review each relationship and each property in the linked objects, and make sure that it's absolutely critical that the data is saved at that level. In any case, Realm itself is very good at handling relationships between objects, so it's not 'wrong' to have several layers of nested objects.
Either way, Realm itself lends itself pretty well to functional programming since every property is explicitly immutable out of the box. Functional programming doesn't mean everything has to be immutable always though. Inevitably, you'll have to reach a point where you'll need to save changes to Realm; the mindset behind it is that you're not transforming data as you're working on it, and you minimise the number of points that actually do so.

How do I add attribute validation to Realm models?

In a swift IOS app I have a Realm Model like this:
import Foundation
import RealmSwift
class Item: Object {
dynamic var name = ""
let prices = List<Price>()
}
This lets me save an instance of Item with an empty String as a name:
let newItem = Item()
newItem.name = "" //or not set the attribute at all
let realm = Realm()
realm.write {
realm.add(newItem)
}
How can I setup simple (and more complex) attribute validation?
The name String is supplied from an UITextField. Do I do validate the user input in the ViewController responsible here, or is there a way to do this on the Realm Model, like the rails developer in me wants to do?
Thanks in advance.
Cheers,
nc
Realm currently does not support validation beyond uniqueness for primary keys, but you can follow https://github.com/realm/realm-cocoa/issues/1769 for further updates!