Why does Realm use try! in Swift?

13,997

Solution 1

If you're referring to the examples in the Realm Swift Docs, I suspect try! is used liberally for the sake of brevity. The user is given a quick and dirty overview of core concepts without too much mental overhead.

You probably will encounter errors at some point in your journey using Realm. You'll notice later on in the docs, in the Realms > Error Handling section that a do-catch example is given.

do {
  let realm = try Realm()
} catch let error as NSError {
  // handle error
}

To me, it's implied that the code examples from the docs are not necessarily production-quality, and the user is encouraged to use the relevant error-handling features of Swift.

Solution 2

From the Realm Swift 2.1.0 guide in the Writes section:

Because write transactions could potentially fail like any other disk IO operations, both Realm.write() & Realm.commitWrite() are marked as throws so you can handle and recover from failures like running out of disk space. There are no other recoverable errors. For brevity, our code samples don’t handle these errors but you certainly should in your production applications.

Source: https://realm.io/docs/swift/latest/#writes

Solution 3

The way I deal with this issue is by creating a DatabaseManager class, that handles the unlikely event of realm throwing an error:

public class DatabaseManager {

    static var realm: Realm {
        get {
            do {
                let realm = try Realm()
                return realm
            }
            catch {
                NSLog("Could not access database: ", error)
            }
            return self.realm
        }
    }

    public static func write(realm: Realm, writeClosure: () -> ()) {
        do {
            try realm.write {
                writeClosure()
            }
        } catch {
            NSLog("Could not write to database: ", error)
        }
    }
}

Thanks to that solution the code looks much cleaner whenever I want to read from realm or write to db :)

DatabaseManager.write(realm: realm) {
    let queryResult = self.realm.objects(Cookies.self).filter("cookieId == %@", cookieId)
    let cookie = queryResult.first
    cookie?.expirationDate = expirationDate as NSDate?
}

Solution 4

Why creating a class with static func when we can create an extension of Realm?

extension Realm {
    static func safeInit() -> Realm? {
        do {
            let realm = try Realm()
            return realm
        }
        catch {
            // LOG ERROR
        }
        return nil
    }

    func safeWrite(_ block: () -> ()) {
        do {
            // Async safety, to prevent "Realm already in a write transaction" Exceptions
            if !isInWriteTransaction {
                try write(block)
            }
        } catch {
            // LOG ERROR
        }
    }
}

Usage Example

Old unsafe code:

let realm = try! Realm()
try! realm.write {
    // Your write transaction body
}

Safety refactor with this extension:

guard let realm = Realm.safeInit() else {
    // Track Error
    return 
}
realm.safeWrite {
   // Your write transaction body as before
}

Solution 5

From the Realm documentation:

You may have noticed so far that we have initialized access to our realm variable by calling Realm(). That method returns a Realm object that maps to a file called “default.realm” under the Documents folder (iOS) or Application Support folder (OS X) of your app.

Any time you interact with the file system you risk encountering errors such as permissions problems or insufficient disk space. Success is not certain.

So if for any reason Realm is unable to create or write to the realm file, these methods you cite would indeed throw an exception.

Share:
13,997
Jason Leach
Author by

Jason Leach

Updated on June 11, 2022

Comments

  • Jason Leach
    Jason Leach almost 2 years

    Why Does Realm use try! so frequently? It seems like if you're certain your call won't fail then you should not design it to throw - no?

    Here is an example, from the Swift page on realm.io:

    // Get the default Realm
    let realm = try! Realm()
    

    or

    // Persist your data easily
    try! realm.write {
      realm.add(myDog)
    }
    

    To me this implies they will never fail so why have the constructor or write() throw?