I have a bug when I update the pod version on my Podfile. I'm using the latest version of RealmSwift's framework on xCode 9. This is my model:
import Foundation
import RealmSwift
import ObjectMapper
class Salesman: Object,Mappable,DataElementDelegate, ObjectDelegate {
//MARK: - properties
public dynamic var id: Int = 0
public dynamic var name: String = ""
public dynamic var amount: Int = 0
public dynamic var updated_at_timestamp: Int = 0
//MARK: - Init
required convenience init?(map: Map) {
self.init()
}
//MARK: - Mapping
func mapping(map: Map) {
id<-map["id"]
name<-map["name"]
amount<-map["amount"]
updated_at_timestamp<-map["updated_at_timestamp"]
}
//MARK: - Realm methods
override static func primaryKey()->String?{
return "id"
}
//MARK: - Methods
public func getID()->Int{
return self.id
}
//MARK: - Data delegate
func getAmount() -> String {
return "\(self.amount)"
}
func getName() -> String {
return self.name
}
func getLastUpdate() -> Int {
return self.updated_at_timestamp
}
}
Now when I try to insert values on my realmn I have this crash 'RLMException', reason: 'Invalid value '1000' for property 'Salesman.amount''.
I write that insert code:
//MARK: - Mocks Salesman
public func mocksSalesman()->[Salesman]{
var array: [Salesman] = []
let sale1 = Salesman()
sale1.id = 1
sale1.name = "Raul"
sale1.amount = 1000
let sale2 = Salesman()
sale2.id = 2
sale2.name = "Tomas"
sale2.amount = 500
let sale3 = Salesman()
sale3.id = 3
sale3.name = "Luciano"
sale3.amount = 700
array.append(sale1)
array.append(sale2)
array.append(sale3)
return array
}
public func insert(){
let list = self.mocksSalesman()
let realm = try! Realm()
for item in list{
try! realm.write {
realm.add(item, update: true)
}
}
}
I've trying to insert 3 salesmen into my realm's database but I have that issue.
What could be happen?
Regards
Ok guys, I have the answer. That just put this code:
func getAmount() -> Int {
return self.amount
}
Instead of:
func getAmount() -> String {
return "\(self.amount)"
}
Related
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 ^_^
Any ideas why Swift is not smart enough to infer the parameters passed to the observeWrapper function.
Code:
let implementation = QuestionJSONStrategy(name: questionGroup.course.rawValue)
_ = observeWrapper(implementation)
showQuestion()
}
func observeWrapper<T: NSObject & QuestionStrategy>(_ object: T) -> NSKeyValueObservation {
return object.observe(\.questionIndex, options: .new) { _, change in
guard let newValue = change.newValue else { return }
print(newValue)
}
}
QuestionStrategy Protocol:
#objc protocol QuestionStrategy :AnyObject {
var questions :[Question] { get set}
var questionIndex :Int { get set }
init(name :String)
func nextQuestion() -> Question
}
QuestionJSONStrategy Class:
#objc public class QuestionJSONStrategy :NSObject, QuestionStrategy {
var questions: [Question] = [Question]()
#objc dynamic var questionIndex: Int = 0
I'm stuck putting all of the above together. I'll appreciate if I can get any input.
Here's my short setup:
typealias RealmObject = Object
/// Extension to ignore undefined keys when mapping
extension RealmObject : EVReflectable {
open override func setValue(_ value: Any?, forUndefinedKey key: String) { }
}
Sample Realm models:
class Product: RealmObject {
dynamic var productId: String = ""
let productLanguages = List<ProductLanguage>()
override static func primaryKey() -> String? {
return "productId"
}
}
class ProductLanguage: RealmObject {
dynamic var productLanguageId: String = ""
dynamic var languageCode: String = ""
dynamic var productName: String = ""
override static func primaryKey() -> String? {
return "productLanguageId"
}
}
To fetch product details I use Moya and RxSwift:
func getProduct(productItemKey: String) -> Observable<Product> {
return provider.request(.product(productId: productItemKey)).map(to: Product.self)
}
I think .map(to: Product.self) does not work with realm Lists out of the box. For each object inside the list I get an error:
ERROR: Could not create an instance for type
dict:{
CreateDate = "2015-10-12T11:11:50.013Z";
IngredientList = "Acao ingredient";
LanguageCode = "en-US";
ProductId = "d6bb0084-6838-11e5-9225-00ac14ef2300";
ProductLanguageId = "f96848d0-df77-4594-99b7-d390bb127891";
ProductName = Acao;
Tagline = "The smart drink - 100% organic, vegan energy booster with guara"
}
Is there any other way to map Moya response into Realm objects?
Thank you very much for any input!
Turns out it was a bug in EVReflection. Fixed in 4.17.0
I am facing an issue where I am unable to keep existing relationships after calling add(_, update: true) function.
I wrote a TaskSync class that is responsible for creating/updating Task objects:
class TaskSync: ISync {
typealias Model = Task
func sync(model: Task) {
let realm = try! Realm()
let inWrite = realm.isInWriteTransaction
if !inWrite {
realm.beginWrite()
}
let _task = realm.object(ofType: Task.self, forPrimaryKey: model.id)
// Persist matches as they are not getting fetched with the task
if let _task = _task {
print("matches: \(_task.matches.count)")
model.matches = _task.matches
}
realm.add(model, update: true)
if _task == nil {
var user = realm.object(ofType: User.self, forPrimaryKey: model.getUser().id)
if (user == nil) {
user = model.getUser()
realm.add(user!, update: true)
}
user!.tasks.append(model)
}
if !inWrite {
try! realm.commitWrite()
}
}
func sync(models: List<Task>) {
let realm = try! Realm()
try! realm.write {
models.forEach { task in
sync(model: task)
}
}
}
}
When a model is to be synced, I check if it already exists in the Realm and if so, I fetch it and try to include the matches property as this one is not included in the model.
Right before the call realm.add(model, update: true), model contains list of matches, however right after the realm.add is executed, the matches list is empty.
Here are the two models:
class Task: Object, ElementPreloadable, ElementImagePreloadable, ItemSectionable {
dynamic var id: Int = 0
dynamic var title: String = ""
dynamic var desc: String = ""
dynamic var price: Float = 0.0
dynamic var calculatedPrice: Float = 0.0
dynamic var location: String = ""
dynamic var duration: Int = 0
dynamic var date: String = ""
dynamic var category: Category?
dynamic var currency: Currency?
dynamic var longitude: Double = 0.0
dynamic var latitude: Double = 0.0
dynamic var state: Int = 0
dynamic var userId: Int = 0
// Existing images
var imagesExisting = List<URLImage>()
// New images
var imagesNew = List<Image>()
// Images deleted
var imagesDeleted = List<URLImage>()
private let users = LinkingObjects(fromType: User.self, property: "tasks")
var user: User?
var matches = List<Match>()
dynamic var notification: Notification?
override static func ignoredProperties() -> [String] {
return ["imagesExisting", "imagesNew", "imagesDeleted", "user", "tmpUser"]
}
override static func primaryKey() -> String? {
return "id"
}
func getImageMain() -> URLImage? {
for image in imagesExisting {
if image.main {
return image
}
}
return imagesExisting.first
}
func getSection() -> Int {
return state
}
func getSectionFieldName() -> String? {
return "state"
}
func getId() -> Int {
return id
}
func getURL() -> URL? {
if let image = getImageMain() {
return image.getResizedURL()
}
return nil
}
func getState() -> TaskOwnState {
return TaskOwnState(rawValue: state)!
}
func getUser() -> User {
return (user != nil ? user : users.first)!
}
}
class Match: Object, ElementPreloadable, ElementImagePreloadable, ItemSectionable {
dynamic var id: Int = 0
dynamic var state: Int = -1
dynamic var priorityOwnRaw: Int = 0
dynamic var priorityOtherRaw: Int = 0
dynamic var user: User!
var messages = List<Message>()
private let tasks = LinkingObjects(fromType: Task.self, property: "matches")
var task: Task?
dynamic var notification: Notification?
override static func primaryKey() -> String? {
return "id"
}
override static func ignoredProperties() -> [String] {
return ["task"]
}
func getId() -> Int {
return id
}
func getSection() -> Int {
return 0
}
func getURL() -> URL? {
if let image = user.getImageMain() {
return image.getResizedURL()
}
return nil
}
func getPriorityOwn() -> PriorityType {
if priorityOwnRaw == PriorityType.normal.rawValue {
return PriorityType.normal
}
else {
return PriorityType.favorite
}
}
func getPriorityOther() -> PriorityType {
if priorityOtherRaw == PriorityType.normal.rawValue {
return PriorityType.normal
}
else {
return PriorityType.favorite
}
}
func getSectionFieldName() -> String? {
return nil
}
func getTask() -> Task {
return (task != nil ? task : tasks.first)!
}
}
I spent hours trying to figure out why I am unable to keep the matches relationship when updating the task. Every advice will be highly appreciated!
This question was also asked upon Realm's GitHub issue tracker. For posterity, here is the solution.
List properties should always be declared as let properties, as assigning to them does not do anything useful. The correct way to copy all objects from one List to another is model.tasks.append(objectsIn: _user.tasks).
I have the main model of ChatConversationModel, which has an array property userAcitivities that is an array of ChatUserAcitivityModel objects. I want to get from Realm the ChatConversationModel object in which the userAcitivities contains the given friendID. I've tried a few different ways but haven't been able to get the result I'm after.
import Foundation
import RealmSwift
import ObjectMapper
class ChatConversationModel: Object, Mappable {
dynamic var id = ""
dynamic var typeIndex = ChatTypes.ChatType.oneToOne.index
var userAcitivities = List<ChatUserAcitivityModel>()
override class func primaryKey() -> String? {
return "id"
}
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
if map.mappingType == .fromJSON {
id <- map["id"]
typeIndex <- map["typeIndex"]
userAcitivities <- (map["userAcitivities"], ListTransform<ChatUserAcitivityModel>())
} else {
id >>> map["id"]
typeIndex >>> map["typeIndex"]
userAcitivities >>> (map["userAcitivities"], ListTransform<ChatUserAcitivityModel>())
}
}
}
import Foundation
import RealmSwift
import ObjectMapper
class ChatUserAcitivityModel: Object, Mappable {
dynamic var userID = ""
/// This is necessary in order to know from what point to download the chat if the user deleted it.
/// If this property is 0.0, then there has never been a deletion.
dynamic var removedChatTimeStamp = 0.0
override class func primaryKey() -> String? {
return "userID"
}
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
if map.mappingType == .fromJSON {
userID <- map["userID"]
removedChatTimeStamp <- map["removedChatTimeStamp"]
} else {
userID >>> map["userID"]
removedChatTimeStamp >>> map["removedChatTimeStamp"]
}
}
}
func getFriendChatConversationModel(_ friendID: String) {
let realm = try! Realm()
let chatConversationModels = realm.objects(ChatConversationModel.self).filter("typeIndex = %#", ChatTypes.ChatType.oneToOne.index)
let friend = chatConversationModels.filter { $0.userAcitivities.filter { $0.userID == friendID } }
}
At the moment I'm doing the following and it's working for me, but I'd like to find the best way to express this:
func getFriendChatConversationModel(_ friendID: String) -> ChatConversationModel? {
let realm = try! Realm()
let chatConversationModels = realm.objects(ChatConversationModel.self).filter("typeIndex = %#", ChatTypes.ChatType.oneToOne.index)
var friendChatConversationModel: ChatConversationModel?
for chatConversationModel in chatConversationModels {
if chatConversationModel.userAcitivities.contains(where: { (chatUserAcitivityModel) -> Bool in
chatUserAcitivityModel.userID == friendID
}) {
friendChatConversationModel = chatConversationModel
return friendChatConversationModel
}
}
return nil
}
If I understand correctly, I think a query like this will do what you're after:
func getFriendChatConversationModel(_ friendID: String) -> ChatConversationModel? {
let realm = try! Realm()
let chatConversationModels = realm.objects(ChatConversationModel.self).filter("typeIndex = %#", ChatTypes.ChatType.oneToOne.index)
return chatConversationModels.filter("ANY userAcitivities.userID == %#", friendID).first
}