Cloud Kit Core Data persistent history transactions not showing author even when set - objective-c

I'm trying to avoid a dreadful hack while implementing Core Data with CloudKit using Objective-C. I've gotten the basics to work ok but what I want to do is trap for when a 'foreign' (ie not local) update to the Core Data database comes in, to indicate in an iOS app via badge on a tab item entry or bold a button on the OSX part of the app to tell the user that the entries they made on one part of the app need to be applied (say on a Mac) need to be refreshed into the UI on the iOS app, as an example.
I can not get the author or name properties to come through to the monitored NSPersistentHistoryTransactions (s) or associated NSPersistentHistoryChange (s) at all. I set the name on the managed object context (and the chid managed object contexts used in various update routines) and that name shows up in the debug messages just fine so SOMETHING is being set but that nor the author properties show up as hoped. I do find entries in the author of the NSPersistentHistoryChange transactions but they show the generic CloudKit delegate (dot) mirror.
Partially for learning but partially out of desperation, I set up the following code in the notification handler and it fires reliably and just as expected.
NSDate *date = [NSDate date];
NSDate *newDate = [date dateByAddingTimeInterval:-3600*4];
NSFetchRequest *histdatefetch = NSPersistentHistoryTransaction.fetchRequest;
NSPersistentHistoryChangeRequest *fetchHistoryDateRequest =
[NSPersistentHistoryChangeRequest fetchHistoryAfterDate:newDate];
NSPersistentHistoryResult *fetchHistoryDateResult = [localAppDelegate.persistentContainer.newBackgroundContext executeRequest:fetchHistoryDateRequest error:&error];
NSArray *tempArrayDate = fetchHistoryDateResult.result;
unsigned long arrayDateCount = [tempArrayDate count];
unsigned long fetchlastDateArrayEntry = arrayDateCount - 1;
NSPersistentHistoryTransaction *tempHistoryTransaction = [tempArrayDate objectAtIndex: fetchlastDateArrayEntry];
long long checkRow = tempHistoryTransaction.transactionNumber;
if ([localAppDelegate.localUpdate isEqualToString:#"YES"]) {
localAppDelegate.lastLocalRowCount = (int) checkRow;
localAppDelegate.localUpdate = #"NO";}
In the update routines just at the save, I set the local update string to #"YES" and assume that if the notifications fire up, the first one at the top is my local update.
I get the expected history transactions and they build as updates are applied to the database, etc... I then check the row number of the current last persistent history transaction and if any of the subsequent notifications do not have row greater than that it doesn't light up the badge or do the button highlight.
If something comes in and no local updates have been done, the last local row will be zero and the indicator(s) will light up. If a new remote update happens after a local update, the remote update will have a row number greater than what I have saved. It works because it really is a single user application and mostly people don't use more than one device at a time and even if it doesn't work, the worst that happens is that you get redundant indications that you need to press the refresh key. Still, yuck.
I could REMOVE all of this nonsense if I could tell local notifications from remote notifications and I can't seem to be able to do it. The token that comes in with the notification didn't help me and neither do the author or name properties of anything I can see.
I haven't worked out the purge logic yet but I could do that easily enough by doing one when I detect a local transaction is being applied and delete everything more than four hours old.
Any help anybody can supply would be tremendous.

Related

GMOD Lua: How can I pass variables between scripts?

Im new to Lua and the gmod gamemode creation, and I'm having a bit of trouble. I want to deactivate a HUD when the game starts. I have 2 files, one the init.lua file, where a function is called that the game starts (there I want to change the value of HUD.lua) and a HUD.lua file, where the HUD is drawn and it contains the variable I want to change.
I tried multiple approaches, like referencing the script like:
hud.gameBegan = true
, but that didn't worked, so I tried also this putting into my init.lua:
SetNWBool("gameBegan", true)
and then I put this into the HUD.lua:
gameBegan = GetNWBool("gameBegan")
Lastly I tried this:
hud = AddCSLuaFile("hud.lua")
hud:gameChanged(true)
Unfortunatly, neither of these approaches worked for me, can somebody help me?
I would suggest keeping a table on the GM table for your gamemode to hold game states. This would then be synced between the server and the client using network messages.
Essentially how it will work is once the game starts, the server will send a network message to every player telling them that game started is true. When a new player joins, they will also need to know whether the game has started yet or not, and so we will also have to send a network message from the server to any new player that joins, telling them whether game started is true or false.
Once the game ends we need to also inform every player that the game has ended.
To start we need to store the states somewhere, and since whether a game has started or not relates to the gamemode it may be best to store it on the GAMEMODE table, and it also needs to be defined for the server and each player, so we should use the GAMEMODE table in gamemode/shared.lua:
GAMEMODE.States = GAMEMODE.States or {}
GAMEMODE.States.GameStarted = false
In your gamemode/init.lua file (which runs on the server) you may then add something like this:
util.AddNetworkString("MyGamemode.GameStartedSync")
function GM:SetGameStarted(hasStarted)
GAMEMODE.States.GameStarted = hasStarted
-- We have updated the state on the server, now update it
-- for each player on their client
net.Start("MyGamemode.GameStartedSync")
net.WriteBool(hasStarted)
net.Broadcast()
end
function GM:PlayerInitialSpawn(ply, transition)
BaseClass.PlayerInitialSpawn(self, ply, transition)
-- Player has joined, so send them the current game state
net.Start("MyGamemode.GameStartedSync")
net.WriteBool(GAMEMODE.States.GameStarted)
net.Send(ply)
end
If you already have a GM:PlayerInitialSpawn(ply) function then simply merge the contents of that one with yours.
(Note you will need DEFINE_BASECLASS("gamemode_base") at the top of your init.lua file to make BaseClass available if you don't already.)
In you gamemode/cl_init.lua file you need to now write the code on the player's client that can receive the network message:
net.Receive("MyGamemode.GameStartedSync", function()
local hasStarted = net.ReadBool()
GAMEMODE.States.GameStarted = hasStarted
end)
You can then set the sate of whether the game has started using GAMEMODE:SetGameStarted(true) or GAMEMODE:SetGameStarted(false) in any serverside script. And its value can be used with GAMEMODE.States.GameStarted on both the client and the server.
e.g.
if GAMEMODE.States.GameStarted then
DrawMyHud()
end
I am not familiar with GMod but maybe this can work?
In your init.lua file, you can use the include function to include the HUD.lua file and then set the variable to deactivate the HUD.
Here is an example:
include("HUD.lua") -- include the HUD file
HUDEnabled = false -- set the variable to false
In your HUD.lua file, you can then check the value of the variable before drawing the HUD:
if HUDEnabled then
-- code to draw HUD
end
You can also move the variable in the HUD.lua file so you can use it in the HUD.lua file and init.lua file.
-- HUD.lua
local HUDEnabled = true
if HUDEnabled then
-- code to draw HUD
end
-- init.lua
include("HUD.lua")
HUDEnabled = false

After adding a lot of info into Realm one of my items do not work

I've been facing a very strange problem in Realm which doesn't occur every time , but it's been really annoying.
I'm saving a lot of information offline which come from a Webservice using realm. I don't get any exception while saving all these information.
Later I'm able to get all these objects by accessing them through MyObject.allObjects().
These objects contains a property which is an UUID, which I use to download the file related to this object and save it locally, after I finish downloading the file, I update my Realm Object with the path of the file, but some times when I do it:
let pred = NSPredicate(format: "UUID = %#", fileId)
print("This is the select \(pred.predicateFormat)")
let results = IssueFile.objects(with: pred) as! RLMResults<IssueFile>
The variable results is empty, but if I check Realm Database via Realm Browser, I can find the item I'm looking for.
So my question is: why some times Realm can't find the object I'm selecting? As I'm saving a lot of information, is there something I should change in the Realm Configuration?
I've already set the UUID as primary key of the table:
+ (NSString *)primaryKey {
return #"UUID";
}
Thanks,

Why does my core data object not show up using fetch requests between related objects that were created in the same session?

This is a really weird situation which I have been battling to resolve over the last couple of days.
I have a Card entity with a To-Many relationship on a Transaction entity. I am also using Magical Record to remove the boilerplate code around managing my data store.
My problem occurs in the following order:
Create a Card object
Then create a Transaction object in another view
When I inspect the transaction count with [card.transactions count] I get 1 as the size
However, if I perform a fetch request using the same context the transaction is not there. My predicate looks like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"card == %# && deleted == 0", card];
So logic says that I am perhaps not saving the context after saving the transaction, but the weird thing is that if I quit the app and then rerun it, the transaction is there. Steps 1-4 work perfectly too if I create the card, quit the app and then run the app and add a transaction. Then my save transaction code works fine.
So my question is, why aren't my objects showing up using fetch requests on a newly created parent object, where "newly created" means that it was created in the same session as the child object.
My code for creating the card looks like this:
GYCard *card = [GYCard MR_createInContext:[NSManagedObjectContext MR_defaultContext]];
[[NSManagedObjectContext MR_defaultContext] MR_save];
Then when I save the transaction my code looks like this:
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_defaultContext];
NSNumber *transactionAmount = [self.numberFormatter numberFromString:self.transactionAmountLabel.text];
GYTransaction *transaction = [GYTransaction MR_createInContext:localContext];
transaction.amount = transactionAmount;
transaction.deleted = [NSNumber numberWithBool:NO];
transaction.card = self.card;
[localContext MR_save];
The code that is not working is this:
+ (int) countTransactions:(GYCard *) card {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"card == %# && deleted == 0", card];
return [GYTransaction MR_countOfEntitiesWithPredicate:predicate inContext:[NSManagedObjectContext MR_defaultContext]];
}
The countTransactions always returns 0 if the card was created in the same session. But it works 100% if I quit the app and relaunch. It also works for adding any new transactions to the card after quitting and relaunching the app.
This appears to be a Magical Record issue. I was using the 2.0 beta branch of Magical Record and after reverting to the tagged version:1.8.3 the issue disappears. So 2.0 seems to be the culprit. It would still be interesting to know however, why version 2.0 is causing this problem.
I would strongly advise anybody who is using Magical Record to avoid the 2.0 branch until it comes out of beta.
Update: After further investigation what the 2.0 beta of Magical Record was doing was generating 2 save notifications for 1 save, this led to my app inadvertently have 2 versions of the card. This resulted in causing the transaction that I was logging to be logged on the first card but the second card obviously did not have a transaction on it. My fetch request was then fetching the second card and returning zero transactions. Quitting and restarting the app then made my app load the correct transaction from the data store and thus worked as expected.
This could be related to the includesPendingChanges property on NSFetchRequest. However the default for this is YES which means any unsaved changes should be included in any queries.
I'm not sure what the MR_ methods are doing under the hood, but the setIncludesPendingChanges: documentation states:
Special Considerations
A value of YES is not supported in conjunction with the result type
NSDictionaryResultType, including calculation of aggregate results
(such as max and min). For dictionaries, the array returned from the
fetch reflects the current state in the persistent store, and does not
take into account any pending changes, insertions, or deletions in the
context.
So I would make sure the MR_countOfEntitiesWithPredicate method is not doing anything funky. Maybe try calling the standard - (NSUInteger)countForFetchRequest:(NSFetchRequest *)request error: (NSError **)error method on your context and see what that returns.

How can I send different SMS' to multiple recipients in a loop

I'm using Symbian C++ to create my code, I'm using S60 5th Ed SDK
I want to know how to send different messages - Their body text not the same - to multiple recipients in a for-loop ?
I've tried the example below, but when I try to use it in a loop it crashes due to ActiveObjects properties, as I should wait to AO to finish before calling it again.
Sending_SMS_in_S60_3rd_Edition_MTM
Below is example of what I need to do:
SendSMSL(); // **I call this function once to start the process**
// **iRecepients is a CDesCArray contains phone numbers**
// ** iSMSBody is a CDesCArray contains each contact SMS body text**
void CSMS::SendSMSL()
{
if(iRecepients->Count() >= 1)
{
TInt x = iRecepients->Count()-1;
TInt y = iSMSBody->Count()-1;
// **If the sms validating and scheduling succeeded then delete last item from both arrays**
if(iSMSHandler->SendL((*iRecepients)[x],(*iSMSBody)[y])
{
iRecepients->Delete(x);
iSMSBody->Delete(y);
}
}
}
Now, in the code above I call iSMSHandler->SendL() which send sms using AO, and in iSMSHandler object RunL() function, I call back the function above CSMS::SendSMSL() , which in turn checks if there is still anymore iRecepients elements and then call again iSMSHandler->SendL() AO , and keeps this way till no more iRecepients.
Looking forward to hear your feedback on the modification above.
Many thanks in advance.
The link you posted doesn't work for me so I can't see the rest of the code.
Assuming that iSmsHandler is a class that uses active objects to send SMS messages,
I see several issues with your loop.
1) You need to wait for the first asynchronous SendL to complete before you can issue the next SendL
2) The buf variable can not go out of scope until the SendL completes. (This may be the reason for your crash)
I suggest that you keep the textbuffer somewhere else, like together with iSmsHandler, and then code the active object that is called when SendL completes to issue the next SendL.
All of this is guesses since I have no idea what class iSmsHandler is....

If you are using naitive identities, are objects persistant instantly?

In my asp.net application, I open and close/flush the session at the beginning/ending of each request.
With this setup, I thought it would result in things like:
Entity e = EntityDao.GetById(1);
e.Property1 = "blah";
EntityDao.MakePersistant(e);
e = EntityDao.GetById(1);
e.Property1 // this won't be blah, it will be the old value since the request hasn't flushed
But I noticed that the value returned was the most recent updated value.
Someone responded that because of they way I have my identity setup?
Can someone explain this behaviour? So I don't need to call flush to ensure it is persisted to the db?
I belive (but could be mistaken) that nHibernate's caching and change-tracking is the reasons for this.
Since this Session instance is still active, nHibernate is tracking the changes you made to 'e'. When you ask for it again it includes those changes in the object returned. What happened when you called MakePersistant (which I assume calls Session.SaveOrUpdate(e)) is that it told that Session instance you are going to save those changes, thus it knows about them and will still show them when you call Session.Get(id) again.
If you instead started another Session and then called Session.Get(id) you wouldn't see those changes if you had not called Flush (or closed the Transaction, since you should be using a Transaction here) as it knows nothing of the changes made in the other Session.
To answer your other question, yes you still need to call Flush or close a Transaction to ensure changes are written to the database. One thing that is neat, is that you don't actually need to call SaveOrUpdate(e). For example this code will cause an Update to the database:
using (var session = SessionFactory.OpenSession())
using (var trans = session.BeginTransaction()){
var e = session.Get(id);
e.Name = "New Name";
trans.Commit();
}
nHibernate knows to update 'e' since it was tracking the changes that were made to during that Session. When I commit that transaction they are written. Note, this is the default behavior and I believe it can be changed if you want to require that .SaveOrUpdate() is called.