I am writing an app and using CloudKit. The app stores its data in the public database. I’ve created a local cache so that my app can function even if the network isn’t available. I’m using CKSubscriptions and the resulting push notifications to keep changes from the cloud in sync. All of this is working well.
Now, if a user has multiple devices and is running my app on all those devices, then I can no longer mark notifications as “read” (using CKMarkNotificationsReadOperation) since I won’t know when all the devices have processed them. This is particularly true if one of the devices is offline when a change happens. If I do mark them as read, then when the other devices check for new notifications (using CKFetchNotificationChangesOperation) they will not see them and their local cache will be out of date.
My current solution is to just leave all notifications in an “unread” state and rely on the CKServerChangeToken in CKFetchNotificationChangesOperation so that each device only grabs the notifications that have occurred since that device last checked. This works well.
But, it seems to me that since I’m not marking any notifications as “read” they will simply continue to pile up on the server. Maybe this isn’t a big deal, but they will take up space and I have no good way to get rid of them. Over time, this seems as if it could be a problem.
Has anyone used subscriptions/notifications in a similar way and come up with a different approach? Also, any feedback on my approach would be welcomed.
The server will delete the old notifications, regardless of the read status.
Related
We have been looking for a reliable way to periodically get data from a URL from an iOS app, regardless of the state of execution of the app on the device (foreground, background, suspended or terminated/force-quit).
We have gone through all the questions on this topic that are on this site, and their answers, as well as all the relevant iOS documentation from Apple and blog posts we can find on the subject. But, we have not yet found a solution that will work.
Here is what has been considered/implemented:
Background fetch - This is our current implementation. It works at the whim of the OS as far as the frequency and timing of the fetch goes, and only when the app is not "terminated" or "force-quit" by the user. This has been found to be totally unreliable, as the users themselves don't understand the quirks of iOS, and force-quit the app, yet expecting it to function even after that. The app deals with financial information, and thus can result in loss of potential financial gains to the users if the fetches are not performed on a schedule.
Change notifications from the server - this is not an option, as we neither store nor wish to store certain information on a server, for privacy reasons. Therefore, we cannot use this.
Silent notifications from the server to trigger fetch - again, this won't work if the app has been force-quit, as the notifications are not delivered to the app in that state.
PushKit - this is what apps like WhatsApp or Vonage would use to fire the app regardless of its state of execution. However, this is only available to VoIP apps, and we don't fall into that category. Can this still be used by non-VoIP apps? If so, we could settle for this but only as a last resort, as the preferred option is to have it all done from the app without involving a server to generate "empty" push notifications, simply for the purpose of triggering a URL download.
Given the nature of the app and the potential for the user to benefit financially, it seems rather harsh that iOS would not allow a simple operation such as a periodic download of a URL. So, hoping that someone knows how to overcome this challenge.
As an aside, the exact same functionality on Android works like a charm, regardless of the app's state of execution.
We are designing a system that based on particular events on the server creates geofences for particular device. It is expected that the client (device) will be in sync with the server data, question is how?
Initial (most optimal) idea was to send a silent push to the device to notify it about new data, and trigger data pull. Knowing that this solution will work for Android OS devices, we though the problem can be solved in similar fashion on iOS. Unfortunately, my iOS dev told me that silent push are not reliable on iOS, and presented following discussion: Silent push notifications only delivered if device is charging and/or app is foreground.
Therefore, my question is how to keep the (geofence) data on the iOS devices in sync with server side?
We can pull, say every say 5 minutes, this solution is extremely inefficient, for most of the devices new geofences are changed rarely if at all, but our ‘power users’ need to have geofences updated very often.
We could push with some kind of silent push mechanism, but it has to be reliable.
Maybe some kind of persistent connection (tcp or better udp) but that seems like battery draining solution. Besides not reliable, the server would have to keep track of changing IPs which is not even possible on many cellular networks.
WebSocket. Also battery draining solution, that is not intended for background sync. Overkill for devices that really have the data updated rarely (like once a month).
Some commercial solution (PubNub or Pusher), but we would definitely prefer in house solution.
Are there any other solutions that are used in such cases? Maybe our approach with silent push is not right, but there is other build in Apple solution for such use case?
There's a fantastic service called Simperium with an iOS SDK that can help keep your info in Sync. I heard about them because I started using SimpleNote, a free note-taking tool that uses the sync service. They were acquired by Automattic, which runs Wordpress so the whole deal should be decently stable.
Hope this helps!
You can run your syncing operations in the applicationDidEnterBackground: method within AppDelegate. That's the solution I've always used as long as the syncing operation doesn't take too long.
Here you can also query your database for data changes instead of pushing a trigger to the device, which could get hairy with push notifications and aren't really their intended use. If changes in the data are found (or some boolean flag is checked) then initiate a data pull.
I'm not sure how much data you are working with but having a REST query every 5 mins is the way I'd go. Perhaps you can even switch it to a query each time you start up the app would be good enough?
We have an app where the user data needs to be sync'd with the back end server - each time we start the app it queries the backend server to see if there are any updates. We md5hash the data from the server and then we can just check our hash against the latest data - if it doesn't "match" then we pull the new data set.
In general, iOS doesn't really allow you to do multi-tasking the way you can do on android. Now, if you aren't releasing to the app store - and only using this as an in-house app you can get a little funky with things and run in a background mode.
I'm making a simple app with a Today Widget extension that logs events.
The user can tap a button in the app or the related Today Widget to log an event. These events are saved with Core Data any time the button is pressed either place.
Whenever a new event is logged in the app, I run a function called updateLocalNotificationsFromCoreData(). It handles the setup of UILocalNotifications based on the most recent event in Core Data after clearing the appropriate existing notifications.
However, when a new event is logged from the Today Widget, I can't use this function because I need to register the Local Notification with UIApplication.sharedApplication().scheduleLocalNotification(), and UIApplication is not available in the Today Widget extension.
I realize I'll probably need do something unconventional or hacky to get this working, so I'm trying to evaluate possible approaches and come up with a relatively robust solution.
Basically, I want to find a way I can call my
updateLocalNotificationsFromCoreData() function right away any time a new event is logged.
If I can't do it every time an event is logged, an alternative would be to trigger the updateLocalNotificationsFromCoreData() function periodically (somewhat frequently) another way. Here are some solutions I was thinking about using, but I don't like any of them:
Do it in AppDelegate when the app is launched (or another state change)
One approach I'm thinking about is running my updateLocalNotificationsFromCoreData()function in AppDelegate somewhere, like didFinishLaunchingWithOptions.
The downside is that it would require the user to open the app periodically. If the user didn't open it much the notification behavior would be inconsistent. I'd prefer a solution where a user could interact with only the Today Widget and reliably get Local Notifications without ever opening the app.
Sync the events to a server and use Push Notifications
I've thought about syncing the data in Core Data to a server, then setting up Push Notifications to the user's phone based on that.
I don't like this, because I want the user to still be able to get notifications without an Internet connection. It also introduces a lot of extra overhead of syncing the data with a server.
Ping a server, and send a content-available Push Notification
When someone logs an event with the widget, I could ping a server. That server could send back a silent content-available push notification to trigger the app to run updateLocalNotificationsFromCoreData() in the background.
I found a similar question (Scheduling local notification from within a Today extension) where one answer proposes a similar solution. Unlike the previous solution, an Internet connection is not needed to receive the notifications, but an Internet connection would be required to make sure the notifications are up to date when a new event is logged.
Background fetch
I thought about using Background Fetch to fetch something arbitrary from a server, then run the updateLocalNotificationsFromCoreData(). This would be a way to trigger the update in the background, although it seems silly to fetch data if that data isn't being used, and seems like something for which an app could be rejected. There also seems to be a risk of the system not calling the background update regularly if the user doesn't open the app much and mostly uses the Today Widget.
Use background location updates
This seems like the dumbest approach, but I thought I would mention it anyway since I thought about it. I could use one of the low accuracy background location update modes to trigger updateLocalNotificationsFromCoreData().
It would require the user to allow location in the background, which would be hard to explain. And, it would require the user to at least move around a few blocks to trigger the function, which could provide an inconsistent user experience. Also, it would increase power consumption of the app for a silly reason.
I'd really appreciate fresh ideas about how I might be able to reliably schedule local notifications when Core Data changes on a device that doesn't have an Internet connection!
Or, if that doesn't seem possible, I'd appreciated feedback on which approach seems to make the most sense.
EDIT: I came up with a new solution. It's not ideal, but I think it's better than these other approaches I was considering. When someone taps the button to log the event, I launch the full app. It's annoying because I have all the data I need at that point to give the user feedback and log the event within the Today Widget without launching the app, but by launching the app I have the opportunity to check and schedule local notifications.
Also, in iOS 9 the annoyance on the user is slightly minimized because the system-wide "back" button will appear and let the user go back to the previous app easily once my app has launched from the Today Widget.
In the future I may try a solution where one of the server-based approaches above is used when an Internet connection is available, and I would then fall back to this system of opening the app only when the network connection is not available and I need to schedule the local notifications within the app.
Is this possible to do? I know basic iOS programming (i've only been playing around with it for a couple weeks), but I was wondering if Apple gives you the ability to do this. I would like the app to receive a notification when a new contact is added and then execute a method from there. Any advice would be great. Thanks!
P.S. I would like the app to not even be running when it gets these notifications too if thats even possible
You mean, when a contact is inserted in the device's address book? No, there's no way to automatically receive a notification triggered by activity in the device's address book.
But with iOS7's added background modes, you could probably take advantage of the Background Fetch mode or Remote Notification mode to regularly check for updates to the user's address book. Background Fetch is technically supposed to be used to fetch data from a server, but you may be able to use it to fetch address book data in this case… Your app would still have to be running though, albeit in the background.
Within my iPhone application I periodically make calls to a webservice, providing the endpoint with a list of numeric IDs. The webservice then returns information relating to the IDs it receives.
This is all well and good. However, I would like to be able to provide functionality whereby the user will receive a local/push notification when these changes occur, regardless of whether the application is open or not.
I'm just looking for guidance on my options in this scenario. As I see it, there are two main approaches: calculate any data changes on my webserver and send a push notification to all devices, or query the webservice from the device itself.
The second option seems ideal, as not all devices will need each push notification, but I'm unsure as to whether this is possible with the current state of iOS' multitasking APIs. Any advice would be appreciated.
Bad news: it's not possible. Apps can only run in the background for a short period of time after the user has exited unless it fits into a small number of categories (GPS, VoIP, etc).
Web services, unfortunately, do not count. So this would have to be performed on the server side and with push notifications.