I searched for a many days, but I can not find it. What would be the equivalent of the following MagicalRecord statement in Realm.io?
[MagicalRecord saveUsingCurrentThreadContextWithBlock:^(NSManagedObjectContext *localContext) {
// Save block
} completion:^(BOOL success, NSError *error) {
// Completion block
}];
I need "Save block", and when it finish, execute "Completion block"
Thanks!!
the equivalent in Realm is -[RLMRealm transactionWithBlock:].
Since both your MagicalRecord example and Realm's equivalent run in the current thread, the completion block is a bit redundant because it's equivalent to adding code immediately after the call to this method.
Related
when I run the following code:
[[FIRAuth auth] signInWithEmail:#"myemail#mydomain.com"
password:#"hdfjhfjhdbf"
completion:^(FIRUser *user, NSError *error) {
NSLog(#"I am in Block");
}];
NSLog(#"I am here");
my output is :
I am here
I am in Block
it means that the FIRAuth block runs in another thread other than main thread.
I add an extra line to the code (while infinite loop)
[[FIRAuth auth] signInWithEmail:#"myemail#mydomain.com"
password:#"hdfjhfjhdbf"
completion:^(FIRUser *user, NSError *error) {
NSLog(#"I am in Block");
}];
NSLog(#"I am here");
while (YES) {};
my output is :
I am here
the question: if the FIRAuth block runs in another thread it should print "I am in block" no matter we have infinite loop in main thread. how could it be possible?does the FIRAuth block runs by less priority in same main thread?
Usually in Firebase we:
run the operations that interact with network and disk in a separate thread
surface the callbacks back on the main thread, so that your code can interact with the UI
I didn't verify for this specific case, but expect it to work the same. And that means that your infinite loop is preventing the completion block from being executed.
If you want to wait for a result, use semaphores as describes here: How do I wait for an asynchronously dispatched block to finish?
I have four api calls to make. They should be in following order:
apiSyncDataToCloud;
apiSyncImagesToServer;
apiDeleteDataFromCloud;
apiSyncDataFromCloudInBackground;
Each one of them is to be called irrespective of the fact that previous one finishes successfully or fails.
Also, each one of them have success and failure completion blocks.
In success completion block database is updated.
All this process has to be performed in background and has to be done a no of times.
Api calls are of course performed in background but once a call completes database update is performed on main thread thereby freezing the app.
So, I went with several solutions:
Tried following code:
NSOperationQueue *queue = [NSOperationQueue new];
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
[self apiSyncDataToCloud];
}];
[queue addOperationWithBlock:^{
[self apiSyncImages];
}];
[queue addOperationWithBlock:^{
[self apiDeleteDataFromCloud];
}];
[queue addOperationWithBlock:^{
[self apiSyncDataFromCloudInBackground];
}];
But this only guarantees that api method calls will be performed in order. But their result follows no specific order. That is, method calls will be in the order specified but success block of apiSyncImagesToServer may be called before success block of apiSyncDataToCloud.
Then I went with following solution:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self apiSyncDataToCloud];
});
and in the success and failure blocks of apiSyncDataToCloud I have called apiSyncImagesToServer. This too did'nt work.
Now I am simply going with my last solution. I am just calling apiSyncDataToCloud.
In success completion block this method first updates the database and then calls other api.
In failure completion block this method simply makes the api call without updating the database.
For example-
structure of apiSyncDataToCloud is as follows:
-(void)apiSyncDataToCloud{
NSLog(#"method 1");
NSMutableDictionary *dicDataToBeSynced = [NSMutableDictionary dictionary];
dicDataToBeSynced = [self getDataToBeSynced];
if (dicDataToBeSynced.count!=0) {
if ([[StaticHelper sharedObject] isInternetConnected]) {
[[ApiHandler sharedObject] postRequestWithJsonString:API_SYNC_DATA_TO_CLOUD andHeader:[UserDefaults objectForKey:kAuthToken] forHeaderField:kAccessToken andParameters:dicDataToBeSynced WithSuccessBlock:^(NSURLResponse *response, id resultObject, NSError *error) {
NSLog(#"Data synced successfully to server");
[self updateColumnZSYNC_FLAGForAllTables];//updating db
[self apiSyncImagesToServer];//api call
} andFailureBlock:^(NSURLResponse *task, id resultObject, NSError *error) {
NSLog(#"Data syncing to cloud FAILED");
[self apiSyncImagesToServer];//simply make api call without updating db
}];
}
}else{
[self apiSyncImagesToServer];make api call even if no data to be synced found
}
}
Similary, inside apiSyncImagesToServer I am calling apiDeleteDataFromCloud.....
As a result my problem remained as it is. App freezes when it comes to success block updating db, downloading images...all operations being performed on main thread.
Plz let me know a cleaner and better solution.
You can create your own custom queue and call request one by one.
i.e.
dispatch_queue_t myQueue;//declare own queue
if (!myQueue) {//check if queue not exists
myQueue = dispatch_queue_create("com.queue1", NULL); //create queue
}
dispatch_async(myQueue, ^{[self YOUR_METHOD_NAME];});//call your method in queue block
If you want update some UI after receiving data then update UI on main Thread.
1) Better to use AFNetworking for this kind of situations. Because AFNetworking provides better way to handle Main & Background Threads.
AFNetworking supports success and failure blocks so you can do one by one WS Api calls from success and failure of previous WS Api call. So during this time period show progress HUD. Success of last API then update DB and hide progress HUD.
2) If you need to use NSOperationQueue and NSInvocationOperation
and follow this link. https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift
Api calls are of course performed in background but once a call
completes database update is performed on main thread thereby freezing
the app.
Then why not perform it in a separate queue?
Try using
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//your code
});
to perform time-consuming tasks and
dispatch_async(dispatch_get_main_queue(), ^{
//your code
});
to only update UI.
I have a method in an iOS app that is supposed to return a bool value depending upon whether or not a web call succeeds.
The web call is structured in a way such that it takes a block as a callback parameter and that callback is called when the web call has a result. Based on that result my method needs to return a True/False value.
So, I need to stop execution from progressing any further without first having a result to return.
I am trying to achieve this via semaphores, after looking at some examples that others have shared, but the callback is never called, if I remove the semaphore then the callback is always called.
What am I missing here?
+ (BOOL)getUserInformation {
__block BOOL flag = false;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[[WebServicesManager sharedManager] getUserInformationWithCallback:^(NSInteger statusCode, NSString *response, NSDictionary *responseHeaders, id obj, NSError *error) {
if (error) {
//Handle error case and perform appropriate cleanup actions.
}
else
{
//Save user information
flag = true;
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return flag;
}
I put a break point on if(error) to check if the callback gets called, it doesnt, unless I remove the semaphore.
I could give this method its own callback block or I could give the containing class a delegate and achieve what I need but I would really like to make this approach work.
The WebServicesManager is probably dispatching it's block on the same thread the semaphore is waiting on.
As #Rob is correctly mentioning in the comments, this is most likely not a good idea to do on the main thread; rather make use of the asynchronous model and not block the main thread for possibly minutes until the connection may time out under certain circumstances, freezing your UI.
You are undoubtedly deadlocking because you're using semaphore on same thread to which the web services manager (or the API that that is using) dispatches its completion handler.
If you want a rendition that avoids the deadlock scenario, but also avoids the pitfalls of blocking the main thread, you can do something like:
+ (void)getUserInformation:(nonnull void (^)(BOOL))completionHandler {
[[WebServicesManager sharedManager] getUserInformationWithCallback:^(NSInteger statusCode, NSString *response, NSDictionary *responseHeaders, id obj, NSError *error) {
if (error) {
completionHandler(false);
} else {
//Save user information
completionHandler(true);
}
}];
}
Then, rather than doing something like:
BOOL success = [YourClass getUserInformation];
if (success) {
...
}
You can instead do:
[YourClass getUserInformation:^(BOOL success) {
if (success) {
...
}
}];
// but do not try to use `success` here ... put everything
// contingent upon success inside the above completion handler
I'm using the [MagicalRecord saveWithBlock: completion:] method but I'm not sure how to access the saved object on the completion block. My code is the following
NSLog(#"saving player");
__block PSPlayer *player;
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
// parse json
player = [self parsePlayer:playerInfoJson inContext:localContext];
NSLog(#"player.md5Id %#", player.md5Id);
} completion:^(BOOL success, NSError *error) {
NSLog(#"player.md5Id in success %# error %#", player.md5Id, error);
...
}];
The player.md5Id is correctly set at the end of the save block but is nil in the completion one. Is this a correct usage?
cheers,
Jan
The completion block captures the player reference before it's set so it will be nil when that block gets executed.
If you want to use the new managed object later you should store it in a property and then call a method from the completion block, (possibly switching to the main thread, not sure if MR does that for you) to find that object in the main context.
Alternatively I think you could define the completion block earlier and then pass a copy to the method, then the copy would have access to the updated player reference (I don't do that very often really but IIRC it should work).
When I call saveWithBlock:completion: it actually saves the data but doesn't call the completion Block. Is there a bug, or am I using this method the wrong way?
I use MagicalRecord 2.2 from CocoaPods. I've tried to downgrade to 2.1 with no luck -- the problem remains.
[MagicalRecord setupCoreDataStackWithInMemoryStore];
[MagicalRecord setDefaultModelNamed:#"Model.momd"];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
TestUser *test = [TestUser MR_createInContext:localContext];
test.name = #"Lweek";
} completion:^(BOOL success, NSError *error) {
NSLog(#"Yeah");
}];
This outputs nothing. The completion Block is not triggered, although the data are saved properly.