JTSSwiftTweener: Animating arbitrary numeric properties

UIView.animate() and CoreAnimation provide an excellent framework for animating changes to visual properties of views in iOS. However, what if you want to animate a non-visual numeric property? Some examples might be:

  • Animate the properties of a CIFilter.
  • Animate a number changing within a label.
  • Animate ANYTHING which isn’t a UIView or CALayer property.

There are some hacky solutions you can do such as making a custom CALayer subclass which uses a delegate callback to report the setting of some property. However, this is cumbersome to set up and maintain, so I created my own tweening library to fill in the gap.

How it works

Tweener is a fairly simple class. It has static methods for creating tweens as well as pausing and resuming animation. At it’s core is a CADisplayLink which provides “ticks” that drive the animation. The core measures the elapsed time since the last tick and advances each of its child animations by that amount. This approach allows animation to finish in constant time, even when the frame rate is fluctuating.

When the Tweener.tween(...) method is called, a new instance of Tweener is created and returned. Simultaneously, it is added to the internal array of managed instances so that it can receive ticks. If the CADisplayLink is paused, it is unpaused.

With each tick, the individual Tweener instances are told how much time has elapsed. They, in turn, calculate how far along through their duration they are and update their progress closures appropriately. If a Tweener instance determines that elapsed time has equaled or exceeded its duration, it calls its completion closure (if it has one) and flags itself as complete. At the end of every tick, the Tweener class scans its instances and removes the completed ones. If the number of active instances is reduced to zero, then the CADisplayLink is paused.

There is only one class file to include in your project, available here.

I also have a very simple example project for you to look at.

What’s next?

The two primary areas for improvement are:

  1. Performance – It seems to work pretty well, but I’ve not done extensive testing on the tick methods to ensure maximum efficiency.
  2. Additional Easing Functions – I only have two Easing families at the moment. There are dozens of variations documented online (see here), and adding a few more to the class would improve its flexibility.

String Obfuscation

Online services and APIs are an inseparable part of most apps. Often they require the use of a secret key to identify the subscribing client, usually is in the form of a long string of alphanumeric characters. Invariably, it would be a bad thing™ for a malicious user to get their hands on this key. Perfect security is impossible, but there are some simple steps you can take to make it more than trivially easy for snoopers to extract your API keys from your app.

strings: A Snooper’s Best Friend

There is a command line app called strings that is designed to scan binary files and print out anything it thinks is a string embedded within. Here’s the description from the man page:

Strings looks for ASCII strings in a binary file or standard input. Strings is useful for identifying random object files and many other things. A string is any sequence of 4 (the default) or more printing characters ending with a newline or a null.

Here’s a tiny bit of the output when I pointed it at the Photos application binary:

Burst favoriting action doesn't currently support the 'none' option
-[IPXChangeBurstFavoritesAction _revertFavoritingChanges]
***** Burst action: _revertFavoriting changes
Will undo/redo for Keep Selection option
Total: %ld. Trashed: %ld. Untrashed: %ld. Pick type set: %ld
Will undo/redo for Keep Everything option
Total: %ld. Fav: %ld. Unfav: %ld.
Warning: burst == nil
-[IPXChangeBurstFavoritesAction _setFavoritingOnVersion:stackPick:]
Invalid state: version exists in both favorite and unfavorite sets for action.
Burst Change - Favorite: %@
Burst Change - Unfavorite: %@
-[IPXActionProgressController endModal]
-[IPXActionProgressController performActionSelector:]
Invalid selector
-[IPXActionProgressController checkModalSession]
Progress window still visible after action complete. Possibly hung? Action log:

In the case of the Photos app, strings found 38675 string candidates. A lot of them were garbage, and there were literally thousands of Objective-C selectors, but there were also a lot of strings that were obviously never intended for user consumption. If it’s a string in your code, it will be found by strings and you can bet that someone snooping for API keys has pattern matching schemes that will make them trivial to find.

Obfuscation Basics

The easiest way to prevent Strings from finding your API keys is simply to not include them as strings. However, do not think that putting a sequence of ASCII bytes into an array is going to help you, if your array’s bytes match the ASCII codes for the characters, you’ve just made a cumbersome string and it will probably still be detected as such.

A good first step for obfuscation would be to mutate those bytes in some way so that they don’t all fall within the ASCII alphanumeric range. The two simplest, non-destructive ways of doing this would be:

  1. Invert the bytes by subtracting them from 255. So, a value of 10 becomes 245 and a value of 50 becomes 205, etc. Note: this is identical to using XOR with a nonce of 255.
  2. XOR each byte with a single-byte “nonce”, which is just random number between 1 – 255 (XOR with 0 produces no change). XOR is a reversible operation: if you XOR with a given byte twice, you end up with your original value. In practice, you’d want to pick a nonce byte that has at least 3 of the 8 bits as 1s to ensure sufficient mutation of your API key bytes.

Then you would simply store the converted bytes in your app instead of the string and convert it back to the string by reversing the operation at runtime to produce the original string.

To be quite honest, either of these approaches is probably good enough. But if you want to be more thorough…

Multi-byte XOR

If you are using the single-byte XOR approach from above, your API key would be safe from a simple strings search, but there are still only 254 ways you can possibly obfuscate the string and a really determined snooper might still be able to find it. Let’s make their job exponentially harder and use a multi-byte nonce!

The basis for this approach is a new Sequence type I created called RepeatingSequence. The general idea is that it initializes with any collection type and returns the elements in sequence, wrapping back to the first element once the last one has been emitted.

[gist https://gist.github.com/JoshuaSullivan/be8e02b9ad7a03377075791e36160610]

This lets us use a sequence of random bytes instead of just one. I created a Playground that you can use to generate a multi-byte nonce and use it to encode a string. Then, just include the byte array it prints out instead of the string in your app.

[gist https://gist.github.com/JoshuaSullivan/92472caefc789554863d429764ad0b59 file=”StringObfuscationPlayground.swift”]

Reconstituted Bytes

Of course, that byte array isn’t going to do you any good unless you can turn it back into a string. Here’s a struct with a static method that does just that:

[gist https://gist.github.com/JoshuaSullivan/92472caefc789554863d429764ad0b59 file=”ObfuscationDecoder.swift”]

This code is pretty simple to incorporate into your workflow and can give you a lot of peace-of-mind that your app’s API keys won’t be trivially easy to steal.