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

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.

Related

Do apps need a purpose to be accepted by the App Stores?

I created an application that simply spins a picture of my goose on screen. That's it. Nothing else.
Could that be deployed to the app stores?
Context:
I'm a mobile app engineer for a few different companies (finTech, gigEconomy, social...) and all of our applications have very specific use cases for the end user.
I'm also an artist in my own time, and have built a few different apps that help people make art.
For each of these, the data gathered by the apps must be well documented and explained to the end user to be accepted by the App Store along with the purpose of that collection.
That's got me thinking, would the Apple App Store accept an app that does not collect any user data at all, but also has no true "purpose"? (Google Play Store too, though I expect their review process is so easy you can get just about anything up there anyways...)
I haven't found any relevant answers to this question online and might test it just for fun, but would love some insight by other curious developers if they have tried uploading apps just for fun
You might find the following section from the official App Store Review Guidelines useful:
4.2 Minimum Functionality
Your app should include features, content, and UI that elevate it beyond a repackaged website. If your app is not particularly useful, unique, or “app-like,” it doesn’t belong on the App Store. If your App doesn’t provide some sort of lasting entertainment value or adequate utility, it may not be accepted.
Does this include a purpose or does this mean that an app like you have suggested in your scenario gets rejected? Absolutely not.
After a quick google search, I have found the IsItMyBirthday app which lets you pick a date and it tells you if that date is today. Is this useful or unique? We could just look up the date in the calendar on the phone itself.
However, this could be considered a 'joke' and a joke has a purpose and might be considered as unique. An app that does nothing, could be considered a joke as well. It has a purpose and Apple might agree or disagree.
In my experience it can be very random on why an app gets rejected or accepted. For example one of our apps got rejected in version X for reason Y. We released a new version without changing Y and it was accepted.

Ways to protect my framework in Xcode?

We intend to sell our framework on the net ,and it needs to be protected in a matter than if someone buying it, he can't put it on the net, or give it to other developers .
We dont want to find it all over the net after a few months.
I had a few ways in mind but each had its catch .
Give a unique ID to every developer, and program that id to the framework, so he must enter that to use it. problem is ,that he can give the framework with the id to anyone .
Ask for the device number and enable only that device in my framework for each developer. problem here is that when he put it on store, all users cant use that since they have others device id.
Use the net to check some how(??) which i preferred not to limit the users to that need.
I can program each framework with a code, that only me can extract, so when i find it on the net i can be sure what dev put it in there (it doesn't help-i cant sue everyone)
Is there any other way to make the framework per developer but also let it work on all other users at the appstore when its there ?
Thanks .
#diederikh makes very good points, and NicolasMiari also provides good insight. The best answer IMO is a combination of these two. (While keeping in mind diederikh's excellent advice that your goal is to come up with something simple that will make things hard on legitimate customers.)
Rather than recompiling your entire framework for every customer, you make your license key depend on their bundle identifier. They send you their bundle ID. You use your private key and sign their bundle id. This provides you a hash that you send to them. Now, at runtime, your framework uses the public key (which is not sensitive; you could publish it anywhere) to verify your signature. See SecKeyRawVerify() for doing that on iOS.
You can use this approach to create time-limited keys. Just include time stamps in the signed data.
Using this approach, you could, if you wanted, let customers test your framework indefinitely by using your bundle identifier. You would make a signed hash of that identifier available to trial customers. But as soon as they want to upload to AppStore, they would have to change the identifier and pay you for a new signed key.
There certainly is a way to get around this. Attackers could modify your framework to ignore the signature verification. But that's always true, and preventing that is better done with lawyers after the fact than with DRM that will only likely cause trouble for paying customers.
Look at how PSPDFKit does it. If you want to use it out of demo mode you'll have to call a method with an unique ID. This ID will enable functionality which is not available in demo mode.
You can also sign (with the codesign tool) the framework with an unique certificate for each customer.
I would not worry too much; if will always find a way around your locks.

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.

Time limited Shareware

I'm thinking about making a time limited full version of my App, so users can try it for example 7 days.
Is there a recommended Obj-C library?
First of all do not store your data inside the app. Otherwise it would be simple to delete the App from the filesystem and download it again restarting the countdown. Store it in User preferences or Application Support instead. You could use a plist for this. You could also use a hidden file if you like. Just don't make it too complicated or too invasive. Your app can be cracked no matter what security measures you use in the end. Just be fair enough to the end user that could be a prospective customer.
Here is a very nice link on the topic of implementing a time-limited trial in Cocoa:
http://lipidity.com/apple/shareware-licensing-techniques/
Without possibility to protect your data from manipulation/deletion it is not possible to do.
How do yo check, if it is first start of your application, if all your data is wiped out.
Some alternative is "hardcoded" id token and connection to the rest of world (at least for first start, to grab any kind signed data key)
Maybe you could use a server where store UUID's and first time they launched the App. Then, Each time they open your application, it asks your server if they can use it or not. It's harder than store dates on device but if you do that, users will access your application simply changing the date of the device on Settings.

Avoid running of software after copying to next machine?

I have developed a small software. I want to provide and run it commercially only. I want it to be run in the machines who have purchased it from me.
If someone copies it from my clients computer and runs it in next computer, I would like to stop functioning/running the software.
What can be the ways to prevent the piracy of my software?
Adaption of one of my previous answers:
There are a few ways to "activate" copied software to try to stop casual copying of the application.
In the most simplistic case, a registration code ("CD key") purchased from you, possibly via your website, and it is sent to the user who enters it into the program or installer. The whole process can basically be done offline; the program itself locally determines that the code is valid or invalid.
This is nice and easy, but it extremely vulnerable to key sharing - since there's no "phoning home" then the application cannot know that thousands of different people are all using the same key that they got off the internet or a serial library or their friend. It's also reasonably easy to make "keygens" which generate valid-seeming keys that were never actually issued by the developers.
Then we get into online registration. You still have some kind of code, but the program will phone home back to the server to determine whether the code is valid and usually unique. This stops basic key sharing, because the company knows if too many people from all over the world are all using the same key. Perhaps there is some kind of identification involved using MAC address, too, with infinite registrations allowed on the same hardware but maybe a limited number on what appears to be a different computer.
This is still pretty easy and stops simple key sharing. People will actually have to get into cracking the software or faking the server response to get past it.
Sometimes the program itself is partially/mostly encrypted and is only decrypted by the online registration step. Depending on how well this is obfuscated then it can be pretty difficult and time consuming to crack. Bioshock was a high-profile example of this - debuting with a brand new encryption/copy protection scheme that took around two weeks from release to be broken.
Finally, a particularly guarded application might stay in constant contact with the server, refusing to work at all if the connection is severed.
If you know for sure that all your users will all have reliable internet connections then it can be considered quite a strong way to protect the app, at the cost of privacy and some user distrust of the spyware.
In this case to get around the activation they would need to fake the server itself. Steam emulators and private WoW servers are an example of this.
And in the end, nothing is uncrackable.
In a nutshell: you can't.
Even very sofisticated systems (e.g. dongle keys) can be circumvented.
I guess your best call is to give a code to your customers and have an online check for that code, so that it cannot be used twice.
Of course, that can be circumvented too but...
As nico said you really can't.
A simple solution might be to generate (registration/activation) codes that are based on hardware or software installed on the particular computer - eg video card serial id or c:/windows creation time.
I have one idea may be it works.
What we can do, we will make an encorrupted database field and that field will be empty for the first time as soon as i install my software to some machine it will read the Mac Address + Mother Board Serial + Processor ID and make an encorrupted value with the combination of these three and write in to that field which i left empty for the first time use.
After that every time my application will read these three values and recreate the encrupptted value in the same manner and compare with the value of that database field. If the value of the database field and the value of the regenerated encrroupted field is equal, that means the computer is same other wise it is installed on some other machine in this case you delete all the code and can make the system unstable to punish the person also :) ...
Please let me know about your opinion about this idea.
The best way is to use some sort of hardware-locking in which your license code contains encrypted info about the machine on which it will run. Your software will then check for this info and match it with the current computer and if the match is successful, the license is deemed valid.
Sure, any scheme can be cracked by someone on the face of the planet, but that does not mean you shouldn't use a protection scheme.
If you are looking for a ready-made scheme for this, have a look at CryptoLicensing.
Companies such as ours (Wibu-Systems), Safe-Net, and Flexera (expensive) offer dongle-free solutions as well as ones based on hardware. But _simon was right in that a dongle is the only iron-clad protection. All software-based systems can be cracked; it's just that some are more difficult than others. Really good hardware-based solutions are effectively uncrackable. No one has yet cracked the CodeMeter stick unless the implementation was flawed.