Realm on iPhone Exception on read

I have an app I am developing . The first read to a Realm db is looking for a username stored in primary key db with four properties. This works like a charm on the Simulator. Here is the line of code

nameLabel.text = users?[0].name

It exceptions with and out of bounds on the 0. The exception also says it must be less than 0. I am running 4.4 and have not done the effort to upgrade to 5.3.4 yet.

@Michael_Granberry As a fellow programmer getting acquainted with this system, I would suggest making sure that users actually contains data before dereferencing it with an array subscript.

if users?.count > 0 {
     nameLabel.text = users?[0].name
}

I suspect that the difference between the simulator and the actual phone is the Realm data for users has not finished downloading to the phone at the time you are dereferencing it.

Typically, one has to set up a notification handler on the query on the Realm in question, to check when the data has actually downloaded to the device. It is explained here.

https://docs.mongodb.com/realm/ios/notifications/

I hope this was useful.

Richard Krueger

1 Like

@Richard_Krueger

That’s a very good call and your explanation sounds correct, but unfortunately that code won’t work as is. If it’s and array, the Value of optional type ‘Int?’ must be unwrapped to a value of type ‘Int’.

If it’s a results then you cannot use optional chaining on non-optional value of type ‘Results’

But more importantly, it would be good to understand what users is in the first place as an optional array is little odd. Is it actually an array? Or a results object? Or something else?

The first read to a Realm db is looking for a username stored in primary key db with four properties.

If that’s correct and you’ve loaded a single object via it’s primary key them the array index (e.g. [0]) would not apply.

@Jay I am assuming that @Michael_Granberry intended users to be of type Results.

Let me try to clarify for Richard and Jay. In the first ViewController that is launched after the Launch Screen. I want to access the PersonalData.swift data model and Realm File to grab the user name that is already in the database. This is for a hello greeting on the first screen

let realm = **try** ! Realm()
var users: Results<PersonalData>?

 users = realm.objects(PersonalData.self)
 nameLabel.text = users?[0].name   //Realm retrieved [0] name property

This works fine in the first access on the simulator. But when I try to run it on the iPhone, I get an out of bounds message on the nameLabel.text = line. Robert mentioned that the reason could be because the Realm data may not have actually loaded on the iPhone at the time that I try to load it into nameLabel.text to make it visible on the view that is tied to that ViewController.

On that View, I have a Setup barbutton that looks like a gear. That will segue to the SetupViewController. I have 5 buttons on that View which segue to specific functionally to be maintained. The top button says Personal. By selecting it, I segue to the PersonalViewController and View that contains three TextFields that are loaded from the PersonalViewControllers access of the PersonalData db.

In that view controller I load three properties from the 0 record. There is only one object in the PersonData db. The three that I load are name, email and hireDate. They come in just fine in the simulator.

let realm = try! Realm()
var users: Results<PersonalData>?

        // Realm retrieved [0] properties from PersonalData.swift
        users = realm.objects(PersonalData.self)
        screenNameTextField.text = users?[0].name
        emailAddressTextField.text = users?[0].email
        hireDateTextField.text = users?[0].hireDate

These load just fine. Richard gave me some suggestions about ways to watch for a notification that the data has actually loaded on the iPhone before I try to use them. I have not done that development yet but that is next on my agenda to make sure I can load the original request as works in the simulator.

Here is my PersonalData.swift file. Please excuse the * that get loaded in the cut&paste.

**import** Foundation

**import** RealmSwift

**class** PersonalData: Object {

**@objc** **dynamic** **var** personalID: Int = 0

**@objc** **dynamic** **var** name: String = "" // Mike Granberry

**@objc** **dynamic** **var** email: String = "" // mgranbe@gmail.com

**@objc** **dynamic** **var** hireDate: String = "" // mm/dd/yyyy of first year

**let** businessYear = List<YearData>()

**let** anniversaryYear = List<AnniversaryData>()

**override** **class** **func** primaryKey() -> String? {

return "personalID"

}

}

I hope this helps the conversation. I am very appreciative. As you can tell I am very new at this language and trying to do some pretty bold object database access as the app grows. Thanks.

@Michael_Granberry my first question is this. Are you trying to just use Realm as a local object data base, or use Realm as synced data base that is connected to a shared Atlas cluster? In the first case, you would be using Realm to cache data locally for your application. In the second case, you would be using Realm to sync data with other devices running the same application but sharing a common set of data stored on the server.

I may have just assumed that you were working on the second scenario. If this is the case, you must first create a RealmApp object, you must then authenticate the user, and lastly you must issue a Realm.asyncOpen call with a user configuration to actually prep a Realm for downloading data from the server. Once you have completed these tasks, you then set up a query on the realm along with a notification call back.

I have detailed how to do this in a Medium article that I wrote a few weeks back. There is some open source code in a Github repo that you are free to download.

Richard Krueger

I put a more extensive description and flow on the Realm forum. Sorry if that was not correct. I am trying to use a local object database for persistence and the opportunity to grow it on a cloud solution if the user has more transactions each year than the iPhone should store. The user will enter small labor transactions daily or weekly and then query or filter some of the history to check his data. I hope that helps.

I did a ton of research on the Build Process System. I realized I was just going to have to dig further and invest more time. I had another project that used Realm that was working fine. I next compared all the Build parameters in the Project and felt pretty certain that was not the issue based on that review. I looked at the two errors which centered around not being able to find Realm.framework and RealmSwift.framework in my project but I could see they were there. When I looked in the project and all looked the same in the left hand pane for the Project assets, the Products, the Pods, but one difference in the Frameworks. for some reason the Realm.frame and the RealmSwift.frame work were sitting above the file listed as Pods_projectname.framework. I moved them below the Pods-projectname.framework. I did a clean up with Shift-Cmd-K and then did a Command-B buid. I compiled , linked and built with no errors. Look at the attached notice where the red arrow is. This is the correct position Frameworks to get the Build to work. I have no idea how this was out of whack.

Let me re-pose my questions so we understand more of what you’re trying to do. It’s critical to know what platform you’re using and in which way you’re using it

TL;DR

In a nutshell, you’re populating a var users but it’s not the same users you’re trying to access when reading data from it.

More

  1. Are you using ‘classic’ Realm with the SDK version # 5.3.x as shown here (4.x in your case)

or are you using the BETA MongoDB Realm SDK 10.x as shown here

https://docs.mongodb.com/realm/

  1. I believe you mentioned your data is stored locally and not sync’ing. Is that correct?

  2. Your message above states this code is not working on a real device and crashes

    let realm = try! Realm()
    var users: Results?
    users = realm.objects(PersonalData.self)
    nameLabel.text = users?[0].name

If that’s correct, it shouldn’t be crashing as long as data exists in the database. There are a few issues with the code though and this code is suspect

if users?.count > 0 {
     nameLabel.text = users?[0].name
}

as if users is an optional as shown in your question

var users: Results<PersonalData>?

, then that code will throw an error in XCode and won’t compile (Value of optional type ‘Int?’ must be unwrapped to a value of type ‘Int’). That tells me you have another var users floating around in your code - possibly a local one within a function which is not the same as a class var. I am a big proponent of addressing class vars with self and not naming vars with the same name.

If this is the code in your object it’s not correct

override class func primaryKey() -> String? {
   return "personalID"
}

Please replace it with

    override static func primaryKey() -> String? {
        return "personalID"
    }

I looked at the two errors which centered around not being able to find Realm.framework and RealmSwift.framework in my project

That’s a different issue than what was originally asked (about crashing on a real device with an out of bounds error). Can you clarify how that ties in?

Jason, thank you for the suggestions. You have given me some good things to look at. I am waiting on a new power supply for my MacBook but will get back with you after it arrives and I can get back to my code. Thanks again. I will put updates on the Realm forum also.

I have resolved the two errors about not being able to find the Realm.framework and the RealmSwift.framework. I had to move them in my workspace to be after the the definition of the Pods_appname.framework in the Frameworks folder. For some reason they were sitting above the Pods-appname.framework. That resolved the missing frameworks. I also went back to verson 4.4.0 in the podfile with the statement: pod ‘RealmSwift’, ‘4.4.0’

I have been unable to find a variable of users that was different from the use of the Results I had defined with :

var users: Results?
let realm = try! Realm()

at the top of the class where I try to setup up the use of the Realm.

Later I want to grab the user name from the PersonalData swift file with:

loadUserName()

    if users!.count > 0 {
        print(users!.count)
        nameLabel.text = users?[0].name
    } else {
        print(users!.count)
        nameLabel.text = "Sample Data"
    }

The loadUserName is as follows:
// MARK : Data Load and Save from Realm
func loadUserName() {
users = realm.objects(PersonalData.self)
}

When I run the code on the simulator I get the correct value loaded in to the nameLabel.text . When I run it on the iphone device, I get “Sample Data” as shown in the Else statement. On the simulator the count is equal to 1. On the iphone the count is equal to 0.

Well, it could be the case where the Realm file on the simulator has been populated with data but on the device was not.

Would you think that is a bug in the Realm code? I am not sure how to proceed, if it looks good on the simulator but will now work when I download from Xcode to the actual device. I am kind of at a standstill until the folks at Realm show some interest in working with this issue.

I will not speak for the MongoDB Realm folks but it’s not likely a bug in Realm. It’s likely a bug in your code - implementation or perhaps even overlooking something obvious. It happens!

Diagnosing an issue is very challenging here as without a solid troubleshooting path and code visibility, it’s hard to isolate the issue. There are some things you can do to help narrow it down.

I always have a button in my UI that allows me to isolate and test code. So in this situation I would add this code to my button action

@IBAction func myButtonAction( _ sender: Any ) {
   let realm = try! Realm()
   let results = realm.objects(PersonalData.self )
   print(results.count) //output to a text field
}

Obviously when you are running it on the device you don’t have a console so perhaps at a temporary text field in your UI where you can send output that would normally go to the console in the simulator.

Does that code output 1 or 0? If it’s 0 that tells us the Realm has no data. If 1 then it has data so then look further into how the data is being read. For example, this is not correct

var users: Results?

but this is

var personResults: Results<PersonalData>? = nil

Then inside functions to access the class var personResults, use self (for clarity) as in

func loadPeople() {
   let realm = try! Realm()
   self.personResults = realm....
}

Jay, I like your idea about the button and the textfield to help with the debug. I will try that. By the way, you mentioned that the code on the line that read … var users: Results? was incorrect. That was not my line of code. The cut & paste and preview function in the Realm forum actually changed my code. My code reads like this … var users: Results? Therefore your suggestion of … var personResults: Results? = nil except for the default value of nil. I will make that change but it seems similar to my code. If it is because I used the term users then I will change it to something that is unique for sure.

Once again, the preview changed my code. I will have to start posting screen images because the text editing of the forum seems to change my text entries. Sorry about that.

The text editing shouldn’t be changing your code.

There is a small symbol </> in the formatting bar specifically for code and any code you put into the question, please use that to format it. That makes the code readable and sets it apart from the rest of the text.

You can also include code in tick marks to make it stand out as well.

You can go back and edit your own post so I suggest doing that to correct errors when you see them.

To be clear, this var personResults: Results? = nil was not my suggestion (I put ticks around that). My code is (using the code format button)

var personResults: Results<PersonalData>? = nil

Stylistically my code suggestion makes it clear that a personResults objects is a Realm Results object containing PersonalData objects. It could be nil, and initially it is.

After an absence from programming for a couple weeks and a hardware problem, I have returned to this issue of being unable to discover why my Realm data files are not visible on my iPhone device. Since I had no experience in Primary Keys, I converted my 7 .swift data files to not include Primary Keys. I have 7 entirely new files with different names and I deleted the App from my iPhone. I got the same error basically when I tested the array following a load I still have no data on the iPhone. However, I do have the App and have the data structures as seen in the Realm Browser.

I discovered a process that would allow me to inspect the data files that reside on the iphone by connecting the iPhone to my Mac, and loading the application from a fresh start after deleting the App from the iPhone. I then rebuilt the app with the iPhone device as the target, it was connected and my test for good data still follows the exception process because the data files were empty.

This is how I looked at the data from Xcode on the Mac to the connected iPhone.

Xcode - Windows - Devices and Simulators . Choose your desired device and navigate to the downloaded app in the Installed App section. With the correct app highlighted, select the gear icon underneath and choose Download Container. There I found the Default Realm file and my swift file entries (all seven of the new named files). When I did this I was expecting to see an object in the file. I move the PersonData.swift file contents to an array (usersArray) and access the first entry. When I test the the count value I take the Else path and load to the variables the Sample Data and not the .name property value.

Here is the test of the data file:
usersArray = realm.objects(PersonData.self)

    if usersArray!.count > 0 {
        print(usersArray!.count)
        nameLabel.text = usersArray?[0].name
    } else {
        print(usersArray!.count)
        nameLabel.text = "Sample Data"
    }

When I run on the simulator the usersArray!.count value is returned as 1 and my .name data displays on the simulated iPhone view with the textfield loaded with the .name value from the db.
When I run on the iPhone device the usersArray!.count value is returned as 0 and the “Sample Data” appears on the view in the appropriate textfield.

Does anyone have any thoughts on why the simulator finds the data and the device has the structures of the data.swift files (showing it’s name and the properties defined) but the data does appear to be on the device?

What else can I show to the community. I am using the same swift statements I used in the Todoey application that I wrote in the London App Brewery course cirriculm.

After having a very gratifying and informative conversation with one of the community resources and talking through my code, the most obvious of all answers was apparent for my problem. Though I had initiated the database in the simulator, I had forgotten to do the same process with my teathered iPhone to initiate the values there. Of course I could have updated the sample data that was hard coded, I really wanted to understand the real problem. And the real problem was me. I believe this is solved but now I need to further discuss and understand whether to have primary keys in each Realm or not. This will take more study. But thank you Richard…