Testing Apps: The Simulator and Devices
A physical iPhone, iPad, or iPod touch is a key component of the SDK. Testing on a device is vital. As simple and convenient as the iOS Simulator is, it is not the same as a real device. You want your apps to run on some or all of the iOS device family, so it’s important that they run best in the native environment. An iOS device itself offers the fully caffeinated, un-watered-down testing platform you need.
Apple regularly suggests that a development unit needs to be devoted exclusively to development. Reality has proven rather hit and miss on that point. Other than early betas, releases of iOS have proven stable enough that you can use your devices for both development and day-to-day tasks, including making calls on iPhones. It’s still best to have extra units on hand devoted solely to development, but if you’re short on available units, you can probably use your main iPhone for development; just be aware of the risks, however small. Note that as a developer program member, you have agreed to a non-disclosure agreement (NDA) with Apple. Beware of accidentally showing Apple confidential prereleases to others.
Devices must be proactively set up for development use with Xcode’s Organizer. The Organizer also lets you register your device with Apple, without having to enter its information by hand at the provisioning portal. Chapter 15, “Deploying Applications,” gives detailed information on how to do this.
When developing, it’s important to test on as many iOS platforms as possible. Be aware that real platform differences exist between each model of iPhone, iPad, and iPod touch. For example, two models of the fifth-generation iPod touch offer front- and back-facing cameras; one only offers a front-facing camera. The second-generation iPad and earlier as well as the original iPad-mini do not have retina screens. iPhones all have cameras, which none of the iPod touches offered until the fourth generation. Certain models of the iPad and the iPhone offer GPS technology; other models do not. A discussion of major platform device features along with some device differences follows later in this chapter.
Each release of the Macintosh-based iOS Simulator continues to improve on previous technology. That said, there are real limitations you must take into account. From software compatibility to hardware, the simulator approximates but does not equal actual device performance.
The simulator uses many Macintosh frameworks and libraries, offering features that are not actually present on the iPhone or other iOS devices. Applications that appear to be completely operational and fully debugged on the simulator might flake out or crash on a device itself due to memory or performance limitations on iOS hardware. Even the smallest Mac nowadays comes with 4GB of RAM, whereas the third-generation iPad has only 1GB of RAM. Instruction set differences might cause apps to crash on older devices when they are built to support only newer versions of the ARM architecture. You simply cannot fully debug any program solely by using the simulator and be assured that the software will run bug-free on iOS devices.
The simulator is also missing many hardware features. You cannot use the simulator to test the onboard camera or to get accelerometer and gyro feedback. Although the simulator can read acceleration data from your Macintosh using its sudden motion sensor (if there’s one onboard, which is usually the case for laptops), the readings will differ from iOS device readings and are not practical for development or testing. The simulator does not vibrate or offer multitouch input (at least not beyond a standard “pinch” gesture).
From a software point of view, the basic keychain security system is not available on the simulator. You cannot register an application to receive push notification either. These missing elements mean that certain kinds of programs can be properly tested only when deployed to an iPhone or other iOS device.
Another difference between the simulator and the device is the audio system. The audio session structure is not implemented on the simulator, hiding the complexity of making things work properly on the device. Even in areas where the simulator does emulate the iOS APIs, you might find behavioral differences because the simulator is based on the Mac OS X Cocoa frameworks. Sometimes you have the opposite problem: Some calls do not appear to work on the simulator but work correctly on the device. For example, if you store or access files, the simulator is usually case-insensitive (depending on how the Mac is set up), but iOS is case-sensitive.
That’s not to say that the simulator is unimportant in testing and development. Trying out a program on the simulator is quick and easy, typically much faster than transferring a compiled application to an iOS unit. The simulator lets you rotate your virtual device to test reorientation, produce simulated memory warnings, and try out your UI as if your user were receiving a phone call. It’s much easier to test out text processing on the simulator because you can use your desktop keyboard rather than hook up an external Bluetooth keyboard to your system and you can copy and paste text from local files; this simplifies repeated text entry tasks such as entering account names and passwords for applications that connect to the Internet.
Another area the simulator shines is localization. As you see in Chapter 5, “Localization,” switching languages for your app is as easy as launching the simulator with the right special flag.
In the end, the simulator offers compromise: You gain a lot of testing convenience but not so much that you can bypass actual device testing.
Apple is moving away from tethered requirements in iOS but has not yet introduced a way to develop untethered at the time this book is being written. At this time, all interactive testing is done using a USB cable. Apple provides no way to wirelessly transfer, debug, or monitor applications as you develop. This means you perform nearly all your work tethered over a standard iPhone USB cable.
When you are debugging a tethered unit, try to set things up to reduce accidentally disconnecting the cable. If that happens, you lose the debug session including any interactive debugging, the console, and screenshot features.
You want to invest in a good third-party dock for iPhones or iPod touches and possibly one for iPads. Look for stands that allow the cable to be connected and hold the unit at a comfortable angle for touching the screen. Even better are docks that work in both portrait and landscape. The iPad will work in the Apple doc, though only in portrait. Alternatively, the Apple folding cases that also act as stands work in both orientations.
When tethered, always try to connect your unit to a port directly on your Mac. If you must use a hub, connect to a powered system that supports USB 2.0 or higher. Most modern screens, including Apple’s large display, come with built-in powered USB ports, but it pays to double check.
When it comes to the iPad, if the USB connection does not have sufficient power to charge the device, untether your device between testing periods and plug it directly into the wall using its 10W power adapter. Some USB ports provide sufficient power to charge the iPad while you’re using it, but this is not a universal situation.
iOS Device Considerations
Designing apps for mobile platforms such as the iPhone or iPad is not the same as designing for the desktop (or laptop). There are several extra considerations such as storage, interaction methods, and battery life. Storage limits, smaller screens, different interaction techniques, and energy consumption are important design considerations when creating your app.
With the iPhone, you are designing for a small touch-based screen with a good, but limited battery life. It is not a desktop with a large screen, a mouse or trackpad, and a physical always-on A/C power supply. Platform realities must shape and guide your development. Fortunately, Apple has done an incredible job designing a platform that leverages flexibility from its set of storage, interaction controls, and constrained battery life.
The iPhone hosts a powerful yet compact OS X–based installation. Although the entire iOS fills no more than a few hundred megabytes of space—almost nothing in today’s culture of large operating system installations—it provides an extensive framework library. These frameworks of precompiled routines enable iPhone users to run a diverse range of compact applications, from telephony to audio playback, from e-mail to web browsing. The iPhone provides just enough programming support to create flexible interfaces while keeping system files trimmed down to fit neatly within tight storage limits.
Most modern devices come with at least 16GB of onboard Flash-based storage, and some have considerably more. Some older devices running iOS 7 and later have as little as 4GB. Although application size is limited (see the “Note: App Size”), the space for data is much larger. Having said that, be aware that users can check how much space an app is using and might delete hungrier apps.
Data Access Considerations
Every iOS application is sandboxed. That is, it lives in a strictly regulated portion of the file system. Your program cannot directly access other applications, certain data, and certain folders. Among other things, these limitations require accessing built-in application data using system APIs including the iTunes library, calendar, photos, location services, notifications, reminders, and built-in social services such as Facebook and Twitter.
Your program can, however, access any data that is freely available over the air when the iOS device is connected to a network—including any iCloud documents it owns. Your app can also access data stored in the shared system pasteboard and data shared using a document interaction controller, which offers a limited way to share document data between applications. Apps that create or download data can send those files to applications that can then view and edit that data. In that situation, the data is fully copied from the originating application into the sandbox of the destination application.
On iOS, memory management is critical. Apple has not enabled disk swap–based virtual memory for iOS. When you run out of memory, iOS shuts down your application; random crashes are probably not the user experience you were hoping for. With no swap file, you must carefully manage your memory demands and be prepared for iOS to terminate your application if it starts swallowing too much memory at once. You must also take care concerning what resources your applications use. Too many high-resolution images or audio files can bring your application into the auto-terminate zone.
Many parts of the iOS framework cache your image data in order to speed up rendering and application performance. This caching can come at the cost of a larger memory footprint and, on retina devices, if used improperly, can generate more memory pressure on your app. Chapter 14 covers using the Instruments tool to figure out what parts of your application consume too much memory and techniques to address and resolve those issues. It also covers the debug memory gauge, a handy way to see if and when your app is approaching the memory danger zone.
For the iPhone and iPod touch, losing physical input devices such as mice and working with a small screen doesn’t mean you lose interaction flexibility. With multitouch and the onboard accelerometer, you can build UIs that defy expectations and introduce innovative interaction styles. The iPhone’s touch technology means you can design applications complete with text input and pointer control, using a virtual screen that’s much larger than the actual physical reality held in your palm.
In addition to the touchscreen, users can interact with your app using a smart autocorrecting onscreen keyboard, built-in microphone (for all units except on the obsolete first-generation iPod touch), and an accelerometer that detects current orientation as well as changes. When designing text input, look for ways you can make it easier for the user such as splitting up longer inputs into smaller fields or using auto completion. For longer text areas, make sure you use scrolling text views. Most importantly, try your interface without an external keyboard, as most users will not have one.
Focus your design efforts on easy-to-tap interfaces rather than on desktop-like mimicry. Remember to use just one conceptual window at a time—unlike in desktop applications, which are free to use a more flexible multiwindow display system.
For mobile platforms, wise use of the battery is part of any design. Apple’s SDK features help to design your applications to limit CPU use and avoid running down the battery. A smart use of technology (for example, properly suspending themselves between uses) lets your applications play nicely on the iPhone and keeps your software from burning holes in users’ pockets (sometimes almost literally, speaking historically). Some programs, when left running, produce such high levels of waste heat that the phone becomes hot to the touch, and the battery quickly runs down. The Camera application was one notable example.
Heavy users of the battery include the Camera app; communications, especially over phone networks; and high-precision location services that use the GPS hardware instead of Wi-Fi triangulation.
Each new generation of iOS device brings some improvement to battery life. Even so, you should continue to keep energy consumption in mind when developing your applications.
With iOS multitasking, applications can allow themselves to
- Be suspended completely between uses (the default behavior)
- Be suspended with occasional slices of background processing time
- Quit entirely between uses
- Run for a short period of time to finish ongoing tasks
- Create background tasks that continue to run as other applications take control
There is built-in support for background tasks including playing music and other audio, collecting location data, and using Voice over IP (VoIP) telephony. Rather than running a simple background daemon, these tasks are event-driven. Your application is periodically called by iOS with new events, allowing the application to respond to audio, location, and VoIP updates.
Since only the current app can update the UI, Apple supports pushing data from web services. Using Push Notifications sends processing off-device to dedicated web-based services, leveraging their always-on nature to limit on-device processing requirements. Registered services can push badge numbers and messages to users, letting them know that new data is waiting on those servers. Push notifications can allow the user to launch your app or bring it to the foreground, passing a small amount of optional data while doing so.
A special kind of notification gives your app some background execution time for updating changes. And even if you do not use notifications, you can ask the system for regular background processing callbacks. These two mechanisms keep your app up to date before the user brings it into the foreground.
In addition, applications can pass control from one to the other by passing data (using the document interaction controller) and by opening custom URL schemes.
Apple strongly encourages developers to limit the amount of cell-based data traffic used by each application. The tendency of carriers to meter data usage and the overall movement away from unlimited data plans help reinforce this requirement. Applications that are perceived to use too much cell bandwidth might be rejected or pulled from the store. If your application is heavily bandwidth-dependent, you may want to limit that use to Wi-Fi connections.
Almost all device families come with Wi-Fi, mostly 802.11n. For those with cellular connections, many are at least 4G (5.8Mbps HSUPA), and LTE is usually the minimum speed for new devices.
User Behavior Considerations
Although this is not a physical device-based consideration, iPhone users approach phone-based applications sporadically. They enter a program, use it for its intended purpose, and then leave just as quickly. The handheld nature of the device means you must design your applications around short interaction periods and prepare for your application to be interrupted as a user receives a phone call or sticks the phone back into a pocket, purse, or backpack. Keep your application state current between sessions and relaunch quickly to approximately the same task your user was performing the last time the program was run. This can demand diligence on the part of the programmer, but payoff in user satisfaction is worth the time invested. Apple does provide APIs for state restoration, though they are beyond the scope of this book. For more information, start with the chapter on state preservation and restoration in the iOS App Programming Guide available with the documentation that comes with Xcode.