Wednesday, February 8, 2017

Swift Performance Tips for Busy iOS Developers

street outlaw

"It’s time to upgrade". That’s a common theme you’ll hear if you watch Street Outlaws on the Discovery Channel. Their goal is simple - be the fastest car on the street or it’s time to upgrade. They are continuously looking for ways to boost the performance of their cars (nitrous, turbos, fiberglass panels, etc). Some performance mods are quick and others take time to implement and refine. In this post, let's focus on Swift performance improvements we can implement and release rather quickly. In fact, most of these improvements should only cost a story point or two:

Mark classes final

A final class will explicitly enable static dispatch for all methods which allows the compiler to optimize methods at compile time resulting in more efficient method invocation at runtime. There are two types of method dispatch: static and dynamic. Dynamic dispatch (the default type of method dispatch) is not as efficient because its dispatch isn’t known until runtime. Dynamic dispatch provides runtime conveniences like polymorphism, inheritance, and overloading but incurs a higher performance penalty. Therefore, by simply marking classes final we get a minor performance boost rather quickly. Refer to Apple’s Understanding Swift Performance talk for additional details.

Prefer isEmpty over count > 0

The native isEmpty method is more efficient because it only verifies one element exists where as the count property iterates the entire collection. Want to enforce this efficient practice within your codebase? SwiftLint has a static analysis check for this rule.

Convert strings to enums

In Apple’s Understanding Swift Performance talk they mentioned three dimensions of performance (see Listing 1).

Listing 1. Dimensions of Performance
FasterDimensionSlower
StackAllocationHeap
LessReference CountingMore
StaticMethod DispatchDynamic

When creating Swift objects it’s important to understand how much an object or property weighs in regards to these three dimensions. For example, a String is not an efficient type because it is allocated on the heap and it incurs a reference count. Comparatively, Enums are much more efficient because they are allocated on the stack, do not incur a reference count, and as a bonus they are type safe resulting in a win-win-win. Interested in automatically converting your localizable strings, asset names, or storyboard names to enums? SwiftGen has accomplished this task nicely on my projects.

Import existing images from the file navigator into an asset catalog

An asset catalog is preferred because it caches images, optimizes memory consumption, and enables lightweight App Store downloads via App Thinning - unnecessary image densities are not downloaded. Xcode has a wizard for automatically importing images into an asset catalog rather quickly. Refer to Apple’s Improving Existing Apps with Modern Best Practices talk for additional details.

Parse JSON on a background thread

This allows the main thread to remain idle to serve other threads while the JSON is transformed to an object. Refer to Apple’s Concurrent Programming with GCD in Swift 3 talk for more details.

let json = [String : Any]()   
  let dataTrasformQueue = DispatchQueue(label: “my.data.trasform.queue”)   
  dataTrasformQueue.async {   
   let result = T(json: json) // Transform JSON to object on background thread   
   DispatchQueue.main.async {   
    completionHandler(result) // Return control to main thread after object is parsed   
   }   
  }  

Prefer structs over classes (until you reach their point of diminishing returns)

Structs have many advantages over classes: they are allocated on the stack, they prevent unintended sharing, their memberwise initializer is a nice convenience, and a struct by itself doesn't incur reference counting. However, a structs properties will incur reference counting when they are reference types like String. Therefore, structs have a point of diminishing returns in regards to reference counting that multiplies proportionally by their number of properties which are reference types. A struct containing more than 2 reference types begins to incur a higher reference count than a comparative class. Refer to Apple’s Understanding Swift Performance talk for additional details.

Rewrite Objective-C objects in Swift

Refactoring Objective-C objects to Swift was the primary theme in Apple’s Optimizing App Startup Time talk. This tip barely makes the list for “busy developers” because the scope of this task may be large depending on your codebase but it’ll become more manageable by focusing on converting one Objective-C class per week or iteration.

Summary

The fastest car doesn't always win. The quickest car from start to finish does. A driver and the adjustments they make to their car has a huge impact on their performance. The tips above are quick adjustments we can make to our Swift code so our apps will be more efficient from point A to point B.

Monday, May 9, 2016

App Linking

Want to learn how to increase app engagement with universal links? In this tutorial we will review the end-to-end steps to get your Web server and app setup for app linking. From a user perspective app linking is as simple as tapping a link (see figure 1). Universal links will be routed to the app when the link has been enabled for app linking and the app is installed. However, if the app is not installed, the link will naturally open in the default browser.

Figure 1. App linking workflow

Get your server ready

The first task for enabling app linking is to configure the links or paths that should be associated to your app. This configuration is setup with the apple-app-site-association file that is deployed to the root of your HTTPS Web server.

Create the Apple association file

Create the apple-app-site-association JSON file with Apple's recommended format. This file identifies the paths or links that are linkable for your app. You may setup your path mappings to be specific or wildcard matchers. For example, the example below (see Listing 1) will route all links to your app via a wildcard path matcher. The appID key is the team ID or app ID prefix, followed by the bundle ID.

Listing 1. Creating an apple-app-site-association file
{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "{team-id-OR-app-prefix}.{app-bundle-identifier}",
                "paths": [ "*" ]
            }
        ]
    }
}

Upload the apple-app-site-association to the root of your Apache server or in the .well-known subdirectory. The default Apache server root exists at /Library/WebServer/Documents.

Configure the association file

The MIME type for the apple-app-site-association file must be application/json. This can be configured in Apache's httpd.conf file (see Listing 2).

Listing 2. Configure MIME type for apple-app-site-association file
<Directory "/Library/WebServer/Documents">
....

#
# Setup MIME type for Apple association file
#
<files apple-app-site-association>
Header set Content-type "application/json"
</files>
</directory>

Test the association file

Start your local Apache Web server via the Terminal:

$ sudo apachectl restart

Verify it is running at http://localhost. Next, setup your local Web server for SSL. Apple requires the association file to be deployed to an HTTPS Web server. The simplest way to deploy your localhost as a publicly accessible Web server via HTTPS is with ngrok. After downloading ngrok run the following command via Terminal (see Listing 3).

Listing 3. Starting ngrok
$ ./ngrok http 80

Tunnel Status                 online                                            
Version                       2.0.25/2.0.25                                     
Region                        United States (us)                                
Web Interface                 http://127.0.0.1:4040                             
Forwarding                    http://b5bdc858.ngrok.io -> localhost:80          
Forwarding                    https://b5bdc858.ngrok.io -> localhost:80  

The prior command to start ngrok (see Listing 3) assumes ngrok is installed at the root directory and localhost is running at port 80. Copy the HTTPS generated URL from ngrok (See Listing 3, line 8) and test it from a browser. Congrats, your localhost is now publicly accessible over HTTPS! This is also the HTTPS URL our iOS app will be associated with when testing locally.

Next, verify the MIME type for the association file is correctly configured with a content-type of application/json in Terminal (see Listing 4).

Listing 4. Verify MIME type
$ curl -I "https://b5bdc858.ngrok.io/apple-app-site-association"

HTTP/1.1 200 OK
Date: Sat, 07 May 2016 12:50:44 GMT
Server: Apache/2.4.18 (Unix) PHP/5.5.31
Last-Modified: Mon, 04 Apr 2016 12:06:48 GMT
ETag: "c9-52fa79025e600"
Accept-Ranges: bytes
Content-Length: 201
Content-type: application/json

Our last test is to verify our domain with Branch's Universal Links Validator. Copy your HTTPS URL and run it against their validator (see Figure 2).

Figure 2. Universal link validator

Get your app ready

The final task is to enable our app for universal links and setup the code to intercept and manage link validation and routing.

Enable Associated Domains in Xcode

In Xcode, navigate to your target's Capabilities tab and toggle Associated Domains on. Then, add each domain which hosts the corresponding association file and prefix it with applinks: (see Figure 3).

Figure 3. Enable associated domains

Update App Delegate

Add a new AppDelegate+AppLinking.swift file with the following code to handle universal link validation and routing. This code simply prints the URL of the universal link and by default forwards to the initial view controller. Refer to Apple's WWDC video on App Linking for validation and routing best practices.

import UIKit

extension AppDelegate {
    
    func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
        
        if let url = userActivity.webpageURL where userActivity.activityType == NSUserActivityTypeBrowsingWeb {
            print("url=\(url)")
        }
        
        return true
    }
    
}

Testing App Linking

After installing the app on a device, paste the HTTPS URL for your website as a link in any iOS app (Mail, Messages, Contacts, etc.) and click the URL (see Figure 1). Did the link take you to your app? If yes, congratulations! You have successfully setup app linking. If you encounter any issues refer to my tips below. After a successful test remove the installed app and re-test the link. Without the app installed the link behaves like an ordinary link and forwards to the website (see Figure 1).


Tips

  • The apple-app-site-association file must not have a .json extension.

  • App link testing will only work from an actual device. Testing from a simulator will not work.

  • The instructions provided above are for iOS 9 and above. If you need to support iOS 8 you are required to sign the apple-app-site-association file. For additional information on iOS 8 support refer to Apple's note on iOS 8 support. Fortunately, certificate signing is not required on iOS 9 and above when the association file is deployed to an HTTPS server.

  • App linking only works when clicking a link from within an Apple app (Mail, Messages, Contacts, etc). App linking will fail from third party apps like Gmail, Facebook, etc.

  • For additional information on App Linking refer to Apple's documentation or their WWDC talk on App Linking.

Sunday, April 17, 2016

Freaky Fast Delivery - Responsive Performance Strategies

Want to learn how to build freaky fast sites that load within the mythical one-second barrier? In this presentation, we’ll explore the latest techniques we can apply to create responsible sites the achieve this goal. Topic discussed will include:

  • Traditional vs enhanced page loading techniques
  • Loading JavaScript, CSS, fonts and content asynchronously
  • Compression techniques for requests, images, and assets
  • Caching techniques via Browser cache and Web storage
  • Performance budgets, anti-patterns, and monitoring tools

Source Code and Demos

Click the "Demo" icon on each slide to run the performance technique via my GitHub Pages account or browse the source code - Enjoy!


Slide Deck Sneak Peek

Poor Performance Causes Stress
Poor Performance Causes Stress
Responsible Performance
Responsible Performance

Responsive Images
Responsive Images
HTTP2
HTTP/2

Tuesday, December 8, 2015

Apple TV Developer Kit

Interested in developing for the Apple TV? Surprisingly, JavaScript (TVJS) and Markup (TVML) are the primary technologies for building Apple TV apps. In this presentation, we’ll explore the new Apple TV developer framework from setup to deployment and everything in between. Topics discussed will include:

  • Project setup
  • Application structure
  • Running from the simulator or Apple TV
  • What apps are great candidates for the Apple TV

Source Code and Demos

My Apple TV demos are available on Github. Enjoy!


Slide Deck Sneak Peek

TVMLKit App Architecture
TVMLKit Technology Stack

Swift
TVJS

TVML
Catalog TVML Template