Realm Object fields are nil while unwrapping an Optional value - ios

I'm having a strange issue with objects in Realm. When i fetch an object from Realm database at first time & deleted object from database. unwrapping an optional value nil.
Model:
class Outlet: Object {
#objc dynamic var id:string = ""
#objc dynamic var name:string = ""
#objc dynamic var name1:Double = 0.0
#objc dynamic var name2:Double = 0.0
#objc dynamic var name3:Double = 0.0
#objc dynamic var name4:Double = 0.0
#objc dynamic var name5:Int = 0
let OutletListS = List<OutletList>()
override class func primaryKey() -> String? {
return "id"
}
}
Class:
class DBManager {
private var database: Realm
static let sharedInstance = DBManager()
private init() {
database = try! Realm()
}
func getDataFromDB() -> Outlet {
let result = database.objects(Outlet.self)
return result.first!
}
}
and calling method:
let selectedOutlet = DBManager.sharedInstance.getDataFromDB()
Error:
Printing description of result:
Results <0x7fb64f02c230> (
)
i could't solve it myself. help me please.

Related

Separate Realm SWIFT data into lists -- and bring data back together?

So, I am a COMPLETE newbie, learning by doing real hands-on projects. I am attempting to create an app that can keep track of car parts. I'm trying to use Realm Database. In order to be organized, I'd like to keep everything separated by key bunches: "partInfo", "storeInfo", and "mechanicInfo" and then bring this back together again with the list feature of realm. Please see my code below and tell help me learn how to go about it? Thanks in advance!
I'm also trying to do MVC pattern--not sure if I did this right by separating everything out or not.
Here's my code for storeinfo.swift
import Foundation
import RealmSwift
class StoreInfo: Object {
#objc dynamic var _id = ObjectId.generate()
#objc dynamic var storeName: String = ""
#objc dynamic var storeNumber: String? = ""
#objc dynamic var storeAddress: String = ""
var catalog = LinkingObjects(fromType: Catalog.self, property: "stores")
//var itemImgs = List<String>()
override static func primaryKey() -> String? {
return "_id"
}
}
Here's my code for partinfo.swift
import Foundation
import RealmSwift
class PartInfo: Object {
#objc dynamic var _id = ObjectId.generate()
#objc dynamic var partName: String = ""
#objc dynamic var partNumber: String = ""
#objc dynamic var partDescription: String? = ""
#objc dynamic var partCost: Double = 0.00
#objc dynamic var partQuantity: Int = 0
#objc dynamic var purchaseDate: String = ""
#objc dynamic var hasWarranty: Bool = true
#objc dynamic var warrantyLength: String? = ""
var catalog = LinkingObjects(fromType: Catalog.self, property: "parts")
//var itemImgs = List<String>()
override static func primaryKey() -> String? {
return "_id"
}
}
here's my code for mechanicinfo.swift
import Foundation
import RealmSwift
class MechanicInfo: Object {
#objc dynamic var _id = ObjectId.generate()
#objc dynamic var mechanicName: String = ""
#objc dynamic var mechanicNumber: String = ""
#objc dynamic var mechanicAddress: String? = ""
#objc dynamic var mechanicCost: Double = 0.00
#objc dynamic var serviceDate: String = ""
var catalog = LinkingObjects(fromType: Catalog.self, property: "mechanics")
//var itemImgs = List<String>()
override static func primaryKey() -> String? {
return "_id"
}
}
and finally (where I'm stuck at) -- the code for Catalog.swift
import Foundation
import RealmSwift
class Catalog: Object {
#objc dynamic var _id = ObjectId.generate()
var parts = RealmSwift.List<PartInfo>()
var stores = RealmSwift.List<StoreInfo>()
var mechanics = RealmSwift.List<MechanicInfo>()
//var itemImgs = List<String>()
override static func primaryKey() -> String? {
return "_id"
}
when I go to try to use it, I'm getting an error of already using the same name. can't use it twice: (so I had to do 3 instances of wipers, which works, but it's not what I wanted...please help!)
My ViewController:
import UIKit
import RealmSwift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let realm = try! Realm()
print(Realm.Configuration.defaultConfiguration.fileURL!)
let wipers = PartInfo()
wipers.partName = "TRICO HD 32 Inch Wiper Blades"
wipers.partNumber = "67-324"
wipers.hasWarranty = true
wipers.warrantyLength = "2 years"
wipers.purchaseDate = "04/21/2021"
wipers.partCost = 34.99
wipers.partQuantity = 2
wipers.partDescription = "Great set. Lifespan of 6 - 9 months."
let wipers2 = StoreInfo()
wipers2.storeName = "O'Reilly AutoParts"
wipers2.storeAddress = "1003 W Broad St, Groveland, FL 34736"
wipers2.storeNumber = "4474"
let wipers3 = MechanicInfo()
wipers3.serviceDate = "04/24/2021"
wipers3.mechanicAddress = "17605 Sunshine Circle, Winter Garden, FL, 34787"
wipers3.mechanicCost = 289.58
wipers3.mechanicName = "Bob Barnicles"
wipers3.mechanicNumber = "353-256-7893"
try! realm.write {
realm.add(wipers)
realm.add(wipers2)
realm.add(wipers3)
}
}
}

Query on Inner and Outer object at same time Realm Swift

I have two objects ConversationModel and MessageModel as follows
class ConversationModel: Object, NSCoding {
#objc dynamic var conversation_id : String?
#objc dynamic var display_name : String?
#objc dynamic var to_jid : String?
#objc dynamic var from_jid : String?
#objc dynamic var unread_count : Int = 0
#objc dynamic var last_timestamp : String?
#objc dynamic var group_name : String?
#objc dynamic var group_id : String?
#objc dynamic var ghost_name : String?
#objc dynamic var ghost_password : String?
#objc dynamic var ghost_message : String?
#objc dynamic var user_profile_image : String?
#objc dynamic var last_message_status : String?
#objc dynamic var m_body : String?
#objc dynamic var sender_timestamp : String?
#objc dynamic var m_type : String?
#objc dynamic var receiver_timestamp : String?
#objc dynamic var userCustomProfileImageUrl: String?
#objc dynamic var profileImageUrl: String?
#objc dynamic var userImageHashKey: String?
#objc dynamic var userImageBase64String : String?
#objc dynamic var sender_jid : String?
#objc dynamic var reciever_jid: String?
#objc dynamic var sending_status: String?
#objc dynamic var is_blocked : Bool = false
#objc dynamic var is_business : Bool = false
#objc dynamic var is_ghost_enable : Bool = false
#objc dynamic var is_deleted : Bool = false
#objc dynamic var is_offline : Bool = false
#objc dynamic var is_archive : Bool = false
#objc dynamic var is_group : Bool = false
#objc dynamic var is_pin : Bool = false
#objc dynamic var is_mute : Bool = false
#objc dynamic var mute_time : String?
#objc dynamic var dataForImage : Data?
#objc dynamic var phoneNumber: String?
#objc dynamic var is_removed: Bool = false
#objc dynamic var audio_playedtime :String?
#objc dynamic var userThumbnailImage : String?
var messagesList = List<MessageModel>()
}
And Message Model is
class MessageModel: Object, NSCoding {
#objc dynamic var burning_msg_timestamp: String?
#objc dynamic var is_burning_msg : Int = 0
#objc dynamic var delivered_timestamp: String?
#objc dynamic var read_timestamp: String?
#objc dynamic var message_id: String?
#objc dynamic var reciever_id: String?
#objc dynamic var conversation_id: String?
#objc dynamic var body: String?
#objc dynamic var url: String?
#objc dynamic var sender_timestamp: CLong = 0
#objc dynamic var receiver_timestamp: String?
#objc dynamic var forward_tag: Int = 0
#objc dynamic var message_type: String?
#objc dynamic var stanza_id: String?
#objc dynamic var selected_message_reply_id: String?
#objc dynamic var burn_time: String?
#objc dynamic var schedule_timestamp: String?
#objc dynamic var is_scheduled: Bool = false
#objc dynamic var room_jid: String?
#objc dynamic var sender_jid: String?
#objc dynamic var is_deleted: Bool = false
}
I would like to fetch all conversation with is_archive == false and only those messageslist with is_deleted = false
I am trying to fetch with this code but its only filtering ConversationModel and not applying filter on Message Model
var result = realm.objects(ConversationModel.self).filter("is_archive = \(is_archive)
AND ANY messagesList.is_deleted == \(boolFalse) AND ANY messagesList.sender_timestamp <=
\(timeStamp)").sorted(byKeyPath: "last_timestamp", ascending: false)
I have tried Nested queries but unable to get desired result.
This isn't exactly an answer but more confirmation that your code is correct and working as intended.
To test, I copy and pasted your code and created sample data and added an _id object to ConversationModel so I could tell which was being retrieved by the query.
So we have three conversations, each having a message. The goal is for the query to retrieve conversation c1; its is_archive is false, it has a message where is_deleted is false and a sender_timestamp <= 5
let c0 = ConversationModel()
c0._id = "c0"
c0.is_archive = true
let c1 = ConversationModel()
c1._id = "c1"
c1.is_archive = false
let c2 = ConversationModel()
c2._id = "c2"
c2.is_archive = false
let m0 = MessageModel()
let m1 = MessageModel()
let m2 = MessageModel()
m0.sender_timestamp = 0
m1.sender_timestamp = 1
m2.sender_timestamp = 2
m0.is_deleted = true
m1.is_deleted = false
m2.is_deleted = true
c0.messagesList.append(m0)
c1.messagesList.append(m1)
c2.messagesList.append(m2)
try! realm.write {
realm.add([c0, c1, c2])
}
Then I ran your query, adding vars.
let is_archive = false
let boolFalse = false
let timeStamp = 5
var results = realm.objects(ConversationModel.self).filter("is_archive = \(is_archive) AND ANY messagesList.is_deleted == \(boolFalse) AND ANY messagesList.sender_timestamp <= \(timeStamp)").sorted(byKeyPath: "last_timestamp", ascending: false)
which produced this output
c1
Unfortunately you can't specify MessageMode constraints in your ConversationModel query. What you can do is create a lazy property consisting of MessageModel objects with constraints on the is_deleted and sender_timestamp properties.
class ConversationModel: Object {
// all other properties ...
var messagesList = List<MessageModel>()
lazy var activeMessages: Results<MessageModel> = {
return messagesList
.filter(.notDeleted)
.filter(.sentOnOrBefore(SOME_TIMESTAMP))
}()
override class func ignoredProperties() -> [String] {
return ["activeMessages"]
}
}
Personally, I like to organize my filter constraints like this:
extension NSPredicate {
static let notArchived = NSPredicate(format: "is_archive == false")
static let notDeleted = NSPredicate(format: "is_deleted == false")
static func sentOnOrBefore(_ timestamp: CLong) -> NSPredicate {
return NSPredicate(format: "sender_timestamp <= %d", timestamp)
}
}
extension SortDescriptor {
static let lastTimestampDescending = SortDescriptor(keyPath: "last_timestamp", ascending: false)
}
To fetch the Conversations we can do this:
func fetchConversations() throws -> Results<ConversationModel> {
let realm = try Realm()
return realm.objects(ConversationModel.self)
.filter(.notArchived)
.sorted(by: [.lastTimestampDescending])
}
Then to access the constrained messages on each conversation we can do something like this:
func processMessages(for conversations: Results<ConversationModel>) {
for conversation in conversations {
print(conversation.activeMessages)
}
}

Adding an array of Json Data to Realm

I'm making an app for airports and I'm getting an array of data from one api, like so:
"data":[
{"id":"001","code":"ABZ","name":"Aberdeen","country":"United Kingdom"},
{"id":"002","code":"AUH","name":"Abu Dhabi","country":"United Arab Emirates"},
.
.
.
]
AND :
"airports":[
{"from":"001",
"to":["1","3","11","13","12","20","23","27","29","31","33"]
},
.
.
.
]
I have created realm model classes:
class AirportsDataRealm: Object {
#objc dynamic var name: String = ""
#objc dynamic var id: Int = 0
#objc dynamic var code: String = ""
#objc dynamic var country: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
class AirportsFromToRealm: Object {
#objc dynamic var fromID: Int = 0
var toID = List<Int>()
override static func primaryKey() -> String? {
return "fromID"
}
}
now I want to save it into realm, I'm using swiftyJSON and I have used for-loop to do it and it is working fine but I think it's taking long time since the array is very long, here is what I've done:
// Airports Data
let countData = json["data"].count
for i in 0...countData - 1{
let airportsDataModel = AirportsDataRealm()
airportsDataModel.code = json["data"][i]["code"].stringValue
airportsDataModel.name = json["data"][i]["name"].stringValue
airportsDataModel.country = json["data"][i]["country"].stringValue
airportsDataModel.id = Int(json["data"][i]["id"].stringValue)!
try! realm.write {
realm.add(airportsDataModel, update: true)
}
}
//Airports FROM-TO
let countFromTo = json["airports"].count
for i in 0...countFromTo - 1{
let fromToDataModel = AirportsFromToRealm()
fromToDataModel.fromID = Int(json["airports"][i]["from"].stringValue)!
let arrayTo = json["airports"][i]["to"].arrayValue.map{ $0.intValue }
fromToDataModel.toID.append(objectsIn: arrayTo)
try! realm.write {
realm.add(fromToDataModel, update: true)
}
}
is there any way to save the whole array in realm in one shot without for-loop?
P.S
"there should be a relation between the two tables because each from 'id' has a list of 'to' id's and the id's are from the data table, for now I managed to create this relations when fetching the data using filters ,, so just ignore this"
Thank you
Simply use map method,
First I needed to add initializers to my object classes and pass json array as a parameter, like so:
class AirportsDataRealm: Object {
#objc dynamic var name: String = ""
#objc dynamic var id: Int = 0
#objc dynamic var code: String = ""
#objc dynamic var country: String = ""
convenience required init(withJSON json : JSON) {
self.init()
self.name = json["name"].stringValue
self.id = json["id"].intValue
self.code = json["code"].stringValue
self.country = json["country"].stringValue
}
override static func primaryKey() -> String? {
return "id"
}
}
class AirportsFromToRealm: Object {
#objc dynamic var fromID: Int = 0
var toID = List<Int>()
convenience required init(withJSON json : JSON) {
self.init()
self.fromID = json["from"].intValue
let toArray = json["to"].arrayValue.map{ $0.intValue }
self.toID.append(objectsIn: toArray)
}
override static func primaryKey() -> String? {
return "fromID"
}
}
Then by using map method the code will look like this:
func updateAirport(json: JSON) {
// Airports Data
let airportsData : [AirportsDataRealm]
let airportsDataJsonArray = json["data"].array
airportsData = airportsDataJsonArray!.map{AirportsDataRealm(withJSON: $0)}
//Airports FROM-TO
let airportsFromTo : [AirportsFromToRealm]
let airportsFromToJsonArray = json["airports"].array
airportsFromTo = airportsFromToJsonArray!.map{AirportsFromToRealm(withJSON: $0)}
//Write To Realm
try! realm.write {
realm.add(airportsData, update: true)
realm.add(airportsFromTo, update: true)
}
}
No for loops anymore ^_^

How to make search query to get all Objects in Realm

I am creating a Realm object like below
import RealmSwift
class Item: Object {
#objc dynamic var ID = 0
#objc dynamic var notificationTitleString = ""
#objc dynamic var notificationBodyString = ""
#objc dynamic var notificationDateString = ""
#objc dynamic var notificationType = ""
#objc dynamic var notificationUrl = ""
#objc dynamic var notificationActivity = ""
#objc dynamic var notificationIsRead = ""
override static func primaryKey() -> String? {
return "ID"
}
}
I am using the below method to get a particular item type
import UIKit
import RealmSwift
class DBManager {
private var database: Realm
static let sharedInstance = DBManager()
private init() {
database = try! Realm()
print(Realm.Configuration.defaultConfiguration.fileURL!)
}
func fetchNotificationType(type: String) -> Results<Item> {
let predicate = NSPredicate(format: "notificationType = %#", type)
let results : Results = database.objects(Item.self).filter(predicate)
return results
}
}
Using the above fetchNotificationType method i am able to get a single object, But i want all the objects in a single query. I do not know how to do that, I am trying Realm for the first time.
I searched SO, but i did not get any related answers.

Adding a new relationship to an existing model in Realm

I'm developing an iOS application using Realm and have a object MedicationObject which contains the information about the Medication being used. This then has a many to one relationship class MedicationRecording which stores the information when the time the medication was taken.
import Foundation
import RealmSwift
class MedicationObject : Object {
//This model requires a Primary Key
#objc dynamic var id = 0
override static func primaryKey() -> String? {
return "id"
}
#objc dynamic var medicationName : String = ""
//stores the refrences to medication recoridngs
var messurements = List<MedicationRecording>()
public func addNewMesurment() {
print(self)
let medicationRecording = MedicationRecording()
medicationRecording.medicationTaken = self
medicationRecording.timeTaken = Date()// this saves the time to now
medicationRecording.medicationTaken = self
RealmAccess().updateMedicationObject(medicationObject: self, medicationRecord: medicationRecording) //this should work
}
}
here is my MedicationRecording:
import Foundation
import RealmSwift
class MedicationRecording : Object {
#objc dynamic var medicationTaken : MedicationObject? = MedicationObject()
#objc dynamic var timeTaken : Date = Date()
#objc dynamic var id = 0
override static func primaryKey() -> String? {
return "id"
}
}
and this is the method I'm calling to save the data to
func updateMedicationObject(medicationObject : MedicationObject, medicationRecord : MedicationRecording) {
let realm = try! getRealmAccess()
let mediObject = MedicationObject()
mediObject.medicationName = medicationObject.medicationName
do {
try! realm.write {
realm.add(medicationRecord, update: true)
medicationObject.messurements.append(medicationRecord)
}
}
}
At the moment when I'm saving the data it is then losing all the information in MedicationObject and only saving the last data.
Any help with be greatly appreciated
Thank you

Resources