Rendering Code As Styled Static Text

I’ve been working on and off with a couple of friends on a pet project of mine called Interview Flash Cards (IFC for short), designed to be a flashcard tool for iOS developers who are preparing for an upcoming interview. I’m planning to open source the project so other developers can weigh in and add whatever questions and answers they feel may be beneficial to the iOS community, but… I haven’t made it very easy to do that 😓. Not yet, at least — but soon.

The Problem (one of them, at least)

In IFC there is an Algorithms section, where you are given a coding challenge like

“Find the product of the three largest numbers in an array of Integers”

Then, when you flip the card you see the solution. But the solution is an image stored on AWS that’s fetched when needed. This makes it too difficult for other people to add algorithm solutions as they would need access to my AWS account. Additionally the images will go stale as the Swift language evolves and styling convention changes. So ideally we’d want a method of adding questions and solutions that makes it easy to maintain the answers. That sort of rules out using static images.

Possible Solution

I realized that submitting a PR where the algorithm solution is just a string, would make life easier for everyone. The only problem was that I needed a way to style the string so that we’d have some syntax highlighting like we’d have in Xcode. That’s where SyntaxKit, by Sam Soffes, comes in 🏅— although, for IFC I forked off of Alexander Hedges’ fork, which he has updated to support Swift 3 🙌🏾

SyntaxKit has many uses, but for this particular case I was only using its AttributedParser, which takes in a string and styles it based on a TextMate Language (tmLanguage) and TextMate Theme (tmTheme) specified in its initializer. The styled string is returned as an NSAttributedString, fully decked out with syntax highlighting. Some of the highlighting isn’t perfect, but for what it does, it deserves plenty of praise.

While SyntaxKit does a good deal of the heavy lifting, it does still leave formatting to the developer — which means I needed to figure out indentation and when to add new lines.

Attempt #1 ❌

I tried having the code sample be a string, for which I’d scanned for { }, (), and [] symbols to determine when to line break or move the indentation values up. So for example in

“func play(videoGame: VideoGame, players: Int) { let weapons = [\“pew\”, \“pew-pew-pew\”, \“boom maker\”, \“noob tube\”] … }”

we’d have to add a new line after the first {, then be sure to indent 4 spaces since we’ve now entered the first level of indentation, then in the defining of weapons we’d have to move to a new line after the first entry, but mark the position of the first [ so that then next line starts under it and so on.. That’s too much to juggle and will undoubtably be slow. Attempt 1: failed.

Attempt #2 ❌

Have the user who is submitting the code sample, just add newline character where needed and have them leave all the spaces in the code. Something like:

“func doIt() {\n    let stuffToDo = [“eat”,\n                    
“sleep”,\n “repeat”]\n for thing in stuffToDo {\n do(thing)\n }"

As you can see here, this is a mess. Not only would it be a major pain for someone trying to submit a simple code sample, but it’s also a giant formatting error waiting to happen. All those spaces left in, makes it impossible to track how many spaces to use and determine if things are misaligned. Just for the amount of effort it would take a user, Attempt #2: failed.

Attempt #3 — Serialization! ✅

If you add a quotation mark to the top and bottom line of your code, you’ll notice something.. It does not turn into a string. And that’s because of the newline characters in the file. But it turns out, we can serialize our code into string really easily. This is the method that finally succeeded as you’ll see.

  • press option + command + f (find and replace) and use regex mode
  • first up find:\n and replace with:#N
  • then find: {4} (“one space”{4} — the regex for exactly 4 spaces), replace with:#T
  • almost there, find:\” and replace with:\\” and find:\\n and replace with:\\\\n (this takes string newlines: \n and turns them into \\n)
  • last up, add quotation marks around the blob of code

These steps take this code:

code sample

and turns it into :

serialized code sample

And the best part is, to deserialize the code so that the string contains spaces and new line characters in the correct location, all I have to do is a find and replace using the StringLayoutHandler I’ve defined below. (lines 30–37):

What does this mean?

Armed with this new serialization and deserialization technique, I think we’re well on our way to making it easy to become an IFC contributor. Not only that, but it’s also pretty bad ass to see string-ified code get rendered on a device as it would if it were in Xcode 💯.

See for yourself:

rendered code string

Want to see the code?

As always, feel free to comment and thanks for reading! ☮ &❤️