Skip to content

Instantly share code, notes, and snippets.

@srstanic
Created March 15, 2022 14:55
Show Gist options
  • Save srstanic/5b0a3cb5225c1b9fb95d4bdd2f296ff5 to your computer and use it in GitHub Desktop.
Save srstanic/5b0a3cb5225c1b9fb95d4bdd2f296ff5 to your computer and use it in GitHub Desktop.
RealmMultiThreadingStaleDataTests.swift
import XCTest
import Combine
import RealmSwift
class RealmMultiThreadingStaleDataTests: XCTestCase {
private var cancellables: Set<AnyCancellable>!
override func setUpWithError() throws {
super.setUp()
cancellables = []
}
override func tearDownWithError() throws {
super.tearDown()
cancellables = nil
}
func testWriteOnBackgroundReadOnMain() throws {
let expectation = XCTestExpectation(description: "Expected to receive the right person.")
let personId1 = ObjectId.generate()
let personId2 = ObjectId.generate()
let realmQueue = DispatchQueue(label: "realmQueue", qos: .background)
getRealm(on: realmQueue)
.flatMap { realm -> AnyPublisher<Void, Error> in
let person = RealmPerson()
person.id = personId1
return realm.save(object: person)
.map { _ in () }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
.flatMap { [self] _ in self.getRealm(on: realmQueue) }
.flatMap { realm -> AnyPublisher<Void, Error> in
let person = RealmPerson()
person.id = personId2
return realm.save(object: person)
.map { _ in () }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
.flatMap { [self] _ in self.getRealm() }
.flatMap { realm -> AnyPublisher<RealmPerson?, Error> in
print("getting person with id: \(personId1)")
return realm.getTypedObject(objectId: personId1.stringValue)
.eraseToAnyPublisher()
}
.sink(receiveCompletion: { event in
if case .failure(let error) = event {
XCTFail("Received error: \(error)")
}
}) { personResult in
XCTAssertEqual(personId1.stringValue, personResult?.id.stringValue)
expectation.fulfill()
}
.store(in: &cancellables)
wait(for: [expectation], timeout: 30)
}
private func getRealm(on queue: DispatchQueue = .main) -> AnyPublisher<Realm, Error> {
return Just(())
.subscribe(on: queue)
.flatMap { [name] _ -> AnyPublisher<Realm, Error> in
guard let realm = Realm.instance(withIdentifier: name) else {
print("dbgt: Failed to get realm instance")
return Fail<Realm, Error>(error: CustomError()).eraseToAnyPublisher()
}
realm.refresh()
return Just(realm)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
}
.eraseToAnyPublisher()
}
}
@objcMembers final class RealmPerson: Object {
dynamic var id: ObjectId = .generate()
dynamic var name: String = ""
public override static func primaryKey() -> String? {
return "id"
}
}
struct CustomError: Error {}
extension Realm {
static func configuration(withIdentifier identifier: String) -> Configuration {
var config = Realm.Configuration.defaultConfiguration
config.inMemoryIdentifier = identifier
return config
}
static func instance(withIdentifier identifier: String) -> Realm? {
let config = configuration(withIdentifier: identifier)
do {
return try Realm(configuration: config)
} catch {
return nil
}
}
func save<RealmType: Object>(object: RealmType) -> Future<RealmType, Swift.Error> {
Future { promise in
do {
try write {
add(object, update: .modified)
}
promise(.success(object))
} catch {
promise(.failure(error))
}
}
}
func getTypedObject<RealmType: Object>(objectId: String) -> Future<RealmType?, Swift.Error> {
return Future { promise in
do {
if let realmObject = try getFirstById(objectId: objectId, objectType: RealmType.self) {
let result = realmObject.freeze()
promise(.success(result))
} else {
promise(.success(nil))
}
} catch {
promise(.failure(error))
}
}
}
func getFirstById<RealmType: Object>(objectId: String, objectType: RealmType.Type) throws -> RealmType? {
let realmId = try? ObjectId(string: objectId)
if let result = objects(RealmType.self)
.filter("\(RealmType.primaryKey() ?? "id") = %@", realmId ?? objectId)
.first
{
return result
}
return nil
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment