Category Archives: Programming

Visual iOS guide on switching from Chinese to English

I wanted to document for my own future reference how to switch an iPhone (in the iOS Simulator) back to English from Chinese. My Chinese literacy is not very high, so I’m sure this guide will be handy.

Note: follow the red ovals and you will be able to change your iOS language back to English

1

2

3

4

5

6

7

8

Why would I need to do this? I’m working on language localization for different apps, and testing localization in the simulator is often easier than on actual hardware.

iOS Universal iPad Portrait Gotcha

I wanted to share a small tip that reinforces the necessity of on-device (non-simulator) testing.

While finalizing my latest iOS app (universal for both iPhone & iPad), I found an issue through manual QA on an actual iPad. I had only left Portrait checked in the Project > General section of Xcode, but my app was somehow running in landscape mode on the iPad.

general

Confused as to why it was rendering in both landscape & portrait mode on my iPad, I found a handy stack overflow post.

For one reason or another, you have to update your Info.plist to only specify portrait settings for iPads. Below is my Info.plist after I updated it to only target Portrait mode.

info_plist

It’s confusing as to why the Project General section’s Device Orientation is not sufficient to force only Portrait orientations and you have to also update the Info.plist.

Through simulator testing, it’s not likely that I would have caught onto this portrait vs landscape issue. I relied mostly on my primary iPhone and copious amounts of simulator testing for the other iOS universal devices.

For the highest level of quality control, you would need an iPhone 4s, iPhone 5, iPhone 6, iPhone 6+, iPad, and iPad Pro. That’s a lot of devices and I certainly don’t have all of those. Sidenote: if you do have all those devices, you would also be positioned to record App Preview videos for all devices natively (AKA lots of work).

Freeing up disk space as an iOS Developer

As an iOS Developer, I often have trouble updating to the latest version of Xcode since my 128GB MacBook Air keeps filling up.

Common culprits include ~/Library/Developer/Xcode/DerivedData and ~/Library/Developer/Xcode/iOS DeviceSupport.

By clearing out those two folders, I’m able to free up 11GB at this time of writing. For more information on what these folders contain, this [LINK REMOVED – see below] was helpful.

May 2021 Edit: This post previously linked to http://ajithrnayak.com/post/95441624221/xcode-users-can-free-up-space-on-your-mac. The link has been removed since the link is now broken.

Dynamic Infinite UIPickerView Scrolling Example

In iOS, I’ve seen a lot of examples of creating a UIPickerView with a hardcoded array of strings. This may be necessary if your data set is a list of strings (such as USA states). For numbers, there’s a much easier way to dynamically generate each row’s title.

To start off with, here’s a simple, programmatic UIPickerView implementation that includes the numbers (as strings) 0 through 9:

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupPicker()
    }

    var pickerView: UIPickerView = UIPickerView()
    var pickerDataSource = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
    
    func setupPicker() {
        pickerView.dataSource = self
        pickerView.delegate = self
        
        pickerView.frame = CGRectMake(0, 0, view.frame.width, 300)
        view.addSubview(pickerView)
    }
    
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return pickerDataSource[row]
    }
    
    // columns count
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    
    // rows count
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return pickerDataSource.count
    }
}

The key piece is the pickerDataSource that gives us our data source. It is an array literal, and in a worst case programming scenario, one can imagine a very, very long hardcoded array of looping “0” – “9”. A long hardcoded array works, but is not optimal for many reasons.

The code above looks like this in the simulator:

manual_ss

We can improve and change this in many ways. We are going to update the code to operate without using a hardcoded array literal. Also, the user has to scroll up (or down) for a very, very long time before they hit the end of the picker. When their scrolling stops, we will move the position of the selected row back to the middle, so that when they scroll again, there is a long way to go before they get to the top (or bottom).

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    
    var pickerView: UIPickerView = UIPickerView()
    let pickerDataSize = 100_000
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupPicker()
    }
    
    func setupPicker() {
        pickerView.dataSource = self
        pickerView.delegate = self
        
        pickerView.frame = CGRectMake(0, 0, view.frame.width, 300)
        view.addSubview(pickerView)
        
        // set the picker to the middle of the long list
        pickerView.selectRow(pickerDataSize/2, inComponent: 0, animated: false)
    }
    
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return String(row % 10)
    }
    
    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        // do something with the resulting selected row
        
        // reset the picker to the middle of the long list
        let position = pickerDataSize/2 + row
        pickerView.selectRow(position, inComponent: 0, animated: false)
    }
    
    // columns count
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 1
    }
    
    // rows count
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return pickerDataSize
    }
}

In this 2nd code snippet for our ViewController, we have created a large number, pickerDataSize, that tells our device how many rows there are. The neat thing is that we don’t need to create an array of 100K items. When the UIPickerViewDelegate’s titleForRow method is called, it uses the row to dynamically return a string for that given row. Progress!

As a bonus, I went ahead and added a call to pickerView.selectRow in the didSelectRow function. When the user finishes selecting a row, this call will silently (animated: false) move the selected row back to the middle of the rows. This will maintain the illusion of infinite scrolling. Success!

Here we can see the updated, dynamic picker view:

dynamic_ss

Anger Driven Development

As a programmer, it’s nice to imagine perfect productivity conditions: many well defined, small units of work lined up. If the work at hand is clear and you have ample time to get into a flow, you expect to finish many things. Unfortunately, real life rarely works out that way.

Instead of picturing an ideal, efficient scenario, I want to talk about a less than ideal productivity method that manages to pop up every now and then: Anger Driven Development (ADD). ADD in this context might mean different things to different people.

What ADD means to me is that I’m working on a task, get frustrated, and then refuse to be beaten. Some of my more productive times have occurred when I was so pissed at the situation that I refused to throw in the towel. I will NOT be beaten by a machine. The power of human will can be indomitable at times.

While driven to extreme frustration at what started off as an “easy task”, it becomes man versus machine. Man does not want to give up. Man refuses to give up.

Even if I was tired or had other reasons to put the work off until later, Anger Driven Development does not give me the choice to stop. Like taking a hammer to a screw and forcing it in through sheer will power and brute force, Anger Driven Development can be messy. Anger Driven Development can’t stop, won’t stop.

The most amazing part of ADD (besides the task that is now completed) is the sense of triumph at the end. While it always feels good to be productive and get things done, there are few things like the joy of victory after ADD.

Anger Driven Development would never be my modus operandi, but it inadvertently has its place in my toolbox.

WKInterfacePicker Attributes Illustrated

The WatchKit Framework’s WKInterfacePicker has some level of customization in Interface Builder.

As of this time of writing, there are 4 main attributes to configure:

  • Style
  • Focus
  • Indicator
  • Enabled

Style is the most important attribute since it heavily influences the type of Picker. The options for Style are List, Stack, and Sequence. List is the standard iOS-style 3d list of text. Stack allows you to flip through images as if it were a deck of cards. Sequence lets you move between images without any intermediate transition effect. For a good look, I’d recommend Big Nerd Ranch’s blog post.

Focus presents an outline around the currently focused/selected Picker. This is helpful if you have multiple WatchKit pickers or multiple elements for selection (think “Customize” for your watch face). The options are None, Outline, and Outline with Caption. The last option, “Outline with Caption”, comes into effect if your Style is Stack (or possibly Sequence), see an example here.

 

Plain, without focus

Plain, without focus

Left picker is focused

Left picker is focused

Indicator has two options: Disabled or Shown While Focused. The documentation wasn’t very clear: “A value indicating whether the picker uses an indicator to convey context about the number of picker items and which item is selected.” As far as I can tell, Disabled means the standard look, and the “Shown While Focused” adds a scroll bar helper on the right side of the watch.

Indicator - Shown While Focused

Indicator – Shown While Focused

Enabled is a helpful option that is either on or off. Enabling it or not doesn’t affect the view. When the picker is enabled, it allows the user to use the picker. This attribute can be set or unset programmatically with -setEnabled.

Hopefully this quick explanation of the different WKInterfacePicker attributes helps you out. The digital crown (AKA dial) & the WKInterfacePicker provides an extremely powerful, convenient input method for your watch app users.

Logging during development with Apple Watch hardware

Inserting breakpoints or logging statements (such as NSLog) is relatively straightforward with an iPhone-only iOS app. But how do you log from the WatchKit Extension (aka Watch app)?

The good news is that logging isn’t filled with many complicated steps. The bad news is that the logging works intermittently.

  1. Add your NSLog statements in your WatchKit Extension. This is probably your InterfaceController.m
  2. Run the watch app on iOS Device + watchOS Device (in the WatchKit App scheme)
  3. Select Debug > Attach to Process > (click on your watch app name)
  4. Profit! You should see your NSLogs when they are triggered in the app lifecycle in Xcode

debug

If logging isn’t working, try typical Xcode debug steps such as:

  • deleting the app on your phone & re-running it in Xcode
  • restarting the phone and/or watch devices
  • clean Xcode (cmd + shift + k) and re-run the app
  • quit & restart Xcode

Apple GameCenter Implementation Strategy

Having worked on my first app that implements GameCenter, I had a lot of questions about the process. The main question was how do I implement GameCenter into my app? Do I need to pick the right application template when creating my Xcode project? Do I need to use SpriteKit? Etc

The good news is that GameCenter can be added on after the rest of your application is ready. You should focus on your game mechanics, game engine, user friendliness, etc. After you are satisfied with the core experience, you can add GameCenter (leaderboard, achievements, and other features) afterwards. You can choose any application template and you don’t have to use SpriteKit.

In my app, I had both a phone app & a watch app. You will need to implement GameCenter using Apple’s GameKit Framework. GameKit is currently only for the iPhone, so GameCenter logic needs to be called in the Phone app.

I found great Apple documentation with the same advice:

Here’s a reasonable process to follow when designing a game that supports Game Center:

  1. Decide which Game Center features you plan to support.
  2. Define your game mechanics, keeping in mind the requirements and limitations of Game Center.
  3. Implement your game engine, ignoring Game Center for now. Focus on developing the other aspects of your game, such as your game engine and gameplay. You do this to avoid needing to create an iTunes Connect record earlier than you need to.
  4. [implement Game Center]

As with any app, focus on nailing the key user experience. Then worry about implementing/integrating GameCenter in your app.

Setup Apple Watch for Development Guide

While working in Xcode and running my watch app on my actual hardware watch for the first time, I ran into this error on my watch the first time: “Failed to install [app], error: Application Verification Failed.” This stack overflow answer provides the solution, but I wanted help illustrate the steps I took to fix the error. Disclaimer: this worked for me, but there are probably more optimal ways of fixing the error.

  1. In Xcode, get the UDID of your Apple Watch (WIndow > Devices). The UDID is labeled “Identifier” and you can double click on the Identifier device hash to select & copy it.
    1
  2. Visit the Apple Developer Portal at https://developer.apple.com/devcenter/ios/index.action and click on “Certificates, Identifiers & Profiles” in the right sidebar.
    2
  3. Click on Devices > All in the left sidebar.
    3
  4. Click on the Plus Sign (+) in the top right.
    4
  5. In Register Device, provide a Name (whatever you want) and your watch UDID (from step 1 above).
    5
  6. Submit the form to register your watch device.
  7. In “Certificates, Identifiers & Profiles”, locate your .watchkitextension Provisioning Profile for your app. Select & download this profile.
    7
  8. Locate your downloaded profile file on your computer & double click the Provisioning Profile.
  9. Restart Xcode.
  10. Build your project and you will encounter a iOS Development Certificate alert.
    10
  11. Warning, this step may be dangerous (proceed at your own risk). This worked for me. Click on “Revoke and Request”. This will revoke your current certificate and request a new one. You will probably get an email notifying you that “Your Certificate Has Been Revoked”.
  12. Run your Xcode project. Your watch app should now load the development build on your actual watch hardware.

About the author: Rex Feng enjoys iOS development and has released Pomodoro Pro for the iPhone & Apple Watch. You can follow him @rexfeng.

Pomodoro Pro WatchKit Architecture

My iOS app, Pomodoro Pro, is a constant work in progress. This post discusses how Apple Watch support was added to v1.1.0. Pomodoro Pro is a free, easy to use app for getting work done in continuous work & break cycles.

If you’ve looked into Apple Watch development, you’ve learned that the watch portion is an extension of your phone app. Your phone app is the brains of the operation, and your watch app relies on the phone app to do anything.

2 Way Event Binding Demo

From watch to phone

watch-final

From phone to watch

phone_8

App Lifecycle

With mobile development, it’s necessary to understand when a user will come into contact with your application. If the user comes across your application and starts using it when the visuals are incorrect, the user will lose confidence and not trust your application.

Working with Xcode and the watch simulator, my app has the following key lifecycle events:

  • watch starts up
  • watch resumes
  • watch actions (button presses) are reflected on both the watch & phone
  • phone actions (button presses) are reflected on both the phone & watch

Each of these events are essential for maintaining a consistent user experience that does not surprise the user.

Implementation

My implementation used MMWormhole for 2 way event binding. This means that the phone should know when a button was pressed on the watch and vice versa. Curtis Herbert has a great blog post on sharing data and events.

In order to have the phone app be responsible for the correct timer state, my phone app is responsible for returning a NSDictionary of the current timer properties. My watch app is able to get the NSDictionary and update the watch screen accordingly. It is important to note that my phone app is the ONLY source of the canonical current timer state. Trying to sync state between the phone and watch app would be a nightmare and a lot of work.

When the watch app starts, the watch app asks the phone for the current timer state (a NSDictionary) in order to set itself up correctly. When the watch app resumes from inactivity, it also asks the phone for the current timer state (a NSDictionary).

Using MMWormhole, I’m able to listen on events from the watch or the phone. When the user presses a button on the watch, the watch app passes a message to notify the phone, and the phone updates the timer state. Similarly, when the user presses a button on the phone, the phone app updates the timer state and notifies the watch app with the latest state.

Lessons Learned

  1. The phone app (not the watch app) is responsible for the correct state
  2. Use a Framework or shared classes (in File Inspector, add Target Membership) to DRY (don’t repeat yourself) out your codebase

Summary

With Pomodoro Pro, it was essential for a user to be able to start / pause / resume / stop the timer from either the phone app or the watch app. This required a way to manage the timer state (phone app) and have user actions occur on all screens (phone & watch).

In order to build a user friendly watch app, you should anticipate & identify where your app’s state and events come from. Make sure to account for those scenarios.

Pomodoro Pro v1.1.0 went live on 4/14/2015. Please let me know what you think @rexfeng