Forum Archive

[Share code] ReminderStore - not what Apple intended

mikael

Logo

Link to Github

From the readme:

ReminderStore

Key-value store using iOS Reminders for persistence and distribution across iOS devices.

Introduction

ReminderStore is a Pythonista persistence provider. It uses the reminders module to store the values in a specific list in the iOS Reminders app. If you set that list to be distributed across your iOS devices, you get a free cloud-based storage for your app data, suitable for prototyping different distributed use cases.

API

Create a store object providing the name of the Reminders list to be created/used:

store = ReminderStore(namespace, to_json=False, cache=False)

Use the store object to store, retrieve, list and delete items:

  • Store: store['key'] = 'value'
  • Retrieve: store['key']
  • List: for key in store: print store[key]
  • Delete: del store['key']

If you want to store structures instead of plain strings, set to_json=True. Store and retrieval operations will then serialize and restore the values to and from JSON.

Setting cache=True reduces direct access to the Reminders app. Use the refresh_cache() method to refresh the cache and get information on any background changes. See Notes below for more details.

Notes

  • Key is stored as the title of the reminder, and value in the notes field.
  • Reminder titles are not unique, but ReminderStore uses an intermediate dict to enforce uniqueness. Results are likely to be unproductive if you create duplicate titles manually.
  • Key is used as-is if it is a plain or unicode string. Otherwise str() is called on key before using it.
  • By default, ReminderStore goes to the Reminders app to get the latest information. This is somewhat inefficient since checking for new items always requires loading the full list. There are two cases where you might to prefer to activate the intermediate caching instead:
  • If you have no-one making any updates remotely, cache will be more efficient and will never need manual refreshing.
  • If you want to get more control over when any background changes are applied in your app, use the caching and call refresh_cache to check and process changes. The method returns None if there are no changes, or a dict with added, deleted and changed sets if there are changes. Each set contains the keys of the stored items that were inserted, deleted or had their contents modified, respectively. You can use this info to e.g. remove deleted items from the UI, or inform the user of a conflict and let them decide which version to keep.
  • Note that iOS Reminders app provides no support for complex atomic transactions or referential integrity between items.
ywangd

This is a quite creative idea. I wonder if it can be extended to provide an alternative solution to sync user scripts across devices. A few existing tools use Dropbox or webdav servers. But iCloud would definitely be an attractive option.

My guess is that one could use both RemiderStore and pyzipista by @marcus67 to achieve the sync. Basically pyzipista provides serialisation of files and folders and RemiderStore provides data transport and communication. Some design is likely needed to take care how data and their metadata are stored in the reminder. Is there a character limit of the notes field? I tried with over 60K and it worked fine.

mikael

Thought about it, and concluded that it is certainly possible, but probably a bad idea for larger amounts of larger files.

I do not know and have not had time to test what kind of a size limit there is on the notes section of a reminder, and whether it gives any error or just cuts the extra if such a limit is reached.

Also, the reminders module is such that there is no way to list reminders and just load one - it behaves like it loads the whole list at once. This can be cumbersome if there are several large files, all in memory at the same time.

Hmm, have to take a closer look at the module implementation.

ccc

You can share reminder lists with others... This might be a cool technique for free device-to-device comms in a multiplayer game with delayed delivery when a player is not online.

mikael

Yes. Cool idea. Messages would need to be encrypted, though, otherwise opening the Reminders app and tampering with results might be too tempting...

My Pythonista-fu is weak. I cannot find the code for the reminders module.

ccc
import inspect, reminders
print(inspect.getsource(reminders))

is the usual approach viewing the source of a module but it fails because reminders is a builtin module so no source is provided.

Encryption could be done via https://github.com/dlitz/pycrypto which is provided in Pythonista.

mikael

@omz: When you are not busy otherwise, could you please share/check whether the reminders implementation is such that when getting all the reminders for a list, it only creates proxy objects to the Reminders app, or whether it creates instantiated objects, i.e. reading all the data into Pythonista?

dgelessus

@mikael It looks very much like Reminder, Calendar, etc. are just proxies. All of their attributes are really getset_descriptors (though that is the standard for most C classes, not just proxies) and gc.get_referents shows no Python objects being referred to (though there might still be C data).

mikael

@dgelessus, thanks. If that is true, creating a iCloud-based file sync between devices would seem feasible. I could take it on once my current project is done.

omz

@mikael The Reminder class is essentially just a wrapper for the Objective-C class EKReminder (from the EventKit framework). How and when exactly the data for each reminder is fetched from the database is basically an implementation detail, and it's hard to tell without having access to the source code.