Comic Books iOS App Programming

Book Binder Update

My comic book collection iOS app continues to evolve. I continue to strip out features and focus on the core mission: Buy a comic, snap a photo, add it to your collection.

With that in mind the UX now looks a lot like a photo app that has been preconfigured for storing comic book metadata. Here are the most recent screen shots from my iPhone XS Max:

Summary View

The Summary View displays a scrolling list of series. Each series displays the covers you have photographed. Right now I’m using placeholder covers–I don’t actually own the original Superman comics from the 1930s! This is all built with standard UIKit UIViewController and UICollectionView. I’ve added a custom UICollectionReusableView for the header of each section (series) and for the last cell of each collection I’m using a custom UICollectionViewCell.

I sort the comic book covers in each section by ID, which is a mashup of issue number and variant string. I sort these strings using localizedStandardCompare so that issue 2a comes before issue 20. I love localizedStandardCompare because I didn’t have do any work to solve the thorny “sort strings with numbers and letter as if they are numbers” problem.

The custom collection reusable view is mostly there to display the publisher, name, and era of a comic book series but also to host an edit button that brings up an EditSeriesPopoverView.

Popover views are cool but no longer supported as a presentation type by UIKit so you have to manually display them. I use a UIVisualEffectView to blur out the background behind the popover. I love it when I don’t have to write code!

Detail View

Each detail view displays a large image of the comic book cover photo and some metadata around it. The UISwitch sets the alpha of the cover image to 0.3 if false and 1.0 if true–this give you a nice visualization of what you still have in your collection and what you have sold.

The Edit button brings up the EditIssuePopoverView. I’ve figured out how to pass functions so that I can reuse popover views from different buttons: add an issue vs edit an issue. That’s very cool and has ramifications for how hardcoded view controller need to be to views.

Cutting Scope == More Value

This app looks nothing like my initial conception and has far less functionality than I thought I needed. I find this to be true with most apps I download. They do too much and don’t focus enough on their core use-case. Too much scope means the value of an app is diffused like the pixels behind a UIVisualEffectView.

As always you check out my code on GitHub!


Apple’s Snakes and Ladders Swift Example

All the cool kids are learning Swift this week! Not wanting to be left out I downloaded Xcode beta 6 and the iBook introduction and started following long with the code sample in Xcode’s new playground.

Swift is impressive as it fixes a number of issues with Objective-C by replacing it wholesale with a modern language that feels like a well thought-out JavaScript. I love the idea of optional values, implicit types, simple generics, and how let and var are used to create immutable and mutable objects. I’m still digesting the class vs struct vs enum idea. I’m not sure why they are just not all classes but give me time, I’m sure I’ll see the light.

The Snakes and Ladders example is a fine bit of sample code and shows off Swift’s power as a teaching language. But I had a couple of issues with it and tweaked it. See below and check out the comments for why I did what I did. You should be able to copy and paste this code into a playground and watch it run!

// Playground - noun: a place where people can play

import Cocoa

// Snakes and Ladders Game

let finalSquare = 25 // 25 squares in the game (0 to 24)

// init all squares to 0 and included a 26th square (to simplify the code)
// Note: you can use var or let to define the array board (Apple uses var, I'm using let)
// - let makes board a constant but values of a const array are still mutable
// - its the number of elements in the array that are immutable
// - using let allows the compiler to optimize looking up values in the array
let board = Int[] (count: finalSquare + 1, repeatedValue: 0)

// in 4 squares as ladders (+ values) and 4 squares as snakes (- values)
// ladders move the player forward by value in square
// snakes move the player backward by value in square
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

var square = 0   // player position
var diceRoll = 0 // moves are determined by an 8-sided die

println("Game Starting at square \(square)")
// start the game
while square 


I could not get the sweet while loop with the label "gameLoop" to work in the playground. It's no surprise that Swift has a couple of bugs. So with a bit of fiddling I got it to work with a gameOver flag. And while I was at it I created a swifty function to hide the ugly arc4Rand_uniform() call and refactored the print statements so you can follow along with the game in the console.

func randNum(limit: Int) -> Int {
  return Int(arc4random_uniform(UInt32(limit)))

// start the game
println("Game Starting at square \(square)")

var gameOver = false

gameLoop: while square != finalSquare {
  diceRoll = randNum(7)
  switch square + diceRoll {
  case finalSquare:
    // diceRolled moved player to finalSquare
    // game over
    print("starting \(square), ")
    print("diceRoll \(diceRoll), ")
    print("destination \(square + diceRoll), Game Over")
    // Swift didn't recognise the loop's label so I commented it out
    // This teams the break only breaks out of the switch
    // The gameOver flag is used to break out of the while loop
    gameOver = true
    break //gameloop
  case let newSquare where newSquare > finalSquare:
    // diceRoll moved player beyound finalSquare
    // roll again
    print("starting \(square), ")
    print("diceRoll \(diceRoll), ")
    print("destination \(square + diceRoll), Re-roll")
    // Swift didn't recognise the loop's label but in this case
    // it doesn't matter...
    continue //gameloop
    // valid move
    // update board
    print("starting \(square), ")
    print("diceRoll \(diceRoll), ")
    square += diceRoll
    print("destination \(square), ")
    print("instruction \(board[square]), ")
    square += board[square]
    print("result \(square)")
  // if flag is set break out of the while loop
  if gameOver {