iOS required init crashing app

I’ve successfully completed the iOS programmatical app tutorial on MongoDB’s documentation. I’m currently trying to apply what I learned from this tutorial into my own app created in Xcode, and using storyboards. However, the following line of code will always crash the app when the respective view controller is called.

// Implement this overload of init to satisfy the UIViewController subclass requirement.
required init?(coder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}

Any help would greatly be appreciated.

That’s a tad brief to understand the use case. Can you expand that code a bit?

Where is it being called, at what point? Is that in a viewController or perhaps a subclass of some kind?

Do you know of any other MongoDB Realm tutorials elsewhere online that do not take the Programmatical approach? I can’t seem to find any.

Not sure what you’re asking.

MongoDB Realm is a database that requires writing code so your users can interact with your app. Are you asking about understanding database modeling or understanding programming concepts in general or something else entirely?

My apologies. I have hit a dead end developing my iOS app with MongoDB Realm. When I comment out the required init for realm, as well as the required init for NSCoder, along with the realm variables, I can display dummy data on a table view that I coded on the cellForRow at method. As soon as I uncomment both required inits, and the realm variables, the app crashes on the NSCoder required init. The required init for realm is pretty much a direct copy of the one I found in the Mongo Realm tutorial, with minor modifications to match the Realm collections in my Atlas account. What could be causing this?

@Hector_S Hey Hector - this initializer

// Implement this overload of init to satisfy the UIViewController subclass requirement.
required init?(coder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}

Does not have anything to do with Realm and is from UIKit which is a built in framework from iOS SDK - not sure how to help you here without more information

Hi Ian, thank you for your response. Currently, I am trying to build a simple test app on a FREE tier Atlas server using Realm and sync. I have created several documents inside the realm that contain: _id, _partition, name, and phonenumber as a test. I have an object class in swift that initializes those same variables. In addition to this, I have set up a view controller with a tableview with the intention of listing those documents in the realm, by means of the name property on each cell.

I’m aware of the fact that the initializer ultimately causing the crash is not required by realm and instead required by UIKit. It has been somewhat difficult to debug the app when the fatal error is thrown almost immediately and prior to any view appearing on the iOS simulator screen. If I comment out any variables ( partitionValue, realm, as well as well as the Results object variable), and then remove both initializers, the app does not crash. In fact, the app successfully connects to the realm as evidenced by the Realm logs and the Xcode console, which shows the following:

Login succeeded!
2020-09-18 07:27:54.537156-0400 App[1379:41058] Sync: Connection[1]: Session[1]: client_reset_config = false, Realm exists = true, async open = false, client reset = false
2020-09-18 07:27:54.623370-0400 App[1379:41058] Sync: Connection[1]: Connected to endpoint 'x.x.x.x:443' (from 'x.x.x.x:50530')
2020-09-18 07:27:55.067969-0400 App[1379:41058] Sync: Connection[1]: Disconnected

This tells me that the Atlas service, along with the Realm, including user authentication and Sync, are set up correctly. It is at this point where a tableview successfully appears with hard coded data for the two required tableview delegate methods. numberOfRowsInSection has a return value of 1, and cellForRowAt displays “Test” as the cell’s label.

There’s no easy way for me to debug anything beyond this. Due to the UIKit initializer crashing the app prior to the view controller’s view appearing on the screen, I am unable to determine what exactly is causing the crash. Perhaps the object Results variable is empty and it is causing the crash, though my level of expertise can’t ascertain that.

At this point, I’m going to re-read the tutorial on MongoDB Realm’s website for the umph time hoping to catch something I may be missing. It would be great if I could find a tutorial online for a similar app using storyboard and simple features, just a simple tableview app being populated by just a simple property, so that I can build upon that.

We can be a lot more help when you include code in your questions - you could have a typo or missing a required component needed to initialize your app property.

It may also be how you’re attempting to work with Realm asynchronously - that can be a complex process until you wrap your brain around it.

Usually a short, minimal amount of code is what’s recommended. The guide on the MongoDB Realm site does contain pretty much everything you need to know about init’ing your app.

I hope this isn’t too much code. My apologies if it is.

let partitionValue: String
let realm: Realm
let names: Results<Names>

var notificationToken: NotificationToken?

required init(projectRealm: Realm) {
    
    guard let syncConfiguration = projectRealm.configuration.syncConfiguration else {
        fatalError("Sync configuration not found! Realm not opened with sync?")
    }
    
    realm = projectRealm
    // After updating to RealmSwift10.0.0-beta.5, and Xcode to V12 
    partitionValue = (syncConfiguration.partitionValue?.stringValue!)!
    
    names = realm.objects(Names.self).sorted(byKeyPath: "_id")
    
    super.init(nibName: nil, bundle: nil)
    
    notificationToken = names.observe { [weak self] (changes) in
        guard let myTableView = self?.myTableView else { return }
        switch changes {
        case .initial:

            myTableView.reloadData()
        case .update(_, let deletions, let insertions, let modifications):

            myTableView.beginUpdates()

            myTableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0) }),
                with: .automatic)
            myTableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
                with: .automatic)
            myTableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
                with: .automatic)
            myTableView.endUpdates()

        case .error(let error):
            fatalError("\(error)")
        }
    }
}

It’s not clear what that code has to do with the code in the initial question. While it’s not too much code, its purpose is unclear.

You initially asked about

required init?(coder: NSCoder) {

but the code above appears to be unrelated and is attempting to add an observer to your realm objects.

I think you need to back up a step; does your app read and write data from realm at all. If yes, then it’s time to explore notifications (with the code included in the post).

If not, you need to address why not and examine how you’re initializing Realm in the first place.

This particular app has no real purpose, other than to apply what I learned in the MongoDB tutorial. As of right now, the only thing this app should do, is list the first name of each user. I am posting the contents of two files ViewController.swift, and Names.swift. I have entered data manually into the realm by means of the compass software. As of right now, the Realm is on Developer mode. Once I am able to correct the issue with this, I can continue to build upon this app and add more features

import UIKit
import RealmSwift

class ViewController: UIViewController {

@IBOutlet weak var myTableView: UITableView!

var partitionValue: String
let realm: Realm
let names: Results<Names>

var notificationToken: NotificationToken?

required init(projectRealm: Realm) {

    // Ensure the realm was opened with sync.
    guard let syncConfiguration = projectRealm.configuration.syncConfiguration else {
        fatalError("Sync configuration not found! Realm not opened with sync?")
    }

    realm = projectRealm

    // Partition value must be of string type.
    partitionValue = (syncConfiguration.partitionValue?.stringValue!)!

    // Access all tasks in the realm, sorted by _id so that the ordering is defined.
    // Only tasks with the project ID as the partition key value will be in the realm.
    names = realm.objects(Names.self).sorted(byKeyPath: "_id")

    super.init(nibName: nil, bundle: nil)

    notificationToken = names.observe { [weak self] (changes) in
        guard let myTableView = self?.myTableView else { return }
        switch changes {
        case .initial:
            // Results are now populated and can be accessed without blocking the UI
            myTableView.reloadData()
        case .update(_, let deletions, let insertions, let modifications):
            // Query results have changed, so apply them to the UITableView.
            myTableView.beginUpdates()
            // It's important to be sure to always update a table in this order:
            // deletions, insertions, then updates. Otherwise, you could be unintentionally
            // updating at the wrong index!
            myTableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0) }),
                with: .automatic)
            myTableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
                with: .automatic)
            myTableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
                with: .automatic)
            myTableView.endUpdates()
        case .error(let error):
            // An error occurred while opening the Realm file on the background worker thread
            fatalError("\(error)")
        }
    }
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

deinit {
     // Always invalidate any notification tokens when you are done with them.
     notificationToken?.invalidate()
 }

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    
    openRealm()
    
    }

func openRealm() {
    
    let email = "********"
    let password = "********"
    
    app.login(credentials: Credentials(email: email, password: password)) { [weak self](user, error) in
        // Completion handlers are not necessarily called on the UI thread.
        // This call to DispatchQueue.main.sync ensures that any changes to the UI,
        // namely disabling the loading indicator and navigating to the next page,
        // are handled on the UI thread:
        DispatchQueue.main.sync {
  
            guard error == nil else {
                // Auth error: user already exists? Try logging in as that user.
                print("Login failed: \(error!)");
               
                return
            }

            print("Login succeeded!");

            // Go directly to the Tasks page for the hardcoded project ID "My Project".
            // This will use a common project and demonstrate sync.
            let partitionValue = "********"

            // Open a realm.
            Realm.asyncOpen(configuration: user!.configuration(partitionValue: partitionValue)) { [weak self](realm, error) in
                guard let realm = realm else {
                    fatalError("Failed to open realm: \(error!.localizedDescription)")
                }
            }
        }
    }
}
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return names.count

}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let name = names[indexPath.row]
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") ?? UITableViewCell(style: .default, reuseIdentifier: "Cell")
    
    cell.textLabel!.text = name.firstname
    
    return cell
    }
}

The following are the contents of Names.swift.

import Foundation
import RealmSwift


class Names: Object {

    @objc dynamic var _id: ObjectId = ObjectId.generate()
    @objc dynamic var _partition: String = ""
    @objc dynamic var firstname: String = ""
    @objc dynamic var phonenumber: String = ""

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

}

At present, this app does not read nor write data from the realm as the app crashes instantly upon building on Xcode.

One question: is your UIViewController instantiated in code, or in a Storyboard (as the @IBOutlet seems to suggest)?

Thing is, the original Realm Swift tutorial creates the UI in code, hence the setup is done in init(projectRealm: …) when the initial view controller puts it on the screen, but, when a view controller is instantiated in a Storyboard, init(coder:…) is called instead, that ends up crashing the app (that’s what fatalError() does…)

It is perfectly possible to use Storyboards with Realm Swift, of course, but the app flow happens in a slight different way: I’ve done it in my own take of the tutorial, feel free to adapt it to your needs.

1 Like

The app crashes upon BUILDING or RUNNING?

You also may be missing a line of code - if so, then the projectRealm var is nil here

required init(projectRealm: Realm)

and the rest of the code will crash. This is probably where you want to call it from and pass in a valid realm:

// Open a realm.
Realm.asyncOpen(configuration: user!.configuration(partitionValue: partitionValue)) { [weak self](realm, error) in
   guard let realm = realm else {
      fatalError("Failed to open realm: \(error!.localizedDescription)")
    }
 }

Somewhere in that section you should be doing something with the realm object like this

guard let realm = realm else { return }
self!.navigationController!.pushViewController(
       TasksViewController(projectRealm: realm), animated: true
)

I’m using a viewController class TasksViewController but that would be whatever the name of your viewController is.

Yes. The app is built using storyboard. Your code is somewhat extensive, but I will try to look into it and see how I can apply it to my simple app. Thank you.

I deleted everything and started over from scratch. Using a storyboard, I have placed a table view that is connected to the view controller. The tableview has a prototype cell with an identifier of “Cell”. The tableview is referenced by means of an IBOutlet in the only view controller the app currently has. I have successfully built and run the app without any RealmSwift code, and it shows a few cells with a label of “Test”.

After adding all the Realm code, including the last bit of code you posted doesn’t help. Upon running the app, it crashes at the init(coder: NSCoder) line. Paolo_Manna is correct to say that a Storyboard app is setup differently, which is why I initially asked if there are any good Realm tutorials in Swift, using storyboards. On his last reply on this thread, he posted ‘TaskTraker2’, the tutorial app made with storyboards, but the code is extensive. I will have to look into his code and see how and what applies to my code.

Yes, your app will crash at that line - every time - because you’re telling it to.

init(coder

In fact you’ll see it has nothing to do with Realm. Create a new iOS App in XCode, single view and add this right after viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

run the app. Crash.

The crash is because that function is missing code (e.g. the function is not fully implemented). There are a number of options but try this

required init ?(coder aDecoder: NSCoder) {
   super.init(coder: aDecoder)
}

Also, and this is just IMO, you’re got a LOT of disciplines to deal with in how you’re going about creating your simple app. It’s actually very complex! You’re dealing with Swift, tableViews, custom cells, Realm, Syncing, Notifications etc. That’s a lot to work through simultaneously.

I would suggest simplifying and tackle one task at a time. So for example, loose the notifications. You don’t need that initially. Just focus on getting sync working and reading the Realm objects and understanding partitions. Add notifications later. Again, just IMO and my .02.