TestsTested | ✓ |
LangLanguage | SwiftSwift |
License | MIT |
ReleasedLast Release | Feb 2018 |
SPMSupports SPM | ✗ |
Maintained by ukitaka.
RealmIO
makes Realm
operation more safely, reusable and composable by using reader monad
.
Realm operations (especially write
operations) is not reusable if you write a function as follows:
func addDog(name: String) throws {
let realm = try Realm()
try realm.write {
let dog = Dog()
dog.name = name
realm.add(dog)
}
}
At first glance, It works well, but actually there are some problems if you call this function multiple times.
addDog(name: "Taro")
addDog(name: "Jiro")
realm.write
is called twice.realm.write
locks realm instance. We should not call realm.write
needlessly.You can also write this function as follows:
func addDog(name: String, to realm: Realm) {
let dog = Dog()
dog.name = name
realm.add(dog)
}
try realm.write {
addDog(name: "Taro", to: realm)
addDog(name: "Jiro", to: realm)
}
2 addDog
calls will be run in a same transaction, but user needs to call realm.write
by oneself.
Realm
instance as argument explicitly.RealmIO
RealmIO<RW, T>
represents a realm operation.
RW
is actually ReadOnly
or ReadWrite
. It represents that operation is readonly or not.T
is a return value type.and you can also use RealmRO<T>
and RealmRW<T>
, these are just alias of RealmIO<ReadOnly, T>
and RealmIO<ReadWrite, T>
.
public typealias RealmRO<T> = RealmIO<ReadOnly, T>
public typealias RealmRW<T> = RealmIO<ReadWrite, T>
For example, operation that reads User
object from realm is typed RealmRO<User>
.
func find(by userID: Int) -> RealmRO<User> {
...
}
If you already know about reader monad
, RealmIO<RW, T>
is the same as Reader<Realm, T>
, except for the RW
type parameter.
realm.run(io:)
You can run preceding realm operation with realm.run(io:)
.
let io: RealmRO<User> = find(by: 123)
let result = try? realm.run(io: io)
If operation needs to write to realm (it means io
is an instance of RealmRW<T>
),
realm.run(io:)
begins transaction automatically.
realm.run(io:)
throws 2 error types.
Realm.Error
flatMap
flatMap
allows you to compose realm actions.
func add(dog: Dog) -> RealmRW<Void> {
...
}
func add(cat: Cat) -> RealmRW<Void> {
...
}
let io: RealmRW<Void> = add(dog: myDog).flatMap { _ in add(cat: myCat) }
And you can run composed operation in a same transaction.
realm.run(io: io) // Add `myDog` and `myCat` in a same transaction.
RW
type parameter of composed operation is determined by 2 operation types.
read.flatMap { _ in read } // ReadOnly
read.flatMap { _ in write } // ReadWrite
write.flatMap { _ in read } // ReadWrite
write.flatMap { _ in write } // ReadWrite
Realm.IO
provides useful operators to create RealmIO
instance.
See: Realm+Operator.swift
RealmIO
are not thread safe yet.Some methods that takes Object
as an argument such as Realm.IO.add
, Realm.IO.delete
are not thread safe for now.
It is not better to pass Object
directly. If you want to use this method safely, you should call realm.run(io:)
in a same thread, or use with flatMap
.
// OK: call `realm.run(io:)` in a same thread.
let io1 = Realm.IO.add(object)
try realm.run(io: io1)
// OK: use with `flatMap`
let io2 = Realm.IO.objects(Dog.self).flatMap(Realm.IO.delete)
try realm.run(io: io2)
Since ThreadSafeReference
has a constraint that references can not be resolved within write transactions, implementation with ThreadSafeReference
can not be done in 2.0. I'm considering measures after the next version.
RealmIO
supports following platforms.
RealmIO
was inspired by Slick's DBIOAction
.