NSManagedObjectContext.performBlockAndWait() runs the block on main thread/queue - ios

print("queue1: \(NSOperationQueue.currentQueue())")
managedObjectContext.performBlockAndWait({
print("queue2: \(NSOperationQueue.currentQueue())")
})
and output is:
queue1: Optional(<NSOperationQueue: 0x7fa31c030cf0>{name = 'NSOperationQueue 0x7fa31c030cf0'})
queue2: Optional(<NSOperationQueue: 0x7fa319d114d0>{name = 'NSOperationQueue Main Queue'})
So, performBlockAndWait runs the block in main thread. Don't know why?
Is this the expected behaviour?
So, any solutions for the problems:
I need to run the code of the block in background, else it freezes UI. So, how can I?, or
If I don't use performBlockAndWait the code in the block crash with the description Terminating app due to uncaught exception NSInvalidArgumentException, reason _referenceData64 only defined for abstract class. Any alternative?

PerformBlock() and performBlockAndWait() runs on the queue in which your context is created. So if your managedObjectContext is created in main queue it will run in the main queue.
If you want background execution you need to create a background context :
let privateContext = NSManagedObjectContext(
concurrencyType: .PrivateQueueConcurrencyType)
privateContext.persistentStoreCoordinator = mainQueueContext.persistentStoreCoordinator
privateContext.performBlock {
...
}

Perform your task you want to do in background in a separate function with a completion handler.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
func performFuncWithCompletion() { (Success) in
if (Success)
NSOperationQueue.mainQueue().addOperationWithBlock {
// update UI here
}
else {
// Show error message?
}
}
}
func performFuncWithCompletion(completion : (SuccessStatus: Bool)) {
// perform your back ground function here.
// Decide if everything went fine
if everythingWentFine {
completion(true)
}
else {
completion(false)
}
}
Ask if any questions

Related

Blocking of Main Thread in Firebase Chat Listener "Save" Operation in Swift

I am using Firebase Chat in Swift 5.0 When I do get all the messages
from the local database then if the number of messages are larger then
this firebase message listener call-backs blocking the Main Thread for sometime due to save operation.
I have already tried adding the background thread on code but it seems not to be worked. Below is my some code snippets.
DispatchQueue.global(qos: .background).async {
self.setChatMessageListener()
}
// this call is already done on background thread
func setChatMessageListener () {
removeChatMessageListener()
FirebaseUtils.instance.getChatMessages(chatMasterId: chatMasterId).observe(.childAdded) {
(snapshot) in
// I want this call to be done on background thread how can i do that?
// this get called for each message so main thread is blocking here
self.updateChatMessage(snapshot: snapshot)
}
}
func updateChatMessage (snapshot: DataSnapshot) {
if let chatMessage = getChatMessage(snapshot: snapshot) {
DatabaseManager.getInstance().getDatabaseWriteManager().saveData(realmObject: chatMessage) {
(success) in
self.loadMessages(chatMessageId: chatMessage.chatMessageId)
}
}
else {
self.loadMessages()
}
}
How could i make self.updateChatMessage() operation on background thread?

main thread is NULL in swift even though I specified running on main thread

I am fetching Contact on myphone Using that code
DispatchQueue.main.async {
let allowedCharset = CharacterSet
.decimalDigits
let store = CNContactStore()
//store.requestAccess(for: .contacts, complete:() -> ()) { (granted,err) in
store.requestAccess(for: .contacts) { (granted, err) in
if let error = err
{
print("failed to access",error)
return
}
if (granted)
{
print(Thread.current)}
However I found that UI is freezing and I am getting that the current thread is NULL even though I specified it to run on mainThread.
print(Thread.current) =
I guess the problem is that you do some UI updates in the completion handler of store.requestAccess.
The handler - according to Apple's documentation - is not called in the main (UI) thread, but in an worker thread:
The completion handler is called on an arbitrary queue. It is recommended that you use CNContactStore instance methods in this completion handler instead of the UI main thread.
Therefore, if you do some UI stuff in here, you must dispatch those calles again into the main thread.

Dispatch Queue inside of Dispatch.main.async

I am currently working on a project that requires me to update a database, and then loop over the database to compare some values. Because of the database update time I have decided to use a delayed call, to give it time to update. Here is the structure of my calls:
//Give database time to update
DispatchQueue.main.asyncAfter(deadline: .now() +5) {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
closure {
...data processing....
dispatchGroup.leave()
}
//wait for data-processing inside closure to complete
dispatchGroup.wait()
}
This freezes my app. It was my understanding that the closure should run asynchronously and so I was hoping the enter/leave balance would be reached while at dispatchGroup.wait(). Any help for solving this problem would be greatly appreciated thank you!
Note also I occasionally get an EXC_BREAKPOINT error at the line I have indicated, in the following function:
- (void) fireEvent:(id <FEvent>)event queue:(dispatch_queue_t)queue {
if ([event isCancelEvent]) {
FCancelEvent *cancelEvent = event;
FFLog(#"I-RDB065001", #"Raising cancel value event on %#", event.path);
NSAssert(self.cancelCallback != nil, #"Raising a cancel event on a listener with no cancel callback");
dispatch_async(queue, ^{
self.cancelCallback(cancelEvent.error);
});
} else if (self.callback != nil) {
FDataEvent *dataEvent = event;
FFLog(#"I-RDB065002", #"Raising value event on %#", dataEvent.snapshot.key);
dispatch_async(queue, ^{
self.callback(dataEvent.snapshot); <---------
});
}
Dispatch is asynchronous but you are running it on the main thread.
You can simply run it on the background thread, it won't bloack the UI.
DispatchQueue.global(qos: .background).async {
}

Updating UIProgressView Progress from background thread

I'm using a UIProgressView and it uses the observedProgress property. I then have a variable of type Progress that is observed.
Now I'm writing to Core Data on a background thread and then updating the completedUnitCount but it's crashing.
Here's the code:
var downloadProgress: Progress
init() {
downloadProgress = Progress()
}
func saveStuff() {
let stuff: [[String: Any]] = //some array of dictionaries
downloadProgress.totalUnitCount = Int64(stuff.count)
persistentContainer.performBackgroundTask { (context) in
for (index, item) in stuff.enumerated() {
// create items to be saved
context.perform {
do {
try context.save()
self.downloadProgress.completedUnitCont = Int64(index + 1)
} catch {
// handle error
}
}
}
}
}
So it's crashing on line self.downloadProgress.completedUnitCont = Int64(index + 1). I realise in writing this that I should also be using weak or unowned self to stop retain cycles, but are there other issues?
All the UI related code have to be performed from the main thread, so you have to dispatch call self.downloadProgress.completedUnitCont = Int64(index + 1) to main thread. Something like this:
DispatchQueue.main.async {
self.downloadProgress.completedUnitCont = Int64(index + 1)
}
Apple Says:
Updating UI on a thread other than the main thread is a common mistake that can result in missed UI updates, visual defects, data corruptions, and crashes.
So whenever you are performing any task on background thread and need to make any ui updates in the process, use all those code inside the following block.
DispatchQueue.main.async{ <uiupdate code here> }

API calls blocks UI thread Swift

I need to sync web database in my coredata, for which I perform service api calls. I am using Alamofire with Swift 3. There are 23 api calls, giving nearly 24k rows in different coredata entities.
My problem: These api calls blocks UI for a minute, which is a long time for a user to wait.
I tried using DispatchQueue and performing the task in background thread, though nothing worked. This is how I tried :
let dataQueue = DispatchQueue.init(label: "com.app.dataSyncQueue")
dataQueue.async {
DataSyncController().performStateSyncAPICall()
DataSyncController().performRegionSyncAPICall()
DataSyncController().performStateRegionSyncAPICall()
DataSyncController().performBuildingRegionSyncAPICall()
PriceSyncController().performBasicPriceSyncAPICall()
PriceSyncController().performHeightCostSyncAPICall()
// Apis which will be used in later screens are called in background
self.performSelector(inBackground: #selector(self.performBackgroundTask), with: nil)
}
An API call from DataSyncController:
func performStateSyncAPICall() -> Void {
DataSyncRequestManager.fetchStatesDataWithCompletionBlock {
success, response, error in
self.apiManager.didStatesApiComplete = true
}
}
DataSyncRequestManager Code:
static func fetchStatesDataWithCompletionBlock(block:#escaping requestCompletionBlock) {
if appDelegate.isNetworkAvailable {
Util.setAPIStatus(key: kStateApiStatus, with: kInProgress)
DataSyncingInterface().performStateSyncingWith(request:DataSyncRequest().createStateSyncingRequest() , withCompletionBlock: block)
} else {
//TODO: show network failure error
}
}
DataSyncingInterface Code:
func performStateSyncingWith(request:Request, withCompletionBlock block:#escaping requestCompletionBlock)
{
self.interfaceBlock = block
let apiurl = NetworkHttpClient.getBaseUrl() + request.urlPath!
Alamofire.request(apiurl, parameters: request.getParams(), encoding: URLEncoding.default).responseJSON { response in
guard response.result.isSuccess else {
block(false, "error", nil )
return
}
guard let responseValue = response.result.value else {
block (false, "error", nil)
return
}
block(true, responseValue, nil)
}
}
I know many similar questions have been already posted on Stackoverflow and mostly it is suggested to use GCD or Operation Queue, though trying DispatchQueues didn't work for me.
Am I doing something wrong?
How can I not block UI and perform the api calls simultaneously?
You can do this to run on a background thread:
DispatchQueue.global(qos: .background).async {
// Do any processing you want.
DispatchQueue.main.async {
// Go back to the main thread to update the UI.
}
}
DispatchQueue manages the execution of work items. Each work item submitted to a queue is processed on a pool of threads managed by the system.
I usually use NSOperationQueue with Alamofire, but the concepts are similar. When you set up an async queue, you allow work to be performed independently of the main (UI) thread, so that your app doesn't freeze (refuse user input). The work will still take however long it takes, but your program doesn't block while waiting to finish.
You really have only put one item into the queue.
You are adding to the queue only once, so all those "perform" calls wait for the previous one to finish. If it is safe to run them concurrently, you need to add each of them to the queue separately. There's more than one way to do this, but the bottom line is each time you call .async {} you are adding one item to the queue.
dataQueue.async {
DataSyncController().performStateSyncAPICall()
}
dataQueue.async {
DataSyncController(). performRegionSyncAPICall l()
}

Resources