Closed dipkasyap closed 6 years ago
Can you either reduce this to a standalone repro case, or share the source to your application which runs into problems? Without that it will be difficult to make any progress.
Thank you for replay, i can share my modal class . Do you wish me to do so ? As i have mentioned before, it was crashing on some users when the realm version was 3.0.2 , but when i upgraded the realm to version 3.5.0 on same code , with out changing any code/ modal class it is crashing on every attempt.
Just the model classes are unlikely to reveal anything interesting.
@tgoyne What is the solution for me then? believe there is some serious issue with library. Could you please provide clue what might be the reason of crash?
Yes, this probably a Realm bug. If it is crashing every time, then you should be able to reduce it to a standalone piece of code which hits the problem which you could share with us.
/************************* Realm Issue method block *************************/
id RLMAccessorContext::propertyValue(__unsafe_unretained id const obj, size_t propIndex,
__unsafe_unretained RLMProperty *const prop) {
// Property value from an NSArray
if ([obj respondsToSelector:@selector(objectAtIndex:)]) {
return propIndex < [obj count] ? [obj objectAtIndex:propIndex] : nil;
}
// Property value from an NSDictionary
if ([obj respondsToSelector:@selector(objectForKey:)]) {
return [obj objectForKey:prop.name];
}
// Property value from an instance of this object type
id value;
if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] && prop.swiftIvar) {
if (prop.array) {
return static_cast<RLMListBase *>(object_getIvar(obj, prop.swiftIvar))._rlmArray;
}
else { // optional
value = RLMGetOptional(static_cast<RLMOptionalBase *>(object_getIvar(obj, prop.swiftIvar)));
}
}
else {
// Property value from some object that's KVC-compatible
value = RLMValidatedValueForProperty(obj, [obj respondsToSelector:prop.getterSel] ? prop.getterName : prop.name,
_info.rlmObjectSchema.className);
}
return value ?: NSNull.null;
}
/**************************************************/
/************************ MY Modal class **************************/
import Foundation
import ObjectMapper
import AlamofireObjectMapper
import ObjectMapper_Realm
import RealmSwift
// MARK: - Enum Meals Type
enum MealsType:String {
case nonVeg="nonVeg",veg="vegetarian",vegan = "vegan"
}
class Model: Object, Mappable {
@objc dynamic var name:String? = nil
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
name <- map["name"]
}
}
class MyAppNameDaysDataModel:Object, Mappable {
///This variable tracks training all stage compeleted or not,
///if hasData = false this means there are no data further available so
///we consider the training or all stages are completed
///if hasData = true then we will fetch new data from server and then replace the offlice cache
var hasData = true
//error handling vars
private var errorStatus : Bool?
private var messageString: String?
var message: String? {
get {
return messageString
}
}
var error: Bool? {
get {
return errorStatus
}
}
//Normal vars
@objc dynamic var name: String? = ""
@objc dynamic var code: String? = nil
@objc dynamic var color: String? = nil
@objc dynamic var descriptions: String? = nil
@objc dynamic var menuTitle: String? = nil
@objc dynamic var infoTitle: String? = nil
@objc dynamic var info: String? = nil
@objc dynamic var currentDay: Int = 1
@objc dynamic var currentStage: Int = 1
@objc dynamic var workOutSummary: String? = nil
@objc dynamic var image: String? = nil
@objc dynamic var timeStamp: String? = nil
var days = List<DayResponseModel>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
//flag var
hasData <- map["data.has_data"]
//error vars
errorStatus <- map["error"]
messageString <- map["message"]
//Regualr vars
name <- map["data.training_program_info.name"]
code <- map["data.training_program_info.code"]
color <- map["data.training_program_info.color"]
descriptions <- map["data.training_program_info.description"]
menuTitle <- map["data.training_program_info.menu_title"]
infoTitle <- map["data.training_program_info.info_title"]
info <- map["data.training_program_info.info"]
currentStage <- map["data.current_day_info.stage"]
currentDay <- map["data.current_day_info.day"]
workOutSummary <- map["data.current_day_info.workout_summary"]
image <- map["data.current_day_info.image"]
timeStamp <- map["data.training_completed_timestamp"]
//update current training type
if let code = code {
UserDefaultsHandler().setToUD(key: UDkey.trainingProgram.rawValue, value: code as AnyObject)
}
days <- (map["data.days"], ListTransform<DayResponseModel>())
}
// MARK: - Geter
class func get(_ viewController: UIViewController, withMessage message:String = "Loading data", withCompletion completion:@escaping (MyAppNameDaysDataModel?)->(), withError error:@escaping ()->() ) {
APIManager(urlString: MyAppNameURL.URLGetTodayInfo, method: .get).getTodaysData(viewController, withProgressMessage: message) { (success,MyAppNameDaysDataModel ) in
if success {
completion(MyAppNameDaysDataModel)
} else {
//show error or retry
completion(nil)
}
}
}
}
class DayResponseModel:Object, Mappable {
@objc dynamic var id: String? = nil
@objc dynamic var stage: String? = nil
@objc dynamic var day : String? = nil
@objc dynamic var workoutSummary: String? = nil
@objc dynamic var messageQuote:String?
@objc dynamic var messageQuoteTitle:String?
@objc dynamic var messageImageUrl:String?
@objc dynamic var trainingThemeGym: String? = nil
@objc dynamic var trainingThemeHome: String? = nil
var trainingTheme: String {
get {
if (UserLoginInfo.getUserInfo()?.trainingEnvironment) ?? .gym == .gym {
return self.trainingThemeGym ?? ""
} else {
return self.trainingThemeHome ?? ""
}
}
}
@objc dynamic var trainingThemeDescriptionGym: String? = nil
@objc dynamic var trainingThemeDescriptionHome: String? = nil
var trainingThemeDescription: String {
get {
if (UserLoginInfo.getUserInfo()?.trainingEnvironment)! == .gym {
return self.trainingThemeDescriptionGym!
} else {
return self.trainingThemeDescriptionHome!
}
}
}
@objc dynamic var image: String? = nil
@objc dynamic var isToday: Bool = false
@objc dynamic var todayCandidate: Bool = false
@objc dynamic var isCompleted: Bool = false
var mealsOfDay = List<MealsOptionsModel>()
@objc dynamic var trainingOfDay : TrainingModel? = nil
///view manipulation var
@objc dynamic var currentTrainingIndex:Int = 0
func getAllMeals() -> [MealModel] {
var meals:[MealModel] = [MealModel]()
let mealsOfDay = Array(self.mealsOfDay)
for meal in mealsOfDay {
let options = meal.mealsOption
for option in options {
meals.append(option)
}
}
return meals
}
func getActiveMealsOfDay() -> [MealModel] {
var meals:[MealModel] = [MealModel]()
let mealsOfDay = Array(self.mealsOfDay)
for aMeal in mealsOfDay {
meals.append(aMeal.getActiveMeal()!)
}
return meals
}
var isExpanded: Bool = false
var currentOpennedIndex:Int = -1
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
stage <- map["stage"]
day <- map["day"]
workoutSummary <- map["workout_summary"]
image <- map["image"]
isToday <- map["today"]
isCompleted <- map["completed"]
trainingThemeGym <- map["theme_name_gym"]
trainingThemeHome <- map["theme_name_home"]
trainingThemeDescriptionGym <- map["theme_description_gym"]
trainingThemeDescriptionHome <- map["theme_description_home"]
messageQuoteTitle <- map["training_quote.title"]
messageQuote <- map["training_quote.quote"]
messageImageUrl <- map["training_quote.image"]
let userInfo = UserLoginInfo.getUserInfo()
let dietRequirement = userInfo?.dietRequirement
let key = "meals.\(dietRequirement!.lowercased())"
mealsOfDay <- (map[key], ListTransform<MealsOptionsModel>())
//Training
trainingOfDay <- map["workouts"]
}
}
class MealsOptionsModel:Object, Mappable{
var mealsOption = List<MealModel>()
/// showIndex is the index value of current meals among meals options array
@objc dynamic var showIndex: Int = 0 {
didSet {
if oldValue != self.showIndex {
let meals = self.mealsOption
for i in 0...meals.count-1 {
if self.showIndex == i {
meals[i].isSelected = true
} else {
meals[i].isSelected = false
}
}
}
}
}
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
///all meals of the day
mealsOption <- (map["options"], ListTransform<MealModel>())
///Current meal tracker for future use
showIndex <- map["show_index"]
}
func getActiveMeal() -> MealModel? {
if uiRealm.objects(MyAppNameDaysDataModel.self).first == nil {
print("Nullvalue of objectg...... Need to handlled accordingly....DIP")
return nil
}
let meals = self.mealsOption
let index = self.showIndex
return meals[index]
}
func replaceMeal(_ then:(_ replaced:Bool,_ mealId:String?, _ isOldMeal:Bool?)->()) {
for index in 0...self.mealsOption.count-1 {
if self.mealsOption[index].isSelected {
if uiRealm.isInWriteTransaction {
do {
try uiRealm.commitWrite()
}
catch {
print("Unableto commit")
then(false,nil,false)
}
}
uiRealm.beginWrite()
guard self.showIndex != index else { then(false,nil,true); return }
self.showIndex = index
do {
try uiRealm.commitWrite() }
catch {
print("Unableto commit")
then(false,nil,false)
}
then(true,self.mealsOption[index].id,false)
return
} else {
if index+1 == self.mealsOption.count {
then(false,nil,false)
}
}
}
}
func rollBackReplaceProcess() {
do {
try uiRealm.commitWrite()
} catch {
print("Unable to commit")
}
uiRealm.beginWrite()
for index in 0...self.mealsOption.count-1 {
self.mealsOption[index].isSelected = index == self.showIndex
}
do {
try uiRealm.commitWrite()
} catch {
print("Unable to roll back commit")
}
}
}
class UnitMeasure:Object, Mappable{
@objc dynamic var ingridient: String? = nil
@objc dynamic var quantity: CGFloat = 0.0
@objc dynamic var unit: String? = nil
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
ingridient <- map["ingridient"]
quantity <- map["quantity"]
unit <- map["unit"]
}
}
class MealModel:Object, Mappable {
@objc dynamic var id: String?
@objc dynamic var title : String?
@objc dynamic var descriptions: String?
@objc dynamic var tags: String?
@objc dynamic var tips: String?
@objc dynamic var PreparationTime: String?
@objc dynamic var servings: String?
@objc dynamic var calories : Int = 0
@objc dynamic var ingredientsMetric: String?
@objc dynamic var ingredientsImperial: String?
var ingredients: String {
let unit = MeasurementUnit(rawValue: UserDefaultsHandler().getUDValue(key: Units_of_Measure) as? Int ?? 1 )
if unit == .metric {
return ingredientsImperial! //ingredientsMetric!
} else {
return ingredientsImperial ?? ingredientsMetric!
}
}
@objc dynamic var mealNo : Int = 1
@objc dynamic var steps : String? = nil
@objc dynamic var favouredMeal: Bool = false
@objc dynamic var image : String?
public let imperialIngridients = List<StringObject>() // Should be declared with `let`
public let metricIngridients = List<StringObject>() // Should be declared with `let`
public let dietShortCode = List<StringObject>() // Should be declared with `let`
var impericalQuantity = List<UnitMeasure>()
var metricQuantity = List<UnitMeasure>()
@objc dynamic var isSelected = false {
didSet {
}
}
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
title <- map["title"]
descriptions <- map["description"]
tags <- map["tags"]
tips <- map["tips"]
PreparationTime <- map["preparation_time"]
servings <- map["servings"]
calories <- map["calories"]
mealNo <- map["meal_no"]
// MARK: - converting array into string with new line
var stepArray: [String]?
stepArray <- map["steps"]
steps = (stepArray?.joined(separator:"\n"))!
favouredMeal <- map["favoured_meal"]
image <- map["image"]
//handling array for realm
var dietShortCode: [String]? = nil
dietShortCode <- map["diet_shortcode"] // Maps to local variable
dietShortCode?.forEach { option in // Then fill options to `List`
let value = StringObject()
value.value = option
self.dietShortCode.append(value)
}
var metricIngridients: [String]? = nil
metricIngridients <- map["ingridients.metric"] // Maps to local variable
metricIngridients?.forEach { option in // Then fill options to `List`
let value = StringObject()
value.value = option
self.metricIngridients.append(value)
}
var imperialIngridients: [String]? = nil
imperialIngridients <- map["ingridients.imperial"] // Maps to local variable
imperialIngridients?.forEach { option in // Then fill options to `List`
let value = StringObject()
value.value = option
self.imperialIngridients.append(value)
}
self.ingredientsMetric = metricIngridients?.joined(separator: "\n")
self.ingredientsImperial = imperialIngridients?.joined(separator: "\n")
metricQuantity <- (map["ingridients.metric_quantity"], ListTransform<UnitMeasure>())
impericalQuantity <- (map["ingridients.imperial_quantity"], ListTransform<UnitMeasure>())
}
}
/************************ End MY Modal class **************************/
/******************** Realm accessing Method Block class ***************/
// Method to flush
func flushDataBase() {
if uiRealm.isInWriteTransaction {
do {
try uiRealm.commitWrite()
} catch {
print("Unable to commit")
}
}
if syncRealm.isInWriteTransaction {
do {
try syncRealm.commitWrite()
} catch {
print("Unable to commit")
}
}
do {
try syncRealm.write {
syncRealm.deleteAll()
}
} catch {
print("Unable to flush -sync DB... Retrying")
flushDataBase()
}
do {
try uiRealm.write {
uiRealm.deleteAll()
}
} catch {
print("Unable to flush DB... Retrying")
flushDataBase()
}
}
//This method to prepare Realm on case of removeAll called previously
func prepareSyncEngine() {
func isDataExists()->Bool {
let data = syncRealm.objects(UserSyncModal.self)
let alldata = data.first
if alldata != nil {
return true
} else {
return false
}
}
if !isDataExists() {
do {
try syncRealm.write {
let sync = UserSyncModal()
AppDataProvider.shared.syncEngine = sync
syncRealm.add(AppDataProvider.shared.syncEngine!)
}
}catch {
print("Unablet to write data")
}
} else {
let sync = syncRealm.objects(UserSyncModal.self)
AppDataProvider.shared.syncEngine = sync.first
print("Sync table exist...")
}
//Countries
Countries.loadCountriesList { (countries, success) in
if success {
self.countries = countries
}
}
}
}
//Realm accessing method
// MARK: - get Todays Info
@objc private func getTodayinfo(_ syncing: Bool = false, isServerSync: Bool = false ) {
print(Realm.Configuration.defaultConfiguration.fileURL!)
func loadOfflineData(_ then:@escaping (_ loadOffline:Bool)->()) {
self.blur()
let data = uiRealm.objects(MyAppNameDaysDataModel.self)
let alldata = data.first
if alldata != nil {
//has data on offline
if AppDataProvider.shared.isSyncRequired() || isServerSync {
AppDataProvider.shared.sync(self, then: { [weak self] (success, error) in
if success {
self?.isSyncing = true
//self?.getTodayinfo(true)
} else {
guard let syncError = error?.syncError else {
then(true)
return
}
if syncError {
if uiRealm.isInWriteTransaction {
do {
try uiRealm.commitWrite()
} catch {
print("Unable to commit")
}
}
//sync engine clearing
do {
try uiRealm.write {
uiRealm.delete(AppDataProvider.shared.syncEngine!)
}
}
catch {
print("could not write")
}
}
//self?.getDataFromServer(true, isServerSync: true)
}
then(false) // needs to refetch data
})
} else {
then(true)//offline cache had data
}
} else {
//no data on offline
let vc: TapToReloadViewController = UIStoryboard(storyboard: .noInternetConnection).instantiateViewController()
self.tapToReloadVC = vc
self.reachability = Reachability()!
reachability?.whenUnreachable = { [weak self] _ in
if self?.presentedViewController != nil {
}else{
self?.present((self?.tapToReloadVC)!, animated: true, completion: nil)
}
print("Not reachable")
}
do {
try reachability?.startNotifier()
} catch {
print("Unable to start notifier")
}
then(false)//no data on offline, fetch from server
}
}
let syncRequired = UserDefaultsHandler().getUDValue(key: UDkey.isSyncing.rawValue) as? Bool ?? false
loadOfflineData { [weak self] (loadOffline) in
if loadOffline && !syncRequired {
AppDataProvider.shared.allData = Array(uiRealm.objects(MyAppNameDaysDataModel.self)).first
if !uiRealm.isInWriteTransaction {
AppDataProvider.shared.hasTodaysTrainingFinisehd {
self?.setUpApi(data: AppDataProvider.shared.allData)
}
self?.presentedViewController?.dismiss(animated: true, completion: nil)
}
} else {
self?.getDataFromServer(true, isServerSync: true)
}
}
}
func getDataFromServer(_ syncing: Bool = false, isServerSync: Bool = false ) {
self.blur()
let isSyncing = (UserDefaultsHandler().getUDValue(key: UDkey.isSyncing.rawValue) as? Bool ?? false) || syncing
self.isSyncing = isSyncing
MyAppNameDaysDataModel.get(self, withMessage: isSyncing ? "Syncing data" : "Loading data" , withCompletion: { [weak self] (response) in
UserDefaultsHandler().removeUD(key: UDkey.isSyncing.rawValue)
if let response = response {
guard response.hasData else {
print("Needs to handle this.....")
let story = UIStoryboard(name: StoryboardName.Training.rawValue, bundle: nil)
let vc1 = story.instantiateViewController(withIdentifier: StoryboardID.StageCompletionVCID.rawValue )as! StageCompletionVC
self?.present(vc1, animated: true, completion: nil)
return
}
//Prepare Realm
flushDataBase()
AppDataProvider.shared.prepareSyncEngine()
do {
try uiRealm.write {
uiRealm.add(response) // This is the line where realm crahes
}
}
catch {
print("could not write")
}
self?.getTodayinfo()
} else {
ProgressHud.hideProgressHUD()
self?.unblur()
Banner.show("Some thing went wrong!", andSubtitle: "Please try again later", andColor: UIColor.appDefaultColor)
let vc: TapToReloadViewController = UIStoryboard(storyboard: .noInternetConnection).instantiateViewController()
self?.tapToReloadVC = vc
self?.reachability = Reachability()!
self?.present((self?.tapToReloadVC)!, animated: true, completion: nil)
}
}) { [weak self] in
ProgressHud.hideProgressHUD()
self?.unblur()
Banner.show("Opps", andSubtitle: "Server not responding..")
}
}
/******************** End accessing Method Block ***************/
@tgoyne Please have a look
Thanks, I'll see if I can reproduce the issue.
@tgoyne Any progress ? My App is in production and facing big loss.
It looks like the problem is that the private var messageString: String?
property is not being handled correctly. String?
is a support type so it's not being automatically ignored, but because it's not @objc
it's being misdetected and initialized incorrectly.
While I'm working on a fix for this you should be able to work around it by explicitly telling Realm not to touch the private properties by adding them to ignoredProperties
(i.e. override class func ignoredProperties() -> [String] { return ["hasData", "errorStatus", "messageString"] }
for the DaysDataModel.
I downgraded to 3.4.0 its working on same code base. There is some serious issue on lib, Which is not providing actual error to swift wrapper.
Installing Realm 3.4.0 (was 3.5.0 and source changed to `https://github.com/CocoaPods/Specs.git` from `https://github.com/cocoapods/specs.git`)
And it stoped to crash on
do {
try uiRealm.write {
uiRealm.add(response)
}
}
catch {
print("could not write")
}
Any way let me try to use ignoredProperties on 3.5.0 , i will update on this soon. Also i have a question regarding primary key, does is make any problem if i am not using primary key on my db ?
Hi there,
Why is this issue closed? I am facing the same issue in version 3.12.0. Is there any workaround?
@ptsiogas Please create a new issue with all details then (and refer to this one).
Goals
Prevent crash
Expected Results
DB Operates normally
Actual Results
Crahsed
Screen shots
Steps to Reproduce
Realm data read or write producing this issue, First my realm version was 3.0.2 at that time some users of app were facing this issue (not all users were facing this issue), Now i decided to upgrade library via cocoapod and mad 3.5.0 it is crashing on every build
Code Sample
Version of Realm and Tooling
Realm framework version: ? 3.5.0
Xcode version: Version 9.3 (9E145)
iOS/OSX version: 11.2.1 (iOS)
Dependency manager + version: CocoaPod 1.5.2