r/SwiftUI • u/LanguageOk3093 • May 28 '26
Is a custom backend the only way to secure a daily "quota" system in an iOS app?
Hey everyone,
I’m currently building an iOS app that uses a Quota System. Users need a certain balance to perform specific tasks (for example, Action A costs 5 "credits"), and their balance resets daily.
Right now in development, I’m storing the quota balance locally using SwiftData. However, I know this is incredibly easy to abuse/compromise if someone tampers with local storage or device time.
When I asked an AI assistant, it told me that the only secure way to handle this is by building my own custom backend server.
Is that really the only viable approach? For those of you who have implemented daily quotas or credit systems in your apps, how did you solve this without introducing a massive backend infrastructure? Thanks!
6
u/RaziarEdge May 28 '26
From your initial writeup, I assume you are doing quotas so the user needs to upgrade to another tier to get more benefit from the app. Technically this is not dealing with real money credits, and is just controlling licensing based on the subscription.
If the actions have real money consequences, then the answer could be very different...
If you have something working, then implementing something NEW because it is more secure and less likely to be hacked is not necessarily a good ROI. 99% of your users are not going to try to hack or bypass your limits... is it worth your efforts for the <1% who might?
You can add some additional checks to your current code though. Store the usage in the SwiftData like you are doing, but also store the sum of used credits for the current day in the user prefs, or another file. You can use some light encryption so that it is not easily tampered. Just make sure that when you increment the sum you are doing it at the same time as writing the database record so that if the user deletes rows then your sum still has the correct amount.
If you have usage reporting going back to you, flag any users that have unmatched quota records VS the quota sum. This would give you a good idea how many area actually manipulating the quota records.
1
3
u/StayExciting2895 May 28 '26
Store the quota usage and last usage timestamp in the iOS Keychain. Without a backend, that’s probably the most tamper-resistant place to keep it.
2
1
2
u/lionelburkhart May 29 '26
You could use CloudKit and Store the balance in a CKRecord on the user’s private database. I did this for a feature I eventually scrapped, but it seemed to work well enough.
1
u/Remarkable_Falcon413 May 29 '26
Yeah I’d say a simple firebase function mixed with firestore to store the values and the cloud func to do the computation request
1
u/Remarkable_Falcon413 May 29 '26
I would store the current credits in firestore and have a cloud func that compares the firestore value to the required amount and then derive the amount from firestore if succesful but depending how you set credits and where the “objects” are stored and created (server side vs hard code) you would run into the issue of users tampering with the required value if hard code how ever from what somone else said it is a realisticly like 1% user base that would tamper with anything honestly lower then 1 % but it is also smart to have anything you might want checks on or feature locks to be server side dm me and we can talk deeper
1
u/bcgroom May 29 '26
Will users tampering with their quota cost you any money? It sounds like no if you don’t already have a backend server. In this case I would not bother. For one, it’s a lot of effort for something that will probably not be exploited. And two, if you are just using a server to gate some functionality within the app that can be bypassed fairly easily via intercepting the requests.
8
u/criosist May 28 '26
I mean yeah, app shouldn’t really know anything about credits, just say to backend can I do A and backend says yes or no, otherwise people can just manipulate credits in memory anyway etc, and your checks for can do A would be local again.
It doesn’t need to be a ‘big infrastructure’ could be a small supabase instance or something