-
-
Notifications
You must be signed in to change notification settings - Fork 906
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Realm does not work with Async Specs #1218
Comments
Fascinating.
That shouldn't be the case here. This move only affects how tests get created & turned into instance methods that I'd love to see additional context here. I spent some time trying to recreate this. Admittedly, I haven't used realm before, so this is based entirely on their Quickstart guide, but I'm not running into any errors, and this test passes. import RealmSwift
import Quick
import Nimble
import XCTest
import Combine
final class SampleSpec: AsyncSpec {
override class func spec() {
var realm: Realm!
var thing: Obj!
var failure: XCTestExpectation!
var finished: XCTestExpectation!
var value: XCTestExpectation!
var cancellable: AnyCancellable?
beforeEach {
Realm.Configuration.defaultConfiguration.inMemoryIdentifier = current.name
realm = try! Realm()
thing = Obj()
try! realm.write {
realm.add(thing)
}
failure = current.expectation(description: "Invalid.failure")
failure.isInverted = true
finished = current.expectation(description: "Invalid.failure")
value = current.expectation(description: "Invalid.failure")
}
afterEach {
await MainActor.run {
current.waitForExpectations(timeout: 10)
}
}
it("works?") {
cancellable = thing.publisher
.sink(receiveCompletion: { (completion: Subscribers.Completion<Never>) -> Void in
switch completion {
case .failure:
failure.fulfill()
case .finished:
finished.fulfill()
}
return
}, receiveValue: { isFavorite in
value.fulfill()
})
}
}
}
class Obj: Object {
@Persisted var name: String = ""
} |
I haven't heard back in over a month on this. I'm going to close this as unable to reproduce. |
@younata Apologies for the delay. I was out for a while and unable to work on this. But thank you for your code sample! I edited here so to reproduce the error: import RealmSwift
import Quick
import Nimble
import XCTest
import Combine
// swiftlint:disable all
final class SampleSpec: AsyncSpec {
override class func spec() {
var realm: Realm!
var thing: Obj!
var failure: XCTestExpectation!
var finished: XCTestExpectation!
var value: XCTestExpectation!
var cancellable: AnyCancellable?
beforeEach {
Realm.Configuration.defaultConfiguration.inMemoryIdentifier = current.name
realm = try! Realm()
thing = Obj()
try! realm.write {
realm.add(thing)
}
failure = current.expectation(description: "Invalid.failure")
failure.isInverted = true
finished = current.expectation(description: "Invalid.failure")
value = current.expectation(description: "Invalid.failure")
}
afterEach {
await MainActor.run {
current.waitForExpectations(timeout: 10)
}
cancellable?.cancel()
}
it("works?") {
cancellable = realm.objects(Obj.self)
.collectionPublisher
.sink(receiveCompletion: { completion -> Void in
switch completion {
case .failure:
failure.fulfill()
case .finished:
finished.fulfill()
}
return
}, receiveValue: { isFavorite in
value.fulfill()
})
}
}
}
class Obj: Object {
@Persisted var name: String = ""
} Long story short, if you use Let me know if that helps with reproducing it! |
I seem to be unable to reopen this issue. Should I create a new issue, or is commenting on here sufficient, @younata? |
Odd. Well, I just re-opened this. Thanks for the extra details, and I'll look at this soon. |
friendly ping @younata :) |
Thanks for the ping! This is... fascinating. I still don't have a full understanding of what's going on (why is Realm getting a null RunLoopMode for the current runloop?), but this is definitely an issue with using Realm from background threads (as this exception is not thrown if you use a This is also an issue in Quick 6 (but not an issue in Quick 5 - because Quick 5 tests only run on the main thread), and also in XCTest if you use async tests. Leading me to believe that this is not so much a Quick issue as a Realm issue, in particular with Realm not working well with Swift Concurrency. For the time being, I'll say that you can work around this issue by only accessing the realm from the main thread. Either by using a |
Thanks for the workaround suggestion! I'm not sure if I quite understand what you mean, though. If I change the above example to use QuickSpec, then I get on I'm also not entirely sure what to apply |
What did you do?
Upgraded from Quick 6.1.0 to Quick 7.0.0.
Trying to listen on a Combine Publisher from Realm, and getting the error
Thread 5: "Can only add notification blocks from within runloops."
What did you expect to happen?
Runs normally as with Quick 6.0
What actually happened instead?
Thread 5: "Can only add notification blocks from within runloops."
Environment
List the software versions you're using:
Target: x86_64-apple-macosx13.0 (Open Xcode Preferences; Components > Toolchains. If none, use
Xcode Default
.)Please also mention which package manager you used and its version. Delete the
other package managers in this list:
pod --version
in Terminal)Project that demonstrates the issue
This will error on the
.sink(receiveCompletion: { (completion: Subscribers.Completion<ErrorType>) -> Void in
line saying that RealmCan only add notification blocks from within runloops.
.I can give a more complete example if you need, but my guess is, the move of
spec()
from an instance method to a class method meant that the thread running the test no longer has a run loop?I tried debugging a bit on my own, and I found some resources that may or may not be related:
https://academy.realm.io/posts/realm-notifications-on-background-threads-with-swift/
https://github.com/Quick/Nimble/pull/549/files
The text was updated successfully, but these errors were encountered: