I am developing an App using Realm. At some point in my app when I try to manipulate my model, my app crashed in an unexpected way. Here is what the stack trace said
Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first
What I am trying to do :
lets break down my problem in parts.following is my model of app
#objcMembers public class ClassGroup : Object , Codable {
dynamic var Id : Int? = ""
dynamic var ClassName : String? = ""
dynamic var TeacherId : Int = 0
dynamic var Teachers : [TeacherMdoel]? = []
}
#objcMembers public class TeacherModel : Object , Codable {
dynamic var Id : String? = ""
dynamic var Name : String? = ""
dynamic var ClassId : Int = 0
dynamic var Students : [StudentClass]? = []
}
#objcMembers public class StudentModel : Object , Codable {
dynamic var Id : String? = ""
dynamic var Name : String? = ""
dynamic var ClassId : Int = 0
dynamic var TeacherId : Int = 0
}
now I am trying to get the list of all classes like this from realm (after saving them to realm )
let mClassLists = mDbHelper.realmObj.objects(ClassGroup.self)
Now here I get exception/error. What I am doing is, I am trying to populate my UITableView with some data that consist of all of the above models. I am fetching data and saving them in my model and trying to supply that list to UITableView but my app crash with the error I mentioned above
let mClassLists = mDbHelper.realmObj.objects(ClassGroup.self)
let classLists = Array (mClassLists)
for classModel in classLists {
let resultPredicateTeachers = NSPredicate(format: "ClassId == %#", classModel.Id)
let mTeachersList = mDbHelper.realmObj.objects(TeacherModel.self).filter(resultPredicateTeachers)
if(mTeachersList.count > 0){
var listTeachers : [TeacherModel] = []
for teacherModel in mTeachersList {
let resultPredicateStudent = NSPredicate(format: "TeacherId == 29")
let mStudentList = mDbHelper.realmObj.objects(StudentModel.self).filter(resultPredicateStudent)
if(mStudentList.count > 0){
let studentsList = Array(mStudentList)
teacherModel.Students = studentsList[0]
}
listTeachers.append(savedDetailItem)
}
classModel.Teachers? = (listTeachers)
listClassModel.append(classModel)
}
}
**In the Above code you can see that I am gathering data on behalf of Ids and saving the resultant arrays in the model. So I am getting error in the following line
**
teacherModel.Students = studentsList[0]
now I really do not understand why it is happening? I am not saving data in realm, I am saving in my model, still I am getting error.
In Realm database, if you want to modify any model (save new data or update), the operation should be performed in a write transaction:
try! realm.write {
realm.add(<your_model_objects>)
}
Related
I have an array like:-
var arrayData : Array<Dictionary<String, [BottleModel]>> = []
Bottle model :-
class BottleModel: NSObject {
var name : String
var price : Int
var reviews : Int
var category : String
var quantity : String
var id : String
var shopData : ShopModel
}
I want filtered array where price is > 2000
I tried let searchByInts = arrayData.filter({m in m.price < 200})
but getting below error:
Contextual closure
type '(Dictionary) -> Bool' expects 1 argument,
but 0 were used in closure body
How to filter such kind of array based on price
Working code:
let searchByInts = arrayData.filter { $0.values.contains { $0.contains { $0.price > 2000 } } }
By the way please write the following using literals:
var arrayData : [[String : [BottleModel]]] = []
Still no idea if that is what you actually want because your goal is very unclear. You have an array of dictionaries of arrays which actually contain the values you want to filter out. If a BottleModel costs more than 2000 do you want to keep the entire array it is contained in and the dictionary that array is in? You might want to map the entire data into one flat array before or after filtering.
Alternative using flatMap:
let flat = arrayData.flatMap { $0.values.flatMap { $0 } }
let searchByInts2 = flat.filter { $0.price < 200 } // or some other criteria
I have a Profile Data singleton class as follows.
I am trying to store data into an empty array in a dictionary .
After appending data to the array also the count of the array is 0.
class ProfileData{
static let sharedInstance = ProfileData()
var artistProfileDict = [String : Profile]()
var loggedInUserProfile = Profile(artistName: "John Smith", artistDescription: "Admiral of New England, English soldier, explorer, and author.", totalLikes: "174", totalViews: "200", totalFollowing: "100",totalFollowers:"50",imageUrl:"image_singer", feeds:[] )
private init() {
getProfilesDictionary()
}
func getProfilesDictionary()->[String: Profile]{
artistProfileDict["John Smith"] = loggedInUserProfile
return artistProfileDict
}
func add(array: Feed, artistName: String) {
artistProfileDict[artistName]!.feeds.append(array)
}
}
In another view Controller I am trying to add an array to the empty array in the dictionary as follows
let newFeed = Feed(profilePicture: "image",artistName: "New",
videoUrl: "url",videoTitle:"New", videoViews: "160",likes:
"200",shoutouts: "200",comments: [],votes: "50", dateCreated: Date(),
userActivity :"This user liked your video")
ProfileData.sharedInstance.add(array: newFeed,artistName:"John Smith")
After appending the array to the empty array in the dictionary I still get the count of the array as 0.
I am not able to figure out the problem here. Any help will appreciated . Thank you.
Profile class
struct Profile {
var artistName: String
var artistDescription: String
var totalLikes: String
var totalViews: String
var totalFollowing: String
var totalFollowers: String
var imageUrl: String
var feeds : [Feed]
init(artistName: String,artistDescription:String,totalLikes:String,totalViews:String,totalFollowing:String,totalFollowers:String,imageUrl:String, feeds:[Feed]) {
self.artistName = artistName
self.artistDescription = artistDescription
self.totalLikes = totalLikes
self.totalViews = totalViews
self.totalFollowing = totalFollowing
self.totalFollowers = totalFollowers
self.imageUrl = imageUrl
self.feeds = feeds
}
}
It's working fine
ProfileData.sharedInstance.add(array: newFeed,artistName:"John Smith")
ProfileData.sharedInstance.artistProfileDict["John Smith"]?.feeds.count // 1
Probably you are using wrong class ArtistProfileData instead of ProfileData.
Previously, I had only one object that had every value that I needed. I "regrouped" them and made separate objects. I added properties with the type of the new objects to the original object.
How can I assign the old property values to the object's properties?
Here's the code for my objects:
class MainObject: Object {
dynamic var id: Int = 0
// Schema 0
dynamic var otherId: Int = 0
dynamic var otherStr: String = ""
dynamic var anotherId: Int = 0
dynamic var anotherD: Double = 0.0
dynamic var anotherText: String = ""
// Schema 1
dynamic var otherObjectVar: OtherObject?
dynamic var anotherObjectVar: AnotherObject?
}
// Schema 1
class OtherObject: Object {
dynamic var id: Int = 0
dynamic var str: String = 0
}
class AnotherObject: Object {
dynamic var id: Int = 0
dynamic var d: Double = 0.0
dynamic var text: String = ""
}
(Changed variable names)
I tried to use convenience init(){} but it didn't work. I also tried to assign an object instance to the newObject, but that didn't work either.
Here's that code for easier understanding:
let other = OtherObject()
other.id = 0
other.str = oldObject["otherStr"] as! string
newObject["otherObjectVar"] = other
How can I migrate the old properties into a new property which is another object?
EDIT: Temporarily, I solved it with
let obj = migration.create(MainObject.className())
migration.delete(obj)
but I don't think this is the right solution. So if anyone has a solution for this, I'd appreciate it.
Assuming you're doing this during schema migration, you need to use migration.create to create new objects, not their init. Then you would set them on the new object, along the lines of:
let other = migration.create(OtherObject.className())
other["id"] = 0
other["str"] = oldObject["otherStr"] as! String
newObject?["otherObjectVar"] = other
I have a struct EventDate and am trying to update a reference in Firebase.
struct EventDate {
var club: String = ""
var date: String = ""
var eventText: String = ""
var free: String = ""
var monthYear: String = ""
}
My update function throws lldb. I guess because the keys are no Strings(?)
func updateEvent(_ backendlessUserObjectID: String, event: EventDate) {
let reference = firebase.child("eventDates").child(backendlessUserObjectID).child(event.date)
reference.setValue(event) { (error, ref) -> Void in
if error != nil {
print("\(error)")
}
} // lldb here
}
If I change the function to the following, everything is fine (because Keys and Values are now Strings?)
func updateEvent(_ backendlessUserObjectID: String, event: EventDate) {
let item: NSMutableDictionary = ["club" : event.club,
"date" : event.date,
"eventText" : event.eventText,
"free" : event.free,
"monthYear" : event.monthYear]
let reference = firebase.child("eventDates").child(backendlessUserObjectID).child(event.date)
reference.setValue(item) { (error, ref) -> Void in
if error != nil {
print("\(error)")
}
}
}
Am I right that I receive lldb because the keys from my models are not Strings? Or what am I missing and how will I be able to save the values into my Firebase using my model without creating the NSMutableDictionary? Help is very appreciated.
PS: print(event.date) = 201610120200000000 -> the desired value for .child
Firebase data exists in a JSON format which can be thought of as key:value pairs. The keys must be strings and the values can be any of the for data types mentioned in Dravidians answer (which is a correct answer and should be accepted). I would like to add some additional comments that may help as well.
There are times when you want to use a structure in code and that can't be written to Firebase directly - you need some way to get the data out of the structure into something Firebase likes - which is a Dictionary.
Heres an example (Swift 3, Firebase 2.x)
struct EventDate {
var club: String = ""
var date: String = ""
var eventText: String = ""
var free: String = ""
var monthYear: String = ""
func getDict() -> [String:String] {
let dict = ["club": self.club,
"date": self.date,
"eventText": self.eventText,
"free": self.free,
"monthYear": self.monthYear
]
return dict
}
}
var event = EventDate()
event.club = "Wimbledon"
event.date = "20161023"
event.eventText = "Special Tennis Event"
event.free = "Free"
event.monthYear = "10/2016"
let ref = self.myRootRef.child(byAppendingPath: "events")!
let eventRef = ref.childByAutoId() //Firebase 2.x
eventRef?.setValue( event.getDict() )
This results in a node being written to Firebase that looks like this
"events" : {
"-KUli8oiM_KKw8GZ0MMm" : {
"club" : "Wimbeldon",
"date" : "20161023",
"eventText" : "Special Tennis Event",
"free" : "Free",
"monthYear" : "10/2016"
}
}
No it has nothing to do with the type of keys that you are trying to save in your Firebase Database its just that struct is a dataModel or to be precise a physically grouped list of variables which you initialise with some custom Data, and you can only save four types of values types in your Firebase Database:-
NSDictionary
NSArray
NSNumber
NSString
Look up the docs :- Read And Write data, Firebase- iOS
So when you cast your values in a NSMutableDictionary, you come clean of struct. And struct and class is not recognisable by the Firebase Database.
I have 2 Realm models that should be connected to each other but I have difficulty understanding how to do this when writing to a model.
The 1st model will always be populated before the 2nd. This model contains a course name and some data related to the course:
class CourseModel: Object {
dynamic var coursename = ""
dynamic var par3field = 0
dynamic var par4field = 0
dynamic var par5field = 0
}
The second model has all the scoring data related to the course:
class ScoresModel: Object {
dynamic var dateplayed = ""
}
My question is this - how do i write the ScoresModel data and link this data to the appropriate CourseModel's course name and it's associated data?
ADDITIONAL AFTER ANSWER FROM bcamur:
bcamur, thank you for the feedback. This looks like what I need. However, when writing the ScoresModel to Realm, what do i need to do to let the Realm know to which course name to link the ScoresModel data I just wrote?
I'm currently writing the 2 models like this:
let mycourse = CourseModel()
mycourse.coursename = courseName
mycourse.par3field = par3s
mycourse.par4field = par4s
mycourse.par5field = par5s
CoursesData.addNewCourse(my course)
let dataToSave = ScoresModel()
dataToSave.dateplayed = dateTextField.text!
ScoresData.saveNewScores(dataToSave)
Thank you in advance.
You can save the ScoresModel objects as a list in your CourseModel:
class CourseModel: Object {
dynamic var coursename = ""
dynamic var par3field = 0
dynamic var par4field = 0
dynamic var par5field = 0
let scoreModels: List<ScoresModel> = List<ScoresModel>()
}
class ScoresModel: Object {
dynamic var dateplayed = ""
//include this if you want to be able to see which course has this ScoresModel in its scoresModels list
var courseModel: CourseModel? {
return linkingObjects(CourseModel.self, forProperty: "scoreModels").first
}
}