There is a badge count of 0 present in information that is entered in a web page. The information is sent out as a remote notification and as a data stream through sync which creates a local notification.
When I receive the remote notification this is indicated by the fact that the badge key is set to 0.
The public documentation says this:
Key: badge, Value type: number; The number to display as the badge of
the application icon. If this property is absent, the badge is not
changed. To remove the badge, set the value of this property to 0.
When I receive this information through the sync and create a UILocalNotification for it, setting the applicationIconBadgeNumber to 0 means something entirely different.
The public documentation says this:
The default value is 0, which means "no change.” The application should use this property’s value to increment the current icon badge number, if any.
Also, the semantics differ a bit since you cannot make a property absent the same way you can with a key-value pair in a dictionary.
The question is, how do I compensate for this difference? I don't have any insight on how it works on the web server (backend), I can only see what is actually entered in the UI as far as badge count is concerned. I do notice that when it is received the 0 have different meanings. Should these differences be handled by the backend, or should I handle them in the client?
Is there any reason for these differences?
Related
I'm learning the new Contact Tracing API that Apple is releasing for iOS (in partnership with Google).
I'm not gasping the relationship of the maxKeyCount property on the CTExposureDetectionSession, and its relationship with the addPositiveDiagnosisKeys: completion: method.
What I understand
A CTExposureDetectionSession is the object that allows an app to ask the framework to start trying to match a list of published Diagnosis Keys against the local database of captured Rolling Proximity Identifiers.
The app would start by calling the activateWithCompletion: method on a new session, and then call addPositiveDiagnosisKeys: one or more times, to eventually inform the framework that no more keys are to be added by calling finishedPositiveDiagnosisKeysWithCompletion:.
That last call will also indicate the block to run upon detection completion, which will be called with a CTExposureDetectionSummary object informing the amount of Diagnosis Keys that the device has been exposed to.
What I do not understand
The maxKeyCount property doc says:
This property contains the maximum number of keys to provide to this API at once. This property’s value updates after each operation complete and before the completion handler is invoked. Use this property to throttle key downloads to avoid excessive buffering of keys in memory.
But the addPositiveDiagnosisKeys: method says:
Asynchronously adds the specified keys to the session to allow them to be checked for exposure. Each call to this method must include more keys than specified by the current value of <maxKeyCount>.
maxKeyCount seems to be a maximum, but the addPositiveDiagnosisKeys: requires me to call it with more keys than the maximum.
Am I expected to call the method with a superlist of the previously sent list? That doesn't seem to fit well with the "avoid excessive buffering of keys in memory" part - if I have to use an ever-growing list of keys.
And what does the This property’s value updates after each operation complete part?
The documentation of maxKeyCount is missing a not.
The Android Contact Tracing API documentation has an analogous interface:
/**
* Provides a list of diagnosis keys for contact checking. The keys are to be
* provided by a centralized service (e.g. synced from the server).
*
* When invoked after the requestProvideDiagnosisKeys callback, this triggers a * recalculation of contact status which can be obtained via hasContact()
* after the calculation has finished. *
* Should be called with a maximum of N keys at a time. */
Task<Status> provideDiagnosisKeys(List<DailyTracingKey> keys);
/**
* The maximum number of keys to pass into provideDiagnosisKeys at any given * time.
*/
int getMaxDiagnosisKeys();
As Paulw11 suggested in a comment, the maxKeyCount property seems to be a value intended for reading that stats how many Diagnosis Keys are to be sent to the API in a single call for the matching to be performed.
Callers should re-check the value before each call, since it may get updated after each call.
The fixed documentation of addPositiveDiagnosisKeys: should, then, read:
Each call to this method must NOT include more keys than specified by the current value of <maxKeyCount>.
I know that UserDefaults are meant simply to save preferences, but these preferences are persistent - the values saved in UserDefaults are maintained over an unlimited number of app launches and are always available to be read as long as the app remains installed... right?
Is it possible that these values will be cleared or will not be correctly accessed at any point? After years of using UserDefaults and depending on the consistency of the values they hold, I have now seen twice in one day of work that when my app launched and checked a simple boolean value, the value was not correct.
if defaults.bool(forKey: "beenLaunched") {
This code runs each time the app launches. If the value is true, I do nothing more, but if it is false, I set a few values as this is the user's very first launch of the app and then I call defaults.set(true, forKey: "beenLaunched") and defaults.set(0, forKey: "eventsCompleted") and a few other values.
I found this thread on the Apple forums in which Eskimo said "For the central NSUserDefaults method, -objectForKey:, a result of nil means that the value is unavailable, but there’s no way to distinguish between this key is not present and this value can’t be fetched because the user defaults are offline." (This appears to be in reference to a specific case of background launching while a device is locked)
I can look into a more secure way of saving simple data such as a Bool value, an Int, or a String, but using UserDefaults for these types of values has always been simple, straightforward, and reliable. Can anybody chime in on the matter and if I was wrong to believe in UserDefaults' persistence?
Thank you!
UserDefaults isn't a "service"; it's never not available to your application. The file it writes to is a PLIST (and therefore all values are stored according to the PLIST standard). For example, all numbers (including booleans) are stored as an NSNumber to the file and can be retrieved either by object(forKey:) or bool(forKey:). If you use the object method and nothing is set for that value you get nil, whose boolean value is false (or 0). Same if you use the boolean method (you get false). This means no matter which way you go, you'll always get false if there's no value or a value of false. Design your logic around that (which you already have - "beenLaunched" will be empty and therefore false if it's never been launched) and you should be fine.
As for the suggestion of synchronize(), ignore it. Unless you're doing something really weird with threads and preference access or you've interrupted the application immediately after setting a value/object for the problem key, it's got nothing to do with this. Per the very first paragraph of the docs, synchronize() is called periodically as needed. In practice, it's called pretty much immediately after a change occurs.
For context, none of my apps have ever called synchronize() and some of them are old enough to drive. Never a single problem. If you don't have a very good justification for calling synchronize() yourself you almost certainly don't need it and attempts to explain why you do need to sprinkle it everywhere are ... often amusing.
In your specific case, the value stuck by after first run multiple times then suddenly didn't once. Is it possible you changed your app's bundle identifier or name? The defaults are stored by identifier+name so a change would effectively "reset" your app's defaults. Have you been running your app in the simulator and did you just reset-content-and-settings in the simulator? On your device and deleted the app before re-running it on-device?
If you are working in swift then returning nil means objectforkey has not been assigned any value at all . In other case it always returns proper value if you casted saved value properly.
And userdefaults is always available to use, it can never goes offline.
Main app sends data to watch, and watch sets the picker index by using:
[picker setSelectedItemIndex:val];
This in turn, will fire up the picker action. However my picker action sends data to phone (using sendMessage) which in turn replies back to watch... and that goes on forever.
How can I cancel the picker action for setSelectedItemIndex:? WKInterfacePicker doesn't have a removeTarget: method.
You don't want to remove the action. Instead, you have a few options to choose from to simply stop the cycle from happening by:
Only setting the picker's value when the initial message is received, not when the response is received.
If the watch says, "The picker index is 3," and the phone replies, "Roger, the picker was set to 3," why would you want your watch code to set the picker based on the response to something the watch initially sent? This seems to be the crux of the issue, and would be the optimum solution.
Use a different key for the response, if necessary, to help any shared code distinguish between the types of messages.
Not setting (or sending) the picker's value, when it already matches the picker's current selected index.
You'd have to maintain a property to keep track of the current value, as the picker's properties are write-only and can't be read.
Using a different WCSession method (such as updateApplicationContext) which is designed not to resend data when it matches the most recent applicationContext.
Even if I add a new local notification right before, the attribute is empty. I found a lot of post (and just one on stack overflow) - but nobody has solved this problem.
My useless is, that I want to delete a local notification. That's why I want to iterate over the array and compare the hash value of my notification to delete and the current iterator object.
The notification fires correctly.
Add notification to the array
UIApplication.sharedApplication().scheduleLocalNotification(newNotification)
Read the array
for notification in application.scheduledLocalNotifications {
if notification.hashValue == hashValue {
application.cancelLocalNotification(notification as! UILocalNotification)
NSLog("Unsheduled local notification for \(notification.alertBody!)")
}
}
Thanks for your help.
Seems I have not been first to be stucked on it..
But seems answer is much closer than I tought.
Cmd + LPM
public var scheduledLocalNotifications: [UILocalNotification]? // setter added in iOS 4.2
The property is just form of setter. And probably was never intended to get scheduled notifications
It seems that checking the UIApplication's scheduledLocalNotifications array is fairly unreliable.
Most people seem to recommend just keeping your own list as well, and querying that.
Firstly, that array will only contain notifications that are scheduled after the current date, so any that have been registered that are in the past or any that have already fired, will not be added.
For me, that was not the problem, the notificatiosn I was registering were definitely in the future, but still didn't appear in the list. The best answer I could find is:
Having similar issues right now. My guess here is that iOS does not schedule the notifications immediately but only at the end of the current run loop. I am running into these problems when setting the scheduledLocalNotifications property several times in the same run loop and changes don't seem to be updated accordingly. I think I will just keep a copy of the local notifications array myself and only set scheduledLocalNotifications and never read it.
(source)
I am working on a custom email notification for a WSS 3.0 solution. I am using a custom class inheriting from IAlertNotifyHandler to generate the email. There is a great example here that shows how this is done for an Immediate alert. Here is some of the code related to the SPAlertHandlerParams, which is used to get information about the alert and the item that triggered the alert.
SPAlertHandlerParams ahp;
int id = ahp.eventData[0].itemId; //gets the itemId of the item triggering the notification.
SPListItem myItem = list.GetItembyId(id);
For immediate alerts, this works great as the item I want is always at the [0] position of the eventData object. For a digest event, I thought I could just loop through all of the items in the ahp.eventData. Two problems with this.
First, it gives me all of the events where it is sending notifications, not just the ones for me. Second the eventData[0].itemId no longer points to a valid id on the list. It is 6-7 digit number instead of a 3 digit number.
Does anyone know the correct way to get to the alert information for digest emails?
Please let me know if you have any additional questions about this.
Thanks for your help!
For my project, I created a custom timer job (using the post by Andrew Connell) that mimics the Alert functionality. It runs overnight and queries for any users subscribed to my list with daily alerts. It then packages all of the new tasks into a custom email message.
I left the custom alert in place to suppress any daily notifications from the system. I just return 'True' so that alerts are not sent for tasks assigned to only 1 person. I suppose looking back on this, I could have run the query code in the custom alert and not need a separate timer job.