What I do is store the data in an obscured and verifiable fashion in memory and on disk, only decoding it when necessary. As mentioned in the comments, this will not prevent alteration, but it will deter it.
For example, an obscured value could be a struct that stores the number twice, once normally and once altered by, for example, XOR-ing it with some key. Then any program trying to alter the value in-memory or on disk would find the first, and modify it. But the next time you try to access the value, some property fetcher would check to see if they match. If they don’t, just forcibly quit the application. That may be sufficient to deter most casual hacking. It would also help if you cryptographically sign your save data and verify that when loading.
However, you also have to worry about users modifying the app code, which would allow them to bypass any check. And if they can see the source decompiled, they can easily determine where your check is and either disable it or figure out how to change the value in the save data on disk. So you would also need to try to obscure your “currency“ class and methods, as well as your loading code so they are harder to find and modify. And if they do manage to modify the game, you can check whether it’s been modified on Android at least by ensuring the signature on the installed package is the one you expect it to be (again, in an obscured way). If not, then you can again force quit, or behave slightly differently so that casual hackers don’t notice anything immediately.
None of these will completely deter someone determined enough, but everything combined together has helped stop many of the problems with games I’ve worked on.