I'm still in the very early stages of a game, and I am saving information using a KeyChain wrapper class someone made. I wanted to ask for some advice early on, since I have time to change my approach.
My game has the potential to persist a fair amount of data about the player and what they've done, such as:
how much gold the player has
what items they've acquired (you can get about 50 items)
what skills, spells, and abilities they've chosen for their character
what experience level they are, max health, stats, etc
The reason I decided to store this in KeyChain was that I was told it's encrypted and much more difficult to tamper with. I felt there were other solutions such as the ones below, but I wrote some potential reasons why that might not be good:
Make everything web-based, and stored in a database somewhere on my server - I want my game to be playable offline
Use a local database (FMDB, let's say) - I could use a tool to edit the values directly, and give myself more health, etc.
Use Core Data - Never used this before, not sure if this is the same ease of tampering as #3?
GameCenter - Never used this before, so not sure what the lift is
NSPreferences - Preferences are more easily tampered with (i've used a tool to change the values pretty quickly)
So I am not sure if i'm completely wrong above, but let's say there is some degree of truth there and KeyChain is a good approach. The problem is now what if I want to then somehow allow the player to pick up a new device and pick up where they left off? How on earth would I serialize all that data I'm saving in keychain? I don't mind creating a giant JSON document of the values, and sending it along somewhere (where? to GameCenter?)
Any advice / pointers in the right direction would be good, especially now since i'm in the early stages and can make changes to step back.
Thanks so much everyone appreciate your time!
A few thoughts based on lessons learned (usually, "the hard way") that may (or may not) be helpful. :)
I see three requirements in your post: offline play (requiring local storage), data security (which is a massive topic in and of itself) and synchronization.
Playable offline:
Thus you need some sort of local storage. Keychain, Core Data, SQL, NSPreferences are all options. I don't know the limitations of the Keychain, so not really sure how suitable it is for continuous read/write of large chunks of data.
Data security:
They keychain protects your secrets when you're not logged in, and partitions them between apps. https://developer.apple.com/library/content/documentation/Security/Conceptual/keychainServConcepts/02concepts/concepts.html and http://evgenii.com/blog/sharing-keychain-in-ios/ give more details. That should prevent other tools from modifying other app's content on non-jailbroken devices.
Core Data, SQL, et al will be inside your app's container, which makes it harder for other tools to access on non-jailbroken devices. There's a good description here: https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html
NSPreferences doesn't offer any security that I'm aware of. Plus, if they jailbreak the device, they basically have root access on a linux machine and can probably do anything they want.
In today's security world, the mantra is generally, "assume breach." As in, assume if they want in bad enough, they're going to find a way in. Thus you need to think about other layers of defense: partitioning your secrets so compromised secrets have limited value, encryption at rest and encryption in transit. Obfuscating the data before you write it/transmit is therefore another layer of defense (although they may still edit it and you'll have to handle potential garbage values).
You can chase security pretty far down the rabbit hole; you'll have to decide where the cost outweighs the benefit, and/or what risks you intend to defend against.
Synchronization across devices:
Thus you need a syncing solution. the iCloud Keychain syncs between devices, so if the keychain meets your storage needs, this probably will meet your sync needs, too. Again, I'm not sure if there are size or frequency-of-update constraints. I haven't used this, but this SO answer gives more info: https://stackoverflow.com/a/32606371/1641444. Based on Apple's docs (https://support.apple.com/en-us/HT204085) it looks like the user does have to enable syncing for this to work.
Otherwise, Apple provides GameCenter and CloudKit. Or you could explore 3rd party options. I've used GameCenter and CloudKit to sync data across devices. CloudKit wins over GameCenter, IMO, no contest.
With GameCenter, you gain multiplayer matchmaking and channels to share data between users. But, you have to adhere specifically to its structure which is IMO both extremely frustrating to work with and limited in functionality. Check the GameCenter and GKTurnBasedMatch tags here on SO for a taste of the problems.
CloudKit is a lower-level solution. It allows you to store a wide variety of data objects in iCloud and "subscribe" to change notifications. All data is contained within a container for the app, and has a "private" (user-specific) section and a public (shared by all users of the app) section. After watching the introductory WWDC videos on CloudKit, I was successfully sync'ing user settings between devices AND different users on different devices in no time. However, if you want some of the multiplayer features of GameCenter, you'll have to build the data model/subscriptions yourself. Since you support offline play, you'd need to save your data to a local storage solution and periodically upload it to iCloud for sync.
Conclusion (aka TL;DR)
So, my opinion is: none of the tools individually meet all three of your requirements, and you'll end up rolling your own solution for at least 1 req, regardless of which option you go for. In my multiplayer game, I'm trusting in Apple's filesystem (containers) to provide sufficient data security, I'm using a local storage option within the app's container, and I'm periodically writing NSData objects to cloudKit for synchronization across devices. I hit my frustration limit with GameCenter and pulled it from my app.
Star for a good question!!
I'm doing something similar with this. Right now, I'm using UserDefaults for primary storage. When I need to transfer data, I can save it to iCloudKit, or I could export it as a plist or json to use with AlamoFire, email, etc.
As for actually storing the data, I saw you mentioned CoreData.. that is kind of overkill! Look at NSCoder and NSKeyedArchiver.
Personally, I make my own save/load functions that manipulate dictionaries, then toss them into a UserDefaults key.
An example hierarchy of how I store dicts in UserDefaults (there are many ways to go about this)
Key:
Items->Equipment->Weapons->WeaponName
Value:
{ dictionary of data like AP, Cost, etc }
This to me is easier than using NSCoder, and certainly easier than CoreData!! The goal is to turn everything into a dictionary, which you can then easily put into UserDefaults, which allows you to easily create plists or save to the cloud.
With the above key system, you basically just do for loops to find what you need, parsing out each section.. so a function to display all equipment, you just load the entire UserDefaults.standard.dictionaryRepresentation, then search for keys that only have 'Items->Equipment' and so on.
Hope this helps!
I have some functions from my game I could share if you need more tips.
UPDATE:
If you are making this an online game, I would focus on learning the essentials of persistence and cloud usage first, then port it to a more secure platform that uses encryption.
Your best bet will be to create your own servers and transfer data that way. That will give you exactly what you want, when you want it, and with the security that you want.
If you want to use GameCenter, I would use GC as an in-between layer of something custom you created, so that way you can filter out the cheaters' scores / have more flexibility.
Related
My ultimate goal: I want to automatically log my weight to healthkit on my phone every time I step on the scale.
Secondary goal: I don't want to spend much money, but I do want to learn. I'm a computer engineer and enjoy building hardware, but I don't just want to buy an off-the-shelf smart scale and install their app. Part of this comes down to data security: I don't like my information (especially health information) being in somebody else's hands.
Tertiary goal: I don't want to need an apple developer account (e.g., to roll my own on-phone app or something) but if I have to compromise here I will.
I want this to be as painless a process as possible - I've seen solutions that involve setting up a shortcut to ask for your current weight, etc, and I don't love that: less user interaction is better.
I'm usually wearing my apple watch when I weight myself, but might forget my phone (it's often right after a workout before I shower), if that helps.
I've seen a lot of questions on here talking about querying data, and I don't care about that: I'll use the phone to look at historical data, but I want to enter it automatically/painlessly so I have some data to look at.
For instance: I could set up an NFC sticker to open a shortcut, and maybe the process of scanning the sticker holds my phone in a specific place to read a QR code output by the scale? I honestly don't know if scanning a QR is possible in a shortcut, but this is just an example. The downside here is that I would need my phone on me.
The most promising solution from what I can tell is to build my scale to send the weight to google fit's API, and then install the google fit app on my phone to sync automatically. The downside is exposing my info to another service (google fit) rather than just keeping it in HealthKit, which I'd prefer.
Is there an auto-magic URL format that could trigger an iOS behavior to enter the data?
Is there an API through iCloud to just submit the data and not query it?
Is there a bluetooth option to talk to my watch and sync data there?
I see a lot of questions for querying/reading HealthKit data, but very little on submitting/saving it - so I'm not sure what's possible.
Suggestions appreciated!
I am developing an iOS notes app where a user can add image, text, audio and drawing notes. I want to implement iCloud synchronization between multiple devices. Out of the 3 options (key value, document storage, CloudKit) which one should I choose? I would like to implement the sharing of notes (collaboration) among users as well. I am using core data as my DB currently.
Key-value storage is out of question due do being too limited for your goal, document storage is only recommended when you need to handle and store the document as a whole. Since you are already using CoreData for local storage, it only makes sense to use regular CloudKit with it for cloud storage and sharing.
Synchronisation of CoreData and CloudKit can be tough. I am personally using a combo of RxCoreData and RxCloudKit libraries which provide some relief in synchronisation and some syntax sugar too.
A word in advance about uniqueness constraints: for CoreData, you define them based on key(s) or hash of all values, for CloudKit it is only possible (and also required) for the CKRecord key, to the best of my knowledge. So it is best to take care of it from the very start.
IMHO, CloudKit is the only opinion :)
I have a note app named marknote. And at the beginning I used iCloud document storage. It worked sometime, but not stable. The worst thing is, when and whether your documents can be synced is out of your control, instead it relies on Apple's daemon service. It becomes even worse when your documents are a little big, for instance several mega bytes.
So after fighting for some time, I changed to CloudKit. As #maxim-volgin has already pointed out, the implementation of CloudKit sync is tough, but it is reliable. And all headache just gone after switching to CloudKit.
I'm looking to develop a game in Swift with the following potential requirements:
It can be played offline
It offers the ability to save and reload from other devices
Boolean data values can be accessed by the game to determine whether the player has unlocked the levels in the game, as well as what they purchased (how much gold they have, items unlocked, etc).
The data cannot be tampered with (I wouldn't want someone to give themselves gold or unlock strong weapons without rightfully purchasing them)
I believe KeyChain is a good approach for this for the following reasons:
I can store data as key/value pairs and access them offline (great for my true/false values for what levels/items are unlocked)
The entire collection of data can be exported as a name/value pair, thus I can send the entire dictionary (in just a few lines of code) to the user's account using CloudKit/GameCenter, and then have it synchronize to other user devices (by importing that data).
It offers very robust security
It feels weird for me to use KeyChain as a poor man's database but for the above reasons I listed I think it could work.
The problem is I could see the name/value pairs being somewhere in the 200-300 range. Would you say storing this many values in KeyChain is a bad decision? It doesn't seem particularly slow (yet).
An alternative to me is to use a database, however many of these values are simply true/false (what levels are unlocked, sword-1 unlocked, armor-3 unlocked, total gold acquired, etc). I suppose the game could grow where it suddenly becomes too robust to manage all of this using KeyChain (and instead I encrypt the database using SQLCipher) but I wanted to ask the community's opinion on storing this many values in KeyChain.
Thanks!
You can just store your info in a plist, csv or database or any other kind of file in the documents directory and encrypt. Store the key in the keychain and use they key to decrypt the file. There are plenty of cryptography pods that will do this part for you so you don't have to deal with the joys of C interop with common crypto.
So I'm looking to create a niche market static yelp-style wine/dine restaurant finder. After asking around, I was suggested to look into cloud kit since apple provides asset storage and synchronization. I'm looking to provide misc. information in regards to restaurants: menu, location, open times, images; this information will be provided by myself.
With that in mind... here is the problem. It appears that cloud kit may be the perfect fit, unfortunately beginner-type tutorials are insufficient in explanation or outdated (obj-c - I only know swift). So for one, would you be able to recommend any tutorials?
Nonetheless, here is the meat of the question...
I am aware that a public database held in a container will be able to hold numerous bits of information: strings, integers, assets, date, etc.
However, would cloud kit (as in, off the device), hold the entirety of instances of a struct in the cloud or are only variables held? In other words, what is held on the device and what is held in the cloud when using cloud kit?
Also, should I create a iCloud id just for development usage or should I use my existing iCloud if?
Jon,
There is a lot of questions here.
1. CloudKit is very easy to get started, but a little bit of a challenge to use in a real app.
2. You can store basic types + assets in cloud kit; an asset being an image for example or just a glob of data.
3. The https://www.raywenderlich.com/video-tutorials#cloudkit website has a number of good tutorials on the subject in SWIFT, although no its not free, you need to pay a small monthly charge; although its worth it if you want to learn more about code development.
I'm working on a new iOS game and would like to save the progress online through iCloud. The saving is fairly frequent, but they are mostly doubles and ints with scores, unlocks and NSDate numbers.
Since internet will be required to play the game, I would like to save the game only online, not locally. When the app opens it should download the saved game from the server, and when it's done, launch the game.
Online currency will be sold through IAP, so I want to make sure the progress will not be lost even if the game is deleted and reinstalled, and of course, be available on multiple devices from the same user.
What's the best way to do this? Is it possible through iCloud? Key value seems like a mess because it doesn't sync quickly with iCloud. So Core Data?
I'm not looking for code to copy and paste, but I would like to create a discussion to find out the best solution for this case, if it's iCloud, setting up my own server, etc. Everywhere I look people say different things, but so far I couldn't find the best solution.
Thanks!
In iOS 8, you might want to look at GKSavedGame — it manages saved games associated with a Game Center player and syncs them through iCloud.
Otherwise, direct use of iCloud APIs sounds reasonable for your use case. If you write a small, well-defined set of values, the ubiquitous key-value store is very easy to use. If your save games are more complicated, write out a property list or encode your custom objects with NSKeyedArchiver, then use the NSFileManager APIs for syncing the resulting files through iCloud.
Going for Core Data or CloudKit is also possible, but sounds like it's more complicated than your game needs.