Primary key in realm database - ios

I create a default realm file to use it during first launching app as replacement (I want to have a file with initial data). I start with creating default realm file from csv files. The problem is that I am not sure if my structure is correct. When I import data from csv (in Realm Browser) and try to import next data for next class, I get this error
I have main class called Exercises
class Exercises: Object {
#Persisted var id: Int = 0
#Persisted var name: String = ""
#Persisted var category: Category?
#Persisted var equipment: Equipment?
#Persisted var instruction: String
#Persisted var muscle: List<Muscle>
#Persisted var gif: String?
#Persisted var image: String?
convenience init(id: Int, name: String, category: Category?, equipment: Equipment?, instruction: String, muscle: [Muscle], gif: String?, image: String?) {
self.init()
self.id = id
self.name = name
self.category = category
self.equipment = equipment
self.instruction = instruction
self.muscle.append(objectsIn: muscle)
self.gif = gif
self.image = image
}
}
and other classes for separate things
class Equipment: Object {
#Persisted(primaryKey: true) var equipmentID = 0
#Persisted var equipment: String = ""
convenience init(equipment: String) {
self.init()
self.equipment = equipment
}
}
class Category: Object {
#Persisted(primaryKey: true) var categoryID = 0
#Persisted var category: String = ""
convenience init(category: String) {
self.init()
self.category = category
}
}
class Muscle: Object {
#Persisted(primaryKey: true) var muscleID = 0
#Persisted var muscle: String = ""
convenience init(muscle: String) {
self.init()
self.muscle = muscle
}
}
Finally I want to receive structure like below. I wonder if it is correct way? Maybe better option is just set text in fields instead of reference to class (after all realm is non-relational)?

Related

Swift Realm - Iterate an array of items to update their relationships

I have a basic many-to-many relation with a relation table on my Realm Schema.
So I have Recipe, Ingredient and RecipeIngredient model.
When I try to create a Recipe, I have to iterate an array of Ingredient to create RecipeIngredient. Once the recipeIngredient is create I want to edit my Recipe and my Ingredient to add a relation to the new RecipeIngredient. But the edition of the Ingredient throws an exception, and I have no idea why...
Here some code
Recipe Model
class RecipeRealm: Object, ObjectKeyIdentifiable {
#Persisted(primaryKey: true) var _id: ObjectId
#Persisted var name: String = ""
#Persisted var lastEditedTime: Date = Date()
#Persisted var recipeIngredients: List<RecipeIngredient>
convenience init(name: String){
self.init()
self.name = name
}
}
Ingredient Model
class IngredientRealm: Object, ObjectKeyIdentifiable {
#Persisted(primaryKey: true) var _id: ObjectId
#Persisted var name: String = ""
#Persisted var unit: Unit = Unit.none
#Persisted var lastEditedTime: Date = Date()
#Persisted var recipeIngredients: List<RecipeIngredient>
convenience init(name: String){
self.init()
self.name = name
}
}
RecipeIngredient Model
class RecipeIngredient: Object, ObjectKeyIdentifiable {
#Persisted(primaryKey: true) var _id: ObjectId
#Persisted var qty: Int = 0
#Persisted var lasEditedTime: Date = Date()
#Persisted(originProperty: "recipeIngredients") var recipe: LinkingObjects<RecipeRealm>
#Persisted(originProperty: "recipeIngredients") var ingredient: LinkingObjects<IngredientRealm>
}
The recipe form model
struct QuantifiedIngredient {
var ingredient: IngredientRealm
var qty: Int16
}
class RecipeForm: ObservableObject {
#Published var name = ""
#Published var ingredients: [QuantifiedIngredient] = []
var recipeID: String?
var updating: Bool {
recipeID != nil
}
init(){}
init(_ recipe: Recipe){
name = recipe.name ?? ""
ingredients = []
recipeID = recipe.id
}
}
The create Recipe logic
private func createRecipe() {
withAnimation {
do {
let realm = try Realm()
// Create Recipe on RealmDB
let realmRecipe = RecipeRealm(name: form.name)
try realm.write{
realm.add(realmRecipe)
for ingredient in form.ingredients{
let recipeIngredient = RecipeIngredient()
recipeIngredient.qty = Int(ingredient.qty)
realm.add(recipeIngredient)
realmRecipe.recipeIngredients.append(recipeIngredient)
ingredient.ingredient.recipeIngredients.append(recipeIngredient) // <--- this line crash
}
}
} catch {
print(error.localizedDescription)
}
presentationMode.wrappedValue.dismiss()
}
}
An exception is throw by this line on createRecipe method: ingredient.ingredient.recipeIngredients.append(recipeIngredient).
Here the error message: Terminating app due to uncaught exception 'RLMException', reason: 'Cannot modify managed RLMArray outside of a write transaction.'
But I have the write block...
Any ideas ?
It seems that when I tried to add recipeIngredient on the ingredient, I actually tried to put a realm object on a copy of the realm ingredient. So is obviously not working, but the error message is not very clear.
To fix it I used the thaw()function that returns me the good realm object, so I can edit it.
try realm.write{
realm.add(realmRecipe)
for ingredient in form.ingredients{
let recipeIngredient = RecipeIngredient()
recipeIngredient.qty = Int(ingredient.qty)
realm.add(recipeIngredient)
realmRecipe.recipeIngredients.append(recipeIngredient)
guard let rIngredient = ingredient.ingredient.thaw() else {
return
}
rIngredient.recipeIngredients.append(recipeIngredient)
}
}
I don't know if this is the best way to do it, so if someone had a better option I'm listening :)

Property 'self.readCount' not initialized at super.init call

I have the following class
// LearningItem
class LearningItem : NSObject {
var id: String
var title: String
var subtitle: String?
var image: String
var uploadDate: Int
init(id: String, title: String, image: String, uploadDate: Int) {
self.id = id
self.title = title
self.image = image
self.uploadDate = uploadDate
}
I have another class
// Book.swift
class Book: LearningItem {
var publishDate: String?
var author: String?
var mediaUrl: String?
var video : String?
var tags: [String]?
var lists: [String: AnyObject]?
var readCount: Int
var categories: [String]?
var courses: [String]?
var amazonBuyUrl: String?
var starCount: Int
var read: Bool?
var completed = [String : Int]()
var stars = [String : Int]()
var completedDate : Int?
var desc: String
var epub: String
var authorDesc: String
init(id: String, title: String, desc: String, authorDesc: String, image: String, epub: String, readCount: Int, uploadDate: Int, starCount: Int) {
super.init(id: id, title: title, image: image, uploadDate: uploadDate)
self.id = id
self.desc = desc
self.authorDesc = authorDesc
self.title = title
self.epub = epub
self.image = image
self.readCount = readCount
self.uploadDate = uploadDate
self.starCount = starCount
}
I get the error "Property 'self.readCount' not initialized at super.init call"
where I call "super.init(id: id, title: title, image: image, uploadDate: uploadDate)" in Book.swift
Class initialisation not finished until it's designated initializers
not finished with initializing all properties
and after that you can call super class's designated initializers
Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.
SO
Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.
apple docs https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
so
class Book: LearningItem {
var publishDate: String?
var author: String?
var mediaUrl: String?
var video : String?
var tags: [String]?
var lists: [String: AnyObject]?
var readCount: Int
var categories: [String]?
var courses: [String]?
var amazonBuyUrl: String?
var starCount: Int
var read: Bool?
var completed = [String : Int]()
var stars = [String : Int]()
var completedDate : Int?
var desc: String
var epub: String
var authorDesc: String
init(id: String, title: String, desc: String, authorDesc: String, image: String, epub: String, readCount: Int, uploadDate: Int, starCount: Int) {
self.readCount = readCount
self.starCount = starCount
self.desc = desc
self.epub = epub
self.authorDesc = authorDesc
super.init(id: id, title: title, image: image, uploadDate: uploadDate)
}
}

How to filter other parent of child with Vapor?

I have this request:
router.get("/fetchOngoingReleases") { (request) -> Future<[ReleaseOut]> in
return Release.query(on: request).filter(\.inprogress == true).all().map { releases in
var result: [ReleaseOut] = []
for r in releases {
var pageEvents: [Event] = []
let num = r.releaseUsers.query(on: request).filter(\.user.fbId ~~ "something").count()
var needAuthentication: Bool
if num == 0 {
needAuthentication = true
} else {
needAuthentication = false
}
let rOut = ReleaseOut(fbId: r.fbId, name: r.name, purpose: r.purpose, needAuthentication: needAuthentication)
result.append(rOut)
}
return result
}
}
}
It says I can not access (???) releaseUser.user.fbId in the query?
Here the data model:
and in code
final class Release: Content {
var id: Int?
var fbId: String
var inprogress: Bool?
var name: String
var purpose: String
/// Creates a new `Release`.
init(id: Int? = nil, fbId: String, name: String, purpose: String = "normal selling") {
self.id = id
self.fbId = fbId
self.name = name
self.purpose = purpose
}
}
extension Release {
var releaseUsers: Children<Release, ReleaseUser> {
return children(\.releaseId)
}
}
final class ReleaseUser: Content {
var id: Int?
var releaseId: Release.ID
var userId: User.ID
init(id: Int? = nil, releaseId: Release.ID, userId: User.ID) {
self.id = id
self.releaseId = releaseId
self.userId = userId
}
}
extension ReleaseUser {
var user: Parent<ReleaseUser, User> {
return parent(\.userId)
}
}
final class User: Content {
var id: Int?
var fbId: String
var name: String
init(id: Int? = nil, fbId: String, name: String) {
self.id = id
self.fbId = fbId
self.name = name
}
}
Ok so there are several things going on here, but the main concept is that you can't just jump across different tables like that - you need to use a JOIN to join the ReleaseUser table to the User table so you can then query on the fbId
Try changing your query to:
Release.query(on: request).filter(\.inprogress == true).join(\ReleaseUser.releaseId, to:\Release.Id).join(\ReleaseUser.userId, to:\User.Id).alsoDecode(User.self).all()
The alsoDecode will give you a tuple with the first position containing your original Release instance and the second containing the corresponding User instance. So, fbId should be available as:
r.1.fbId
In your case.

Swift Inheritance with custom array

class RecipeBrain: NSObject {
var name: String
var pictureUrl: String
var likes = 0
var ingredients: [Ingredient]
var method: [String]
init(name: String, pictureUrl: String, ingredients: [Ingredient], method: [String]) {
self.name = name
self.pictureUrl = pictureUrl
self.ingredients = ingredients
self.method = method
}
}
class Ingredient{
var name: String
var quantity: Double
var unit: String
init(name: String, quantity: Double, unit: String) {
self.name = name
self.quantity = quantity
self.unit = unit
}
}
class AddRecipe {
var recipeBrain = [RecipeBrain]()
var ingredients = [Ingredient]()
var ingredient = Ingredient(name: "apple", quantity: 1.0, unit: "Kg")
ingredients.append(ingredient)
var recipe1 = RecipeBrain(name: "Recipe1", pictureUrl: "nil", ingrexdients: ingredients, method: ["Method"])
recipeBrain.append(recipe1)
}
I am trying to build a recipe app in Swift. Problem is creating the ingredients for it where I require a string,double,string.
How I imagined : an ingredient is an array of Ingredients. and to create a new recipe I just .append it to the recipeBrain
Main problem : when I try to append a new recipe to the recipeBrain array It says that the recipe1 is not declared.
( The AddRecipe class purpose is only testing with static data )
I changed it to recipeBrain.append(recipe1) But I still get the error : expected declaration , same with ingredients when I try to append
You are trying to append recipe and ingredient objects into their arrays in body of AddRecipe class, but you can't.
if you do this in body of a method everything will be ok, for example in init() method:
class AddRecipe {
var recipeBrain = [RecipeBrain]()
var ingredients = [Ingredient]()
init()
{
var ingredient = Ingredient(name: "apple", quantity: 1.0, unit: "Kg")
ingredients.append(ingredient)
var recipe1 = RecipeBrain(name: "Recipe1", pictureUrl: "nil", ingredients: ingredients, method: ["Method"])
recipeBrain.append(recipe1)
}
You are getting the error message because you have tried to write code in a class declaration. You can only declare items such as properties, functions, enumerations and other classes directly inside a class declaration. Code, such as ingredients.append(ingredient) needs to go inside a function.
I would suggest that you move this code into a class function of your RecipeBrain class (or indeed put it somewhere else entirely, such as a view controller, but you haven't shown how your app is structured):
class RecipeBrain: NSObject {
var name: String
var pictureUrl: String
var likes = 0
var ingredients: [Ingredient]
var method: [String]
init(name: String, pictureUrl: String, ingredients: [Ingredient], method: [String]) {
self.name = name
self.pictureUrl = pictureUrl
self.ingredients = ingredients
self.method = method
}
class func addRecipe() -> [RecipeBrain] {
var recipeBrain = [RecipeBrain]()
var ingredients = [Ingredient]()
let ingredient = Ingredient(name: "apple", quantity: 1.0, unit: "Kg")
ingredients.append(ingredient)
let recipe1 = RecipeBrain(name: "Recipe1", pictureUrl: "nil", ingredients: ingredients, method: ["Method"])
recipeBrain.append(recipe1)
return recipeBrain
}
}
Now you can say let recipeBrain = RecipeBrain.addRecipe() and recipeBrain will be an array of RecipeBrain containing a single RecipeBrain

Usage NSManagedObject model in code

I have a User model class (generated by XCode with Swift):
#objc(User)
class User: NSManagedObject { }
And it's extension:
extension User {
#NSManaged var id: NSNumber?
#NSManaged var firstName: String?
#NSManaged var lastName: String?
#NSManaged var birthYear: NSNumber?
}
I can save/fetch data from CoreData.
But can I use this class for object management without CoreData things? Or i need to create other class/struct for this?
For example, create User object (without ObjectContext), set his attributes and send it as property in some func? Maybe i can create some struct in class User (like struct {var firstNameData, secondNameData,...}) and use it in code?
I updated class:
struct User {
var id: Int!
var firstName: String!
var lastName: String!
var birthYear: UInt?
}
#objc(UserManagedObject)
class UserManagedObject: NSManagedObject {
func toStruct() -> User {
var userData = User()
userData.id = Int(self.id)
userData.firstName = self.firstName
userData.lastName = self.lastName
if let by = self.birthYear {
userData.birthYear = UInt(by)
}
return userData
}
}
Now for object management i use struct User and UserManagedObject for CoreData in/out

Resources