D3-6 – Development

Here we are, you have defined your specifications for our app, the design is ready, it’s now time to implement it.

Zeplin

To help you designing your screens programmatically, you can export your sketches to Zeplin.
It can save you a lot of time as every screens, colors, and fonts are easily accessible.
The key-point I love in Zeplin is that it is very easy to see the constraints you have to implement in the iOS app as margins in pixels between each element is clearly displayed when hovering it.

The SOLID principle

To meet short deadlines, you have to have strong knowledge of programming principles, software architecture, and testing strategies.

You should try to reduce as much as possible coupling between your view and your logic, and extract your networking operations in a separate bloc. Also always keep in mind that you should design your game module to be reusable as much as possible to improve your productivity when starting a new project.

Some basic key concept of object oriented programming which sum up these ideas is the SOLID principle:

S : Single responsibility principle : each class is responsible of one and only one task (thus no UI should be displayed by your network interface class)
O : Open/closed principle : each module you develop must be Open for extension but closed for modification, that is the architecture should anticipate further changes and thus provide ways to add new related items in an easy way through inheritance for examples
L : Liskov substitution principle : a subclass must preserve the purpose of its parent class,  overrided methods or properties should have the same meaning as in parent class.
I : Interface segregation principle : Define interface by small unique roles, some class should be able to implement it and when doing so implement only methods useful to it.
D : Dependancy inversion principle : High-level modules should not depend on low-level modules, you must introduce some kind of abstraction through some interface. A great explanation can be found here : https://www.oodesign.com/dependency-inversion-principle.html

You can read more about the clean architecture in this Clean Architecture course.

These are general principles that can be applied to any software project.

We will apply these programming concepts to some concrete example: “Escape From Blindness”, a blind-users accessible Escape Room app.

Sample App Development: Escape From Blindness

The app principle is simple: you will have to go through different levels by solving problems related to the usage of VoiceOver; the core tool for providing accessibility to blind users on the iOS platform.

The application is a simple app designed to illustrate the core UIAccessibility library concepts.

There is four game modes in the game:

  1. A quiz with closed questions: you go to the next level if you select the correct answer.
  2. A quiz with open questions: you go to the next level if you type in the correct answer in the provided text field.
  3. Rotor based levels: we will use the VoiceOver rotor to validate answers and go to the next level.
  4. Custom Actions based levels: we will use UIAccessibilityCustomAction to validate answers, you must select validate all level answers in any order to validate the level.

All of the game will be usable only when VoiceOver is activated; if it is not we will indicate the user how to turn it on.

We will develop the app in the following order:

  • Define the Intro and game end screens explaining the game concept and thanking the user for playing
  • Define the VoiceOver explanation screen responsible for explaining that VoiceOver is mandatory to use this game, and explaining how to turn it on.
  • Define the GameFlow responsible of validating the answers and of delegating the next level / next chapter routing to the AppCoordinator component
  • Define the AppCoordinator responsible of routing through the
    different view controllers based on the chapters scenario.
  • Develop Closed Questions levels
  • Develop Open Questions levels
  • Develop Rotor based levels
  • Develop Custom Actions based levels

You can get the project’s source code here.

In this article, we will only discuss the Intro and Game End screens.

The first step is to onboard the user to the game concept, we will use the same module for both the intro and game end screens as we simply want to show texts sequentially to the user ; we will only change the data set and provide a “Replay” button for the game end screen to reset the game to its initial state.
As always we will only highlight the interesting points here, please refer to the GitHub repository for any additional info (XIB files, AppDelegate, resource files…).

class InstructionsViewController: UIViewController, Coordinated {
    var coordinator: AppCoordinatorProtocol? // 1
    enum Scenario {
        case intro
        case gameEnd 
    }
    var scenario: Scenario! // 2
    var interactor: InstructionsInteractorProtocol? = InstructionsInteractor() // 3
    private var instructions: [String] = []
    private var currentInstructionIndex = 0 
    private var currentInstruction: String? {
        return self.instructions[safe: self.currentInstructionIndex]
    }

    @IBOutlet weak var instructionsButton: UIButton!
    var timer: Timer?

    override func viewDidLoad() {
        super.viewDidLoad()
        guard let instructions = scenario == .intro ? interactor?.fetchIntroInstructions() : interactor?.fetchGameEndInstructions() else { // 4 return }
        self.instructions = instructions
        if let firstInstruction = instructions.first {
            self.replayButton.setTitle(firstInstruction, for: .normal)
        }

      // 5 
      EscapeFromBlindnessAccessibility.shared.post(
          notification: .layoutChanged,
          argument: self.instructionsButton )
          startInstructionsUpdateTimer() 
      }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        EscapeFromBlindnessAccessibility.shared.post(notification: .layoutChanged, argument: self.instructionsButton)
    }

    override func viewWillDisappear(_ animated: Bool) { 
       super.viewWillDisappear(animated) 
        stopInstructionsUpdateTimer()
    }

    private func startInstructionsUpdateTimer() {
        timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] timer in
            guard let strongSelf = self else { return }
            strongSelf.currentInstructionIndex += 1
            strongSelf.handleInstruction(strongSelf.currentInstruction)
        }
        timer?.fire() 
    }

    private func stopInstructionsUpdateTimer() {
        timer?.invalidate()
    }

    private func handleInstruction(_ nextInstruction: String?) { 
        if let nextInstruction = nextInstruction {
            setInstructionsText(nextInstruction) } else {
                timer?.invalidate()

    // 6
    if self.scenario == .intro {
        coordinator?.validateChapter() } else {
        self.replayButton.isHidden = false 
        }
      }
    }

    @IBOutlet weak var replayButton: UIButton!

    @IBAction func onReplayButtonTouched(_ sender: Any) {
        coordinator?.replay() 
    }

    private func setInstructionsText(_ text: String) {
        let duration = 0.5
        UIView.animate(withDuration: duration, animations: {
            self.instructionsButton.alpha = 0 }, completion: { _ in
            self.instructionsButton.setTitle(text, for: .normal) UIView.animate(withDuration: duration) {
            self.instructionsButton.alpha = 1 }
        }) }
    }
}

// 1: Here we will use the Coordinator pattern to act as a router between our different screens.

// 2: We define a Scenario enum to define which script we will load (Intro of Game End).

// 3: We store the script instructions in a dedicated collaborator class: InstructionsInteractor. We store the dependency as a protocol to be resilient to change: if we decide later to fetch scripts from a database data source, performing the change will be as simple as modifying the interface to send the result as a callback and swapping the implementation.

// 4: We fetch the correct instructions based on scenario.

// 5: This is the first interesting accessibility mechanism: we post a layout changed notification using under the hood: UIAccessibility.post(notification: UIAccessibility.Notification, argument: Any?).
You should definitely take a look at the documentation for the different available notifications.
Here we use the UIAccessibility.Notification.layoutChanged notification to notify VoiceOver that something interesting has changed on the instructionsButton view and that it should focus it and describe it. It is a good practice to choose the focussed element on each screen being presented in viewDidAppear(_:).
Another useful notification to post is:
UIAccessibility.post(notification: .announcement, argument: someString)
That would cause the text passed as argument to be read by the VoiceOver engine.

// 6: if it is intro screen, we route to the first game chapter. If it is game end screen, we simply reset the game.

The remaining code is simple view logic using Timer, we display the next instruction while there is one.
And the collaborator class that will simply return our scripts for both screens ; we rely on an abstraction (protocol) to allow us to swap implementations easily later (it could be fetched from a database, local memory or whatever, this is just an implementation detail):

protocol InstructionsInteractorProtocol {
    func fetchIntroInstructions() -> [String]
    func fetchGameEndInstructions() -> [String]
}
class InstructionsInteractor: InstructionsInteractorProtocol { 
    func fetchIntroInstructions() -> [String] {
        return [
            "Welcome to Escape From Blindness!",
            "You are an explorer of an old tomb!",
            "Your torch just turned off!",
            "This game is 100% based on VoiceOver usage!",
            "VoiceOver is an assistive technology that allows blind users to use their smartphones.", 
            "In this adventure, you will have to activate VoiceOver to progress!",
            "You will be faced dozens of unique levels to will use the assistive technology's features!", 
            "Good luck explorer!"
        ] 
    }

    func fetchGameEndInstructions() -> [String] { 
        return [
            "Congratulations!",
            "You successfully managed to go through all game levels!",
            "We hope you enjoyed this game as much as we enjoyed developing it!",
            "We also hope that this game helped you to understand better the challenges encoutered by
blind people while using smartphones :)"
        ] 
    }
}

To get a more detailed description of this app development process along with gaining knowledge on how to make your app more accessible, you can check out the iOS Accessibility Toolbox project.

Here is a review video (in french):

What’s next?

Now that we developped the full app, we still have some work to do before releasing it to the App Store.

See more in the last part of this tutorial!
Day 7 – Release.

Learning more: