Is an AssetURL guaranteed to be unique on a device? - objective-c

Currently with iCloud, it is possible for a user with iTunes Match to swipe to delete a track, or download a new track from iCloud.
Is it guaranteed that the AssetURL of a track will not be used again on the same device?
I ask as im creating my own cache of the library meta data. When I detect a change to the library, rather than rebuilding my entire cache, id like to be able to just detect removed tracks and newly added ones, and update my cache accordingly.

It seems to be a large enough number to be unique on a device for most intents and purposes. If anyone can confirm this I will change the accepted answer.

Related

How to figure out that two apps are on the same device on macOS(Alternative of identifierForVendor in macOS)

advertisingIdentifier is different for apps from the same vendor.
Of course there is an ability to add apps into group and share some "unique string". But I suppose that there must be some easier way.
I also read about "Uniquely Identifying a Macintosh Computer" but I suppose that such apps are rejected in mac AppStore.
In our app we access the system serial number. We use it to try prevent multiple users using the same account + for debug purposes (so not for ads or anything, our app has none).
We also have code to access the hardware uuid but that code isn't actually used at the moment, but it is in there, so not sure how deep Apple checks. So you might be able to use this one too. As an additional step you could hash either of these (or hash them appended or something).
This app has been on the AppStore for a long while now, and was never rejected for this reason. So I'd say accessing this data on macOS should be ok (for now) depending on usage and safe to submit to the app store.
Keep in mind that in some rare cases, the serial number will not be available. In that case we store a random string in UserDefaults.standard and use that cached value in the future.
Since this information won't be available to your 'other' app(s), this workaround won't work for you though.

Which is a better way: retrieve images from AWS S3 or download it and store locally in a temp folder to be displayed?

Problem: Retrieve image from S3 and load into UIButton.
I'm currently doing my research on this issue and can't seem to make up my mind. Which is a better way to do it in terms of performance and security issue?
Also, do I need to do caching or store these images in Core Data?
Thanks!
It depends on how you use them. If your app is going to retrieve the images similiar to instagram, or twitter, it's good to download them as the user requested the images via the app.
If once the images are retrieved, the application going to use the images again and again multiple times, it's a good idea to store the images after they are downloaded.
For example, let's think about an application about "social networking" concept, and this app, let's say, has a chat interface which is functional after users add each other. As the users add each other, you download the images of the users and store them on the device, then you can use the profile images of the users by retrieving the locally stored images rather than retrieving them from the server, thus, each time they chat each other, you dont use the bandwidth for nothing. And you should also use a push notification or something that has a similiar functionality for the scenario, when a user changes his profile pictures, all of his/her friends should also be notified (I mean the app is going to be notified by the server) to retrieve and update their local profile image with the newly added ones.
As I said, it depenends on the scenarios of your application/server relationship. If the images you are storing on the AWS S3 are only going to be used only once, then storing them is useless. And If the user is going to use some of the images frequently (like app is a social networking app, friendship app and the friends are going to see each other's photo or uploaded images frequently), then storing them is a good idea, only for some of the users that who has subscribed to each other.

Find out which version of app was first installed - iOS

I want to change my app from a paid version to a free with ads. However, I don't want the people that originally paid for the app to get the ads.
I was thinking I could include a new update that has extra code that sets up some UserDefaults saying the version doesn't have ads and then do the update to the free version. But that doesn't seem like a very reliable solution.
I think, it depends on minimum OS version, that your program can be run on. For iOS 5.0 and later you can try to use iCloud to store some flag about purchased version. It allows you to set this flag not only for one device, but for the user's account.
Or you can store this info in the keychain to get it later. But in this case your user will not be able to have no ADs on some other device with his(her) account.
Anyway, as far as I know, you need to create an update for your app first to write this flag anywhere. And only in some time make an update with ADs.
I will be glad to see comments if someone has another thoughts about this issue.
It seems there is no reliable method to detect whether someone paid for your app or downloaded it later when the price has been changed to free. For this reason I have decided to create two separate entries in the app store.

Proper handling of NSUbiquityKeyValueStore updates across devices?

My app stores a single key-value pair in iCloud using NSUbiquityKeyValueStore, an array of objects. The entire array is saved to iCloud when a change is made to any object in the array. This works great as long as each device has an opportunity to pull down the latest update before a change is made locally. Otherwise the local change can get pushed up to iCloud before other devices' latest updates have been pulled down, and those updates get lost across all devices. Is this my app's shortcoming or iCloud's shortcoming, and how can I prevent this scenario from occurring?
Otherwise the local change can get pushed up to iCloud before other devices' latest updates have been pulled down
I ran into a similar issue this week with a project I'm working on. I just made sure that I didn't push anything up to the iCloud server until I received my first update from iCloud. Also, FWIW, I set a fake key-value pair right after initialization so that it updates immediately.
HackyStack's idea of a local flag is also a good solution; if a change comes in you can ask the user if they want to use it or not. (sorta like how Kindle asks if you want to update to the latest page).
I'm not sure I fully understand the exact issue, but I believe the answer is either a category on NSObject (where you could have a "version" property) to check the "version" of the object OR you need another key-value pair to store on iCloud for "version" that can be compared to one stored locally on the device (lastUpdateVersion) to know where you stand. If you could give me an exact real world example of your problem I could answer better... It could be that you don't even need a "version" but rather a flag (BOOL).
You should read the documentation for -[NSUbiquitousKeyValueStore synchronize]. It gives you a decent idea of when to use it and what its limits are. In particular, pay attention to the fact that it makes no promises on when it actually synchronises the data, and implies that updates are uploaded to iCloud only a couple of times at a minute, at most (and that may apply to the device as a whole, not just your app).
The key-value storage mechanism is intended to be very simple and used only for non-essential data, typically configuration information about your app. You shouldn't store user data in it, basically, or anything that resembles it. For that kind of data, use the file-based iCloud APIs. They're more complicated, but with them you have more insight into the sync state of your data, and most importantly you can be notified of conflicts and provide your own merge handler.
Is this my app's shortcoming or iCloud's shortcoming, and how can I prevent this scenario from occurring?
This is an app shortcoming and expected behaviour from iCloud. You can account for this in various ways, but in general, this won't be easy. Especially with >2 devices, there are scenarios where conflicting changes will never be presented to a device to do resolution, as generally speaking the iCloud behaviour is "last change wins" (see my longer description below). Some thoughts:
instead of using an array of objects, use individual keys for each object. Obviously this depends on the semantics of your app, but if the objects are essentially independent, then this generally will give your app the behaviour it expects 🎉
if all the items are interlinked, then you will have to do your own conflict resolution. The best way to do this will depend heavily on your app + data semantics. E.g. maybe you could add a timestamp to your array, or to some objects in the array. You could use new key names for every save so that all devices eventually get all keys and can resolve conflicts (obviously this could chew through storage quickly!). Resolving conflicts might not be worth doing depending what you're already storing locally to help with this
Background
I recently had reason to research the topic of NSUbiquitousKeyValueStore change conflicts in some (tedious) depth. I found some information in two old WWDC videos that expand on current Apple documentation, specifically WWDC11 Adopting iCloud Storage, part 1 (currently available here, found via here) at locations 17:38 and subsequently (e.g. 19:27). Another is a WWDC12 iCloud Storage Overview talk (here originally via here) at 6:30 and 10:55. I subsequently verified the behaviour described below by running two devices, an iPhone 8 running iOS 15.2 and an iPad Air 2 running iOS 12.4 with a test program and lots of console logging in Xcode. What follows is my best guess of the intended behaviour and mechanism for conflict resolution.
Summary
When an individual key is saved by a device using NSUbiquitousKeyValueStore.default.set(value, forKey: key), a hidden timestamp is included with the key with the device time of that call. If/when the operating system syncs with the iCloud replica of the key value store, it examines the timestamps for each key and, if the iCloud timestamp is earlier in time, it saves the new key value and timestamp into the iCloud key value store. If the key value is saved, devices that are currently registered and online to receive notifications will be notified that this key has changed and can fetch the new value if they wish. If iCloud does NOT save the key value, NO notification will happen on any device, and the change is simply dropped.
Notes
If all devices on this iCloud account are online while in use (caveat low power mode, poor internet connection etc.), the result is generally exactly what you want: the app makes a change, it is saved in iCloud, it propagates to other devices. Notifications happen as expected, if a device has registered for them.
If device A saves a value while it is offline, and another device B later saves a value while it is online, then device A goes online, the change from device A is ignored, as iCloud now has a newer value with a later timestamp. B will never be notified of A's change. However, if A has registered for changes, A will get notified of the newer B value and can then decide if it should re-submit its value.
Because of this "last in wins" behaviour, multiple values that belong together should thus be saved together as a dictionary or array, as suggested in various Apple docs and talks.
Values that don't interact should be saved as individual keys - thus allowing most recent changes from multiple devices to successfully intermingle.
There is no automated way to test these behaviours. Back in Xcode 9 days, it was possible to UI script two simulators to verify sync worked as expected, but that hasn't worked in a while, which leaves manual testing as a poor and tedious substitute.
NSUbiquitousKeyValueStore is a great solution for many scenarios beyond simple app settings. Personally, I'd like to see more keys (e.g. 10k instead of 1k), but the general ease of setup and separated storage from a customer's iCloud quota is generally a joy.
There's no perfect solution in a real world environment where devices are not always reliably connected. Indeed, some customers may intentionally keep, say, an older iPad, mostly offline to save battery between intermittent usage. If you can keep your synced data in small discrete units and save it one value per key, sync will generally work as expected.

Is NSUbiquitousKeyValueStore certified to work without a connection?

The documentation doesn't seem very clear about this, but can I rely on NSUbiquitousKeyValueStore to persist data locally when the device is not connected, or should I also use a NSUserDefaults?
From the iCloud design doc with regards to Key Value storage
Always effectively available. If a device is not attached to an account, changes created on the device are pushed to iCloud as soon as the device is attached to the account.
(in the table at the bottom of the page)