Realm Transaction without notifying tokens - swift

I have Realm notifications on a background thread created with the following code (taken from Realm's website)
class BackgroundWorker: NSObject {
private let name: String
private var thread: Thread!
private var block: (()->Void)!
init(name: String) {
self.name = name
}
#objc internal func runBlock() {
block()
}
internal func start(_ block: #escaping () -> Void) {
self.block = block
if thread == nil {
createThread()
}
perform(
#selector(runBlock),
on: thread,
with: nil,
waitUntilDone: false,
modes: [RunLoop.Mode.default.rawValue]
)
}
private func createThread() {
thread = Thread { [weak self] in
while (self != nil && !self!.thread.isCancelled) {
RunLoop.current.run(
mode: RunLoop.Mode.default,
before: Date.distantFuture)
}
Thread.exit()
}
thread.name = name
thread.start()
}
func stop() {
thread.cancel()
}
}
And using the background worker like this
struct RealmBackGroundWorker {
static var tokens: [NotificationToken] = []
static let backgroundWorker = BackGroundWorker(name: "RealmWorker")
static func start() {
backgroundWorker.start {
self.tokens = ...
}
}
}
The background notifications work great. But I often need to save data to realm without notifying these transactions. From what I have found, it does not look like there is a way write data without notifying all tokens. You always have to specify the tokens you want to ignore.
How can I write data to the Realm without notifying these background tokens?

Let me preface this answer with a couple of things. The Realm website the OP got their code from was here Realm Notifications on Background Threads with Swift and part of the point of that code was to not only spin up a runloop on a background thread to handle Realm functions but to also handle notifications on that same thread.
That code is pretty old - 4+ years and is somewhat outdated. In essence, there are possibly better options. From Apple:
... newer technologies such as Grand Central Dispatch (GCD) provide a
more modern and efficient infrastructure for implementing concurrency
But to address the question, if an observer is added to a Realm results on thread A, then all of the notifications will also occur on thread A. e.g. the token returned from the observe function is tied to that thread.
It appears the OP wants to write data without receiving notifications
I do not want to sync local changes to the server, so I would like to
call .write(withouNotifying: RealmWorkBlock.tokens)
and
I want a way to write data to the realm database without notifying
these notifications.
Noting that those notifications will occur on the same thread as the runloop. Here's the code that we need to look at
static func start() {
backgroundWorker.start {
self.tokens = ...
}
and in particular this line
self.tokens = ...
because the ... is the important part. That ... leads to this line (from the docs)
self?.token = files.observe { changes in
which is where the observer is added that generates the notifications. If no notifications are needed then that code, starting with self?.token can be completely removed as that's is sole purpose - to generate notifications.
One thought is to add a different init to the background worker class to have a background worker with no notifications:
static func start() {
backgroundWorker.startWithoutNotifications()
}
Another thought is to take a more modern approach and leverage DispatchQueue with an autorelease pool which eliminates the need for these classes completely, will run in the background freeing up the UI ad does not involve tokens or notifications.
DispatchQueue(label: "background").async {
autoreleasepool {
let realm = try! Realm()
let files = realm.objects(File.self).filter("localUrl = ''")
}
}

Related

SwiftUI #Published and main thread

Could someone explain why I get this warning: Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
I'm know that if I wrap the changes in DispatchQueue.main.async the problem goes away. Why does it happen with some view modals and not others? I thought that since the variable has #Published it's automatically a publisher on main thread?
class VM: ObservableObject {
private let contactsRepo = ContactsCollection()
#Published var mutuals: [String]?
func fetch() {
contactsRepo.findMutuals(uid: uid, otherUid: other_uid, limit: 4) { [weak self] mutuals in
guard let self = self else { return }
if mutuals != nil {
self.mutualsWithHost = mutuals // warning...
} else {
self.mutualsWithHost = []
}
}
}
}
Evidently, contactsRepo.findMutuals can call its completion handler on a background thread. You need to ward that off by getting back onto the main thread.
The #Published property wrapper creates a publisher of the declared type, nothing more. The documentation may be able to provide further clarity.
As for it happening on some viewModels and not others, we wouldn't be able to tell here as we don't have the code. However it's always best practice to use DispatchQueue.main.async block or .receive(on: DispatchQueue.main) modifier for combine as you've already figured out when updating your UI.
The chances are your other viewModel is already using the main thread or the properties on the viewModel aren't being used to update the UI, again without the code we'll never be sure.

Swift: Thread safe Singleton, why do we use sync for read?

While making a thread-safe Singleton, it is advised to use a sync for read and an async with a barrier for write operation.
My question is why do we use a sync for read? What might happen if we perform a read with async operation?
Here is an example of what is recommended:
func getUser(id: String) throws -> User {
var user: User!
try concurrentQueue.sync {
user = try storage.getUser(id)
}
return user
}
func setUser(_ user: User, completion: (Result<()>) -> Void) {
try concurrentQueue.async(flags: .barrier) {
do {
try storage.setUser(user)
completion(.value(())
} catch {
completion(.error(error))
}
}
}
The concept of using concurrent queue with “read concurrently with sync; write with barrier with async” is a very common synchronization pattern called “reader-writer”. The idea is that the concurrent queue is just for synchronizing writes with a barrier, but that reads will take place concurrently with respect to other reads.
So, here’s a simple, real-world example of using reader-writer for synchronized access to some private state property:
enum State {
case notStarted
case running
case complete
}
class ComplexProcessor {
private var readerWriterQueue = DispatchQueue(label: "...", attributes: .concurrent)
// private backing stored property
private var _state: State = .notStarted
// exposed computed property synchronizes access using reader-writer pattern
var state: State {
get { readerWriterQueue.sync { _state } }
set { readerWriterQueue.async { self._state = newValue } }
}
func start() {
state = .running
DispatchQueue.global().async {
// do something complicated here
self.state = .complete
}
}
}
Consider:
let processor = ComplexProcessor()
processor.start()
And then, later:
if processor.state == .complete {
...
}
The state computed property is using the reader-writer pattern to offer thread-safe access to the underlying stored property. It synchronizes access to some memory location, and we are confident that it will be responsive. In this case, we don’t need confusing #escaping closures: The sync reads result in very simple code that is easy to reason about.
That having been said, in your example, you’re not just synchronizing interaction with some property, but synchronizing the interaction with storage. If that’s local storage that is guaranteed to be responsive, then the reader-writer pattern is probably fine.
But if storage methods could take anything more than a few milliseconds to run, you wouldn’t want to use the reader-writer pattern. The fact that getUser can throw errors makes me wonder if storage is already doing complicated processing. And even if it is just reading quickly from some local store, what if it was later refactored to interact with some remote store, subject to unknown network latency/issues? Bottom line, it is questionable to have the getUser method making assumptions about implementation details of storage, assuming that the value will always be returned quickly.
In that case, you would refactor getUser method to use #escaping completion handler closure, as suggested by Jeffery Thomas. We never want to have a synchronous method that might take more than a few milliseconds, because we never want to block the calling thread (especially if it’s the main thread).
By the way, if you stay with reader-writer pattern, you can simplify your getUser, because sync returns whatever value its closure returns:
func getUser(id: String) throws -> User {
return try concurrentQueue.sync {
try storage.getUser(id)
}
}
And you can’t use try in conjunction with async (only within your do-catch block). So it’s just:
func setUser(_ user: User, completion: (Result<()>) -> Void) {
concurrentQueue.async(flags: .barrier) {
do {
try storage.setUser(user)
completion(.value(())
} catch {
completion(.error(error))
}
}
}
It's all in what you want. By changing get user to async, then you need to use a callback to wait for the value.
func getUser(id: String, completion: #escaping (Result<User>) -> Void) -> Void {
concurrentQueue.async {
do {
let user = try storage.getUser(id)
completion(.value(user))
} catch {
completion(.error(error))
}
}
}
func setUser(_ user: User, completion: #escaping (Result<()>) -> Void) {
concurrentQueue.async(flags: .barrier) {
do {
try storage.setUser(user)
completion(.value(()))
} catch {
completion(.error(error))
}
}
}
That changes the API of get user, so now when calling get user, a callback will need to be used.
Instead of somethings like this
do {
let user = try manager.getUser(id: "test")
updateUI(user: user)
} catch {
handleError(error)
}
you will need something like this
manager.getUser(id: "test") { [weak self] result in
switch result {
case .value(let user): self?.updateUI(user: user)
case .error(let error): self?.handleError(error)
}
}
Assuming you have somethings like a view controller with a property named manager and methods updateUI() and handleError()

How should I guarantee fetch results from a different thread in a nested contexts are up to date, when saves are done asynchronously in background?

I've read the following Behavior differences between performBlock: and performBlockAndWait:?
But wasn't able to find an answer to my question.
The following code is picked up from an RayWenderlich video. Specifically at 10:05 the code is something like this:
class CoreDataStack {
var coordinator : NSPersistentStoreCoordinator
init(coordinator: NSPersistentStoreCoordinator){
self.coordinator = coordinator
}
// private, parent, in background used for saving
private lazy var savingContext : NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
return moc
}()
lazy var mainManagedObjectedContext : NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
moc.parent = self.savingContext
return moc
}()
func saveMainContext() {
guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
return
}
mainManagedObjectedContext.performAndWait {
do {
try mainManagedObjectedContext.save()
}catch let error{
fatalError(error.localizedDescription)
}
}
savingContext.perform {
do {
try self.savingContext.save()
}catch let error{
fatalError(error.localizedDescription)
}
}
}
}
From what I understand what happens is that the main context just passes the changes to its parent context which is a private, background context. It does this synchronously.
Then the parent, private context, does the actual saving against sqlite in a background thread asynchronously. Long story short this helps us a lot with performance. But what about data integrity?!
Imagine if I was to do this:
let coredataManager = CoreDataStack()
coredataManager.saveMainContext() // save is done asynchronously in background queue
coredataManager.mainManagedObjectedContext.fetch(fetchrequest)
How can I guarantee that my fetch is reading the most recent and updated results?
If we do our writes asynchronously then isn't there a chance that another read at the same time could end up with unexpected results ie results of the save changes could or could not be there?
EDIT:
I've made an improvement with the code below. I can make my save take in a completionHandler parameter. But that doesn't resolve the entire problem. What if I'm making a fetchRequest from a mainQueue somewhere else that isn't aware that a save is happening at the same time?
enum SaveStatus{
case noChanges
case failure
case success
}
func saveMainContext(completionHandler: (SaveStatus -> ())) {
guard savingContext.hasChanges || mainManagedObjectedContext.hasChanges else {
completionHandler(.noChanges)
return
}
mainManagedObjectedContext.performAndWait {
do {
try mainManagedObjectedContext.save()
}catch let error{
completionHandler(.failure)
fatalError(error.localizedDescription)
}
}
savingContext.perform {
do {
try self.savingContext.save()
completionHandler(.succes)
}catch let error{
completionHandler(.failure)
fatalError(error.localizedDescription)
}
}
}
All calls to mainManagedObjectContext will be synchronous and therefore blocking. If you call saveMainContext() and immediately afterwards call mainManagedObjectedContext.fetch(fetchrequest), the fetch request will not go through until the save request is completed, even if the save/fetch requests come from different queues (see the paragraph on FIFO in your link above).
When you perform a fetch request, you aren't pulling from the persistent storage - you're pulling from the child container, whom you just updated. You don't need to wait for the changes to be committed to the persistent storage, since you aren't accessing the data from there. The child container will give you the latest changes.
The child container is a container - it will hold your latest changes in memory (as opposed to stored on the disk - that is the persistent container's job).
The real issue here is that your CoreDataStack should implement the singleton pattern to prevent instantiating multiple versions of the same containers (that would still technically be on the same thread and therefore serialized, but accessing the containers wouldn't be thread safe). In other words, each time you instantiate CoreDataStack(), you're creating a new savingContext and mainManagedObjectedContext.
Instead, instantiate it just once.
class CoreDataStack {
var coordinator: NSPersistentStoreCoordinator
public static let sharedInstance = CoreDataStack()
private override init() {
self.coordinator = NSPersistantStoreCoordinator()
}
...
rest of your code here
...
}
And call like this:
CoreDataStack.sharedInstance.saveMainContext()
(See this link re: 'does the child have the same objects as the parent?')
The only case where a child would not be synced up with the parent is where you have multiple children accessing the same parent - but that doesn't seem to be the case here.
The question isn't specific to core-data.
It's the classic read-write question.
The common approach with protecting a datasource is to access your datasource using a serial queue. Otherwise yeah without the serial queue you will have a read-write problem.
In the following example:
let coredataManager = CoreDataStack() // 1
coredataManager.saveMainContext() // 2 save is done asynchronously in background queue
coredataManager.mainManagedObjectedContext.fetch(fetchrequest) // 3
coredataManager is to be accessed from a serial queue. So even if the write in the 2nd line is done asynchronously, the read at line 3, will have to wait until the serial queue is unblocked.

Can NotificationTokens be held outside of the thread that they are made within?

(I wasn't able to find this information in the documentation, or don't know where to look, so forgive me if this is already been explained somewhere (link would be helpful).
My app creates and uses NSThreads to interact with Realm. All threads have working Run Loops set up on them so the Realms created on them will autorefresh.
One thread, called ReadThread, is used by the app by different modules to set up notification tokens so that they can receive updates and do some processing without blocking the main thread.
Example (Pseudocode:
ReadThread {
func performAsync(_ block: ()->Void) {
// execute block on run loop of the thread using self.perform(#selector(), on: self)
}
}
Singleton {
let readThread: ReadThread()
init {
self.readThread.start()
}
}
Main Thread:
Class A {
private var token: NotificationToken?
init {
Singleton.readThread.perform {
let token = realm.observe() { [weak self] (notification: Realm.Notification, realm) in
self?.doWork()
DispatchQueue.main.async { [weak self] in
self?.notifyUI()
}
}
DispatchQueue.main.async {[weak self] in self?.token = token }
}
}
}
The idea is that the token is created on the ReadThread, but the token is stored in an instance variable on a different thread (main thread). Is the token thread-safe enough that the main thread objects can at lease call invalidate() on the token, or if the main thread object is deallocated, the token will be automatically invalidated?
Thanks for your help!
Learned the hard was, but the answer is no, the omens must be invalidated under the same thread that the token was generated under. Otherwise, a runtime exception will be thrown by Realm.verifyThread check.

RxSwift state changes trigger "Warning: Recursive call or synchronization error!"

I've inherited some Swift 3 code which uses RxSwift to manage a store. The basic layout of the class is:
class UserActivityStore {
fileprivate lazy var internalDataCache: Variable<Set<NSUserActivity>?> = Variable(nil)
func addAction(_ actionToAdd: NSUserActivity) {
var content = internalDataCache.value ?? Set<NSUserActivity>()
content.insert(actionToAdd)
internalDataCache.value = content
}
func resolveAction(_ action: NSUserActivity) {
var content = internalDataCache.value
_ = content?.remove(action)
internalDataCache.value = content
}
func expectActions(_ filter: #escaping ((NSUserActivity) -> Bool)) -> Observable<NSUserActivity> {
let observable = Observable<NSUserActivity>.create { (observer) -> Disposable in
return self.internalDataCache.asObservable().subscribeNext { (newActions) in
guard let newActions = newActions else { return }
for eachAction in newActions where filter(eachAction) {
observer.onNext(eachAction)
self.resolveAction(eachAction)
}
}
}
return observable
}
}
When an action is added to this, it adds the item to the set correctly. However, the observer (in expectActions) catches that change and resolves it. Since this is all in a single thread, the error "Warning: Recursive call or synchronization error!" is triggered.
I think this is a perfectly legitimate error and that RxSwift is probably correct in its handling. However, I can't help thinking that this is a bad model. The code is essentially handling a queue of NSUserActivity objects itself.
Is this genuinely a modelling error / abuse of RxSwift or is my limited understanding of RxSwift misunderstanding this? As a hacky solution, I've tried replacing the resolveAction function with a single line internalDataCache.value?.remove(action) but that still triggers the observable and hence the bug.
Changing the observable to use a different queue (Serial or Concurrent dispatch) fixes the problem but I'm not convinced its the correct fix.