Does it surprise you that I’m dedicating a tutorial to a simple addition as the guard statement? I hope you’ll better understand my excitement at the end of this tutorial. During this tutorial, I hope to convince you that guard is anything but a redundant addition to the Swift programming language.
Minimizing Complexity
Conditionals are a fundamental component of every programming language. Objective-C and Swift are no exception to this rule. If you plan to write an application of any complexity, conditionals will cross your path, there’s no escaping them.
Unfortunately, conditionals are often the very cause of complexity. Nested conditionals in particular can lead to difficult to find bugs, hard to understand code, and easily overlooked edge cases.
To keep nested if statements to a minimum, I frequently use the following pattern in Objective-C.
- (void)fetchListOfCustomers:(NSArray *)customers {
if (!self.reachable) return;
if (!self.connected) return;
if (!customers || ![customers count]) return;
...
}
The idea is to bail out as soon as possible. The if statements in the example represent a set of requirements that need to be met before the rest of the method’s body is executed.
The above example translates to the following slightly more complex equivalent.
- (void)fetchListOfCustomers:(NSArray *)customers {
if (self.reachable && self.connected) {
if (customers && [customers count]) {
...
}
}
}
Do you see the problem lurking in this example? We’re already nested two levels deep without having done anything interesting.
It’s easy to translate the above pattern to Swift. The syntax looks similar, but due to customers being an optional, we need to unwrap the customers argument before we can access its value.
func fetchListOfCustomers(customers: [Customer]?) {
if !reachable { return }
if !connected { return }
if let customers = customers where customers.count > 0 {
print(customers)
}
}
Exiting Early
Swift 2 introduces the guard statement. It was designed specifically for exiting a method or function early. The guard statement is ideal for getting rid of deeply nested conditionals whose sole purpose is validating a set of requirements. Take a look at the updated example in which I’ve replaced every if statement with the new guard statement.
There are several things worth noting. Let’s start with the syntax.
Syntax
The guard keyword emphasizes that we are validating a requirement. We are guarding against something. In the example, we explicitly check if reachable and connected are true. If they aren’t, then we exit the method early. The point is that the syntax is more explicit about the requirements than a regular if statement.
Exit
Note that a guard statement always has an else clause. The else clause is executed if the condition of the guard statement evaluates to false. Using guard makes much more sense when you’re validating requirements.
In the else clause, you must transfer control away from the scope in which the guard statement appears. We use a return statement in the above example, but you could, for example, use a continuestatement if you’re in a loop or throw an error. Take a look at the updated example below in which we throw an error in the else clause. Note the throws keyword in the method declaration to indicate that fetchListOfCustomers(_:) is a throwing method.
A guard statement is just as powerful as an if statement. You can use optional bindings and even the use of where clauses, introduced in Swift 1.2, is permitted. I’m sure you agree that the example is easy to understand, eliminating unnecessary nested conditionals.
Scope
An important difference with if statements is the scope of variables and constants that are assigned values using an optional binding. In the above example, the customers constant was assigned a value using an optional binding. The customers constant is accessible from the scope the guard statement appears in. This is an important detail and one of the key advantages of using guard.
Conclusion
If you thought that guard was a simple variation on Swift’s if statement, then I hope I’ve convinced you otherwise. While if statements will continue to be your tool of choice in most situations, guard has a number of advantages in certain situations. This is especially true if used in combination with error handling, which was also introduced in Swift 2.
In this short tutorial, I’d like to focus on Swift’s brand new syntax for availability checking. If you’ve done any amount of iOS or OS X development, then I’m sure you know how tedious it can be to check if a particular API is available on the device your application is running on. In Swift 2, this has become much less of a pain for developers.
The Problem
Picture the following scenario. You’re developing an iOS application that targets iOS 7 and up. During last year’s WWDC, Apple introduced a new API for notification registration.
registerUserNotificationSettings(_:)
Does that mean that you need to raise your application’s deployment target from iOS 7 to iOS 8? You could do that, but it would leave a significant portion of your application’s user base in the cold, only to comply with Apple’s new policy for local and remote notifications. Your users won’t thank you for that.
The alternative is to only use the new API on devices that run iOS 8 and up. That makes more sense. Right? The implementation would look something like this.
This is a viable option, but it isn’t witout risk. In this tutorial, I won’t go into the details of what those risks involve, but I do want to emphasize that most developers think it’s fine to use the above approach. The following example shows a variation of this approach, this time using Objective-C.
While both approaches will work in most situations, there are situations in which you’ll run into problems. Some APIs, for example, start their lives as private APIs and are made public at a later stage. In that scenario, you may end up hitting private APIs on devices running an operating system in which those APIs aren’t public yet. And I’m sure you know what that means.
The Solution
Thanks to the work of the Swift team, the solution to our problem is simple and straightforward in Swift 2. Take a look at the following example. Note that the deployment target of the project is set to iOS 7, using Swift 2 and Xcode 7.
In the example, we are using APIs that were introduced in iOS 8. Because the compiler knows that the deployment target of the project is set to iOS 7, it throws an error, telling us that the APIs we want to use are only available in iOS 8 and up. It knows this by inspecting the SDK for availability information. If you press Command and click the registerUserNotificationSettings(_:) method, you should see something like this.
Fortunately, Xcode gives us a solution to resolve the issue. It suggests to use a version check to avoid that the APIs exclusive to iOS 8 and up are called if our users run the application on an older version of iOS.
Note that this feature was introduced in Swift 2. The compiler won’t throw an error if you’re using Swift 1.2. The addition of the version check also makes the example easier to understand. Take a look at the updated example below in which we follow the advice Xcode has givesn us.
The syntax is clear and understandable. Using the availability syntax, we check if the application is running on a device with iOS 8 and up. If it isn’t, the if clause is skipped, otherwise the application calls the new API for notification registration.
Syntax
The syntax is straightforward. We start the availability condition with #available and wrap the condition in parentheses. We can add as many platforms as necessary, separating the list of platforms with commas.
Note that we end the list of platforms with an asterisk. This asterisk is required and indicates that the if clause is executed on the minimum deployment target for any platform that aren’t included in the list of platforms.
As we saw earlier, we can use the @available attribute to add availability information to functions, methods, and classes. In the following example, we tell the compiler that the useFancyNewAPI should only be called on devices running iOS 9 and up.
Keep in mind that the availability syntax is not an alternative for the two examples I showed you at the start of this tutorial. These examples are flawed and should only be used if you’re using Objective-C or an earlier version of Swift.
The availabilty syntax is yet another reason for migrating your Swift projects to Swift 2. It gets rid of error-prone solutions for checking API availability. The world looks a little bit friendlier with Swift 2. Doens’t it?
In this short tutorial, I’d like to focus on Swift’s brand new syntax for availability checking. If you’ve done any amount of iOS or OS X development, then I’m sure you know how tedious it can be to check if a particular API is available on the device your application is running on. In Swift 2, this has become much less of a pain for developers.
The Problem
Picture the following scenario. You’re developing an iOS application that targets iOS 7 and up. During last year’s WWDC, Apple introduced a new API for notification registration.
registerUserNotificationSettings(_:)
Does that mean that you need to raise your application’s deployment target from iOS 7 to iOS 8? You could do that, but it would leave a significant portion of your application’s user base in the cold, only to comply with Apple’s new policy for local and remote notifications. Your users won’t thank you for that.
The alternative is to only use the new API on devices that run iOS 8 and up. That makes more sense. Right? The implementation would look something like this.
This is a viable option, but it isn’t witout risk. In this tutorial, I won’t go into the details of what those risks involve, but I do want to emphasize that most developers think it’s fine to use the above approach. The following example shows a variation of this approach, this time using Objective-C.
While both approaches will work in most situations, there are situations in which you’ll run into problems. Some APIs, for example, start their lives as private APIs and are made public at a later stage. In that scenario, you may end up hitting private APIs on devices running an operating system in which those APIs aren’t public yet. And I’m sure you know what that means.
The Solution
Thanks to the work of the Swift team, the solution to our problem is simple and straightforward in Swift 2. Take a look at the following example. Note that the deployment target of the project is set to iOS 7, using Swift 2 and Xcode 7.
In the example, we are using APIs that were introduced in iOS 8. Because the compiler knows that the deployment target of the project is set to iOS 7, it throws an error, telling us that the APIs we want to use are only available in iOS 8 and up. It knows this by inspecting the SDK for availability information. If you press Command and click the registerUserNotificationSettings(_:) method, you should see something like this.
Fortunately, Xcode gives us a solution to resolve the issue. It suggests to use a version check to avoid that the APIs exclusive to iOS 8 and up are called if our users run the application on an older version of iOS.
Note that this feature was introduced in Swift 2. The compiler won’t throw an error if you’re using Swift 1.2. The addition of the version check also makes the example easier to understand. Take a look at the updated example below in which we follow the advice Xcode has givesn us.
The syntax is clear and understandable. Using the availability syntax, we check if the application is running on a device with iOS 8 and up. If it isn’t, the if clause is skipped, otherwise the application calls the new API for notification registration.
Syntax
The syntax is straightforward. We start the availability condition with #available and wrap the condition in parentheses. We can add as many platforms as necessary, separating the list of platforms with commas.
Note that we end the list of platforms with an asterisk. This asterisk is required and indicates that the if clause is executed on the minimum deployment target for any platform that aren’t included in the list of platforms.
As we saw earlier, we can use the @available attribute to add availability information to functions, methods, and classes. In the following example, we tell the compiler that the useFancyNewAPI should only be called on devices running iOS 9 and up.
Keep in mind that the availability syntax is not an alternative for the two examples I showed you at the start of this tutorial. These examples are flawed and should only be used if you’re using Objective-C or an earlier version of Swift.
The availabilty syntax is yet another reason for migrating your Swift projects to Swift 2. It gets rid of error-prone solutions for checking API availability. The world looks a little bit friendlier with Swift 2. Doens’t it?
Stetho is an open source debugging platform, developed by Facebook, that offers a rich and highly interactive debugging experience to Android developers. With Stetho, debugging native Android apps becomes as simple as debugging a web page, because it allows you to use Google Chrome’s developer tools to perform various debugging activities, such as view hierarchy inspection, network inspection, SQLite database management, and more.
In this tutorial, you are going to learn how to add Stetho to an Android project and use both Google Chrome’s developer tools and Stetho’s command line utility, dumpapp, to debug it.
1. Adding Gradle Dependencies
To add the Stetho library to your project, add com.facebook.stetho:stetho as a compile dependency in the app module’s build.gradle file:
compile 'com.facebook.stetho:stetho:1.1.1'
In this tutorial, you will be using OkHttp, a popular networking library from Square, to manage all network connections, because it plays really well with Stetho. Add it as another compile dependency:
compile 'com.facebook.stetho:stetho-okhttp:1.1.1'
2. Initializing Stetho
Step 1: Creating a Custom Application Class
The best time to initialize Stetho is when your application is starting. Therefore, you have to create a new class that extends Application and initialize Stetho inside its onCreate method.
Create a new class called MyApplication and override its onCreate method:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
}
}
To initialize Stetho, you must first create an instance of Stetho.InitializerBuilder, using the Stetho.newInitializerBuilder method. Next, to allow Stetho to work with Chrome’s developer tools, you must call enableWebKitInspector. If you also want to enable dumpapp, you must call enableDumpapp. Once Stetho.InitializerBuilder is ready, you can call its build method to generate an Initializer object and pass it to the Stetho.initialize method.
For now, let’s enable the default functionality by using the default InspectorModulesProvider and DumperPluginsProvider. Add the following code to the onCreate method:
// Create an InitializerBuilder
Stetho.InitializerBuilder initializerBuilder =
Stetho.newInitializerBuilder(this);
// Enable Chrome DevTools
initializerBuilder.enableWebKitInspector(
Stetho.defaultInspectorModulesProvider(this)
);
// Enable command line interface
initializerBuilder.enableDumpapp(
Stetho.defaultDumperPluginsProvider(context)
);
// Use the InitializerBuilder to generate an Initializer
Stetho.Initializer initializer = initializerBuilder.build();
// Initialize Stetho with the Initializer
Stetho.initialize(initializer);
Step 2: Editing the Manifest
To let the Android operating system know that you have a custom Application class, add an attribute called android:name to the manifest’s application tag and set the value to the name of your custom Application class.
After compiling and installing your app on an Android device (or the emulator), start Google Chrome and type in chrome://inspect in the address bar. You will see a screen that looks like this:
Click the inspect link to open the Developer Tools.
Step 1: Inspecting Network Connections
Stetho allows you to inspect, in real time, the network connections that your app makes. However, in Stetho version 1.1.1, this only works with the OkHttp network library. When using OkHttp with Stetho, you should remember to add a StethoInterceptor to the OkHttpClient object’s List of network interceptors.
Here’s some sample code that connects to HttpBin and retrieves a JSON document:
// Create an instance of OkHttpClient
OkHttpClient httpClient = new OkHttpClient();
// Add Stetho interceptor
httpClient.networkInterceptors().add(new StethoInterceptor());
try {
// Fetch the contents of http://httpbin.org/ip
Response response = httpClient.newCall(
new Request.Builder().url("http://httpbin.org/ip").build()
).execute();
} catch(IOException ioe) {
Log.d("StethoTut", ioe.getMessage());
}
When the code runs, you will see the following in the Network tab of the Developer Tools window:
If you click the URL in the first column, you will be taken to a screen that displays more information about the response:
Step 2: Querying SQLite Databases
With Stetho, you can perform a lot of operations on your app’s SQLite databases. Click the Resources tab and select Web SQL. If your app has any SQLite databases, they will be listed here. Selecting a database shows a list of the tables in the database. Finally, clicking a table displays the records of the table:
You can also execute SQL queries after selecting a SQLite database:
Step 3: Manipulating Your App’s Preferences
To view your app’s SharedPreferences, open the Resources tab of the Developer Tools window and select LocalStorage. You will see the names of the files your app uses to store the preferences. Clicking a file displays the key-value pairs stored in that file:
You can even edit the values stored in a file:
Note that any changes you make to the values are permanent.
4. Using dumpapp
Step 1: Downloading dumpapp
dumpapp is a powerful utility that allows you to manipulate your Android app from the command line. You can get it by cloning Stetho’s repository:
git clone https://github.com/facebook/stetho
Because dumpapp is a Python script, you should have the latest version of Python installed on your computer to use it.
Step 2: Using Plugins
To view a list of available plugins, enter the stetho/scripts directory and execute the following command:
./dumpapp -l
The output looks something like this:
Let’s use the plugin called prefs. This plugin is used to view and edit the values stored in your app’s SharedPreferences. For example, the following command lists all the key-value pairs stored in your app’s SharedPreferences:
./dumpapp prefs print
The output looks something like this:
Step 3: Creating a Custom Plugin
Custom dumpapp plugins are simply Java classes that implement the DumperPlugin interface. Let’s create a simple plugin that prints the package name of the app being tested.
Create a new class inside the MyApplication class called MyDumperPlugin. After overriding the methods of the DumperPlugin interface, your class should look like this:
class MyDumperPlugin implements DumperPlugin {
@Override
public String getName() {
}
@Override
public void dump(DumperContext dumpContext) throws DumpException {
}
}
The getName method should return the name of the plugin. To return the value my_plugin, add the following code to the getName method:
return "my_plugin";
The dump method is the method that is called when you run the plugin from the command line. The DumperContext provides various I/O streams, which allow you to read from the command line or write to it. For now, we will just be using the standard output. Add the following code to the dump method to get a reference to the standard output stream:
PrintStream out = dumpContext.getStdout();
Because this plugin is part of the MyApplication class, to get the package name of the app, you can directly call the getPackageName method. Once you have the package name, print it using the PrintStream object’s println method:
out.println(MyApplication.this.getPackageName());
Your custom plugin is now ready to use.
Step 4: Creating a Custom Plugins Provider
The plugin you created in the previous step will not be available to Stetho unless you create a custom plugins provider and use it while initializing Stetho. A custom plugins provider is a class that implements the DumperPluginsProvider interface.
Let’s create a custom plugins provider called MyDumperPluginsProvider. Create this class inside the MyApplication class. After overriding the only method of the DumperPluginsProvider interface, your class should look like this:
class MyDumperPluginsProvider implements DumperPluginsProvider {
@Override
public Iterable<DumperPlugin> get() {
}
}
Because the get method returns an Iterable, all you have to do is create a list, add your custom plugin to the list, and return the list. The code for doing that would look like this:
// Create a list
ArrayList<DumperPlugin> plugins = new ArrayList<>();
// Add one or more custom plugins
plugins.add(new MyDumperPlugin());
// Return the list
return plugins;
However, because your custom plugins provider’s Iterable does not include the default plugins, you won’t be able to use them while running dumpapp. If you want to use both the custom and the default plugins together, you should add the default plugins to your ArrayList. To get the list of default plugins, you have to call the get method of the plugins provider returned by the defaultDumperPluginsProvider method.
// Add default plugins to retain original functionality
for(DumperPlugin plugin:Stetho.defaultDumperPluginsProvider(MyApplication.this).get()){
plugins.add(plugin);
}
Your custom plugins provider is now ready. To use it, go to the onCreate method and pass an instance of it to the enableDumpapp call:
List all the available plugins again using the dumpapp -l call. You will see the name of your custom plugin in the list:
To run it, execute the following command:
./dumpapp my_plugin
It should print the package name of your app:
Conclusion
In this tutorial, you learned how to use Stetho in your Android projects. You also learned how to use both the command line interface, dumpapp, and Google Chrome’s developer tools to debug your apps. By now, you must have realized that, though Stetho cannot fully replace Android Studio’s debugger yet, it definitely offers features that can significantly improve your debugging experience.
To learn more about Stetho, refer to the code and documentation that’s available on GitHub repository.
Stetho is an open source debugging platform, developed by Facebook, that offers a rich and highly interactive debugging experience to Android developers. With Stetho, debugging native Android apps becomes as simple as debugging a web page, because it allows you to use Google Chrome’s developer tools to perform various debugging activities, such as view hierarchy inspection, network inspection, SQLite database management, and more.
In this tutorial, you are going to learn how to add Stetho to an Android project and use both Google Chrome’s developer tools and Stetho’s command line utility, dumpapp, to debug it.
1. Adding Gradle Dependencies
To add the Stetho library to your project, add com.facebook.stetho:stetho as a compile dependency in the app module’s build.gradle file:
compile 'com.facebook.stetho:stetho:1.1.1'
In this tutorial, you will be using OkHttp, a popular networking library from Square, to manage all network connections, because it plays really well with Stetho. Add it as another compile dependency:
compile 'com.facebook.stetho:stetho-okhttp:1.1.1'
2. Initializing Stetho
Step 1: Creating a Custom Application Class
The best time to initialize Stetho is when your application is starting. Therefore, you have to create a new class that extends Application and initialize Stetho inside its onCreate method.
Create a new class called MyApplication and override its onCreate method:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
}
}
To initialize Stetho, you must first create an instance of Stetho.InitializerBuilder, using the Stetho.newInitializerBuilder method. Next, to allow Stetho to work with Chrome’s developer tools, you must call enableWebKitInspector. If you also want to enable dumpapp, you must call enableDumpapp. Once Stetho.InitializerBuilder is ready, you can call its build method to generate an Initializer object and pass it to the Stetho.initialize method.
For now, let’s enable the default functionality by using the default InspectorModulesProvider and DumperPluginsProvider. Add the following code to the onCreate method:
// Create an InitializerBuilder
Stetho.InitializerBuilder initializerBuilder =
Stetho.newInitializerBuilder(this);
// Enable Chrome DevTools
initializerBuilder.enableWebKitInspector(
Stetho.defaultInspectorModulesProvider(this)
);
// Enable command line interface
initializerBuilder.enableDumpapp(
Stetho.defaultDumperPluginsProvider(context)
);
// Use the InitializerBuilder to generate an Initializer
Stetho.Initializer initializer = initializerBuilder.build();
// Initialize Stetho with the Initializer
Stetho.initialize(initializer);
Step 2: Editing the Manifest
To let the Android operating system know that you have a custom Application class, add an attribute called android:name to the manifest’s application tag and set the value to the name of your custom Application class.
After compiling and installing your app on an Android device (or the emulator), start Google Chrome and type in chrome://inspect in the address bar. You will see a screen that looks like this:
Click the inspect link to open the Developer Tools.
Step 1: Inspecting Network Connections
Stetho allows you to inspect, in real time, the network connections that your app makes. However, in Stetho version 1.1.1, this only works with the OkHttp network library. When using OkHttp with Stetho, you should remember to add a StethoInterceptor to the OkHttpClient object’s List of network interceptors.
Here’s some sample code that connects to HttpBin and retrieves a JSON document:
// Create an instance of OkHttpClient
OkHttpClient httpClient = new OkHttpClient();
// Add Stetho interceptor
httpClient.networkInterceptors().add(new StethoInterceptor());
try {
// Fetch the contents of http://httpbin.org/ip
Response response = httpClient.newCall(
new Request.Builder().url("http://httpbin.org/ip").build()
).execute();
} catch(IOException ioe) {
Log.d("StethoTut", ioe.getMessage());
}
When the code runs, you will see the following in the Network tab of the Developer Tools window:
If you click the URL in the first column, you will be taken to a screen that displays more information about the response:
Step 2: Querying SQLite Databases
With Stetho, you can perform a lot of operations on your app’s SQLite databases. Click the Resources tab and select Web SQL. If your app has any SQLite databases, they will be listed here. Selecting a database shows a list of the tables in the database. Finally, clicking a table displays the records of the table:
You can also execute SQL queries after selecting a SQLite database:
Step 3: Manipulating Your App’s Preferences
To view your app’s SharedPreferences, open the Resources tab of the Developer Tools window and select LocalStorage. You will see the names of the files your app uses to store the preferences. Clicking a file displays the key-value pairs stored in that file:
You can even edit the values stored in a file:
Note that any changes you make to the values are permanent.
4. Using dumpapp
Step 1: Downloading dumpapp
dumpapp is a powerful utility that allows you to manipulate your Android app from the command line. You can get it by cloning Stetho’s repository:
git clone https://github.com/facebook/stetho
Because dumpapp is a Python script, you should have the latest version of Python installed on your computer to use it.
Step 2: Using Plugins
To view a list of available plugins, enter the stetho/scripts directory and execute the following command:
./dumpapp -l
The output looks something like this:
Let’s use the plugin called prefs. This plugin is used to view and edit the values stored in your app’s SharedPreferences. For example, the following command lists all the key-value pairs stored in your app’s SharedPreferences:
./dumpapp prefs print
The output looks something like this:
Step 3: Creating a Custom Plugin
Custom dumpapp plugins are simply Java classes that implement the DumperPlugin interface. Let’s create a simple plugin that prints the package name of the app being tested.
Create a new class inside the MyApplication class called MyDumperPlugin. After overriding the methods of the DumperPlugin interface, your class should look like this:
class MyDumperPlugin implements DumperPlugin {
@Override
public String getName() {
}
@Override
public void dump(DumperContext dumpContext) throws DumpException {
}
}
The getName method should return the name of the plugin. To return the value my_plugin, add the following code to the getName method:
return "my_plugin";
The dump method is the method that is called when you run the plugin from the command line. The DumperContext provides various I/O streams, which allow you to read from the command line or write to it. For now, we will just be using the standard output. Add the following code to the dump method to get a reference to the standard output stream:
PrintStream out = dumpContext.getStdout();
Because this plugin is part of the MyApplication class, to get the package name of the app, you can directly call the getPackageName method. Once you have the package name, print it using the PrintStream object’s println method:
out.println(MyApplication.this.getPackageName());
Your custom plugin is now ready to use.
Step 4: Creating a Custom Plugins Provider
The plugin you created in the previous step will not be available to Stetho unless you create a custom plugins provider and use it while initializing Stetho. A custom plugins provider is a class that implements the DumperPluginsProvider interface.
Let’s create a custom plugins provider called MyDumperPluginsProvider. Create this class inside the MyApplication class. After overriding the only method of the DumperPluginsProvider interface, your class should look like this:
class MyDumperPluginsProvider implements DumperPluginsProvider {
@Override
public Iterable<DumperPlugin> get() {
}
}
Because the get method returns an Iterable, all you have to do is create a list, add your custom plugin to the list, and return the list. The code for doing that would look like this:
// Create a list
ArrayList<DumperPlugin> plugins = new ArrayList<>();
// Add one or more custom plugins
plugins.add(new MyDumperPlugin());
// Return the list
return plugins;
However, because your custom plugins provider’s Iterable does not include the default plugins, you won’t be able to use them while running dumpapp. If you want to use both the custom and the default plugins together, you should add the default plugins to your ArrayList. To get the list of default plugins, you have to call the get method of the plugins provider returned by the defaultDumperPluginsProvider method.
// Add default plugins to retain original functionality
for(DumperPlugin plugin:Stetho.defaultDumperPluginsProvider(MyApplication.this).get()){
plugins.add(plugin);
}
Your custom plugins provider is now ready. To use it, go to the onCreate method and pass an instance of it to the enableDumpapp call:
List all the available plugins again using the dumpapp -l call. You will see the name of your custom plugin in the list:
To run it, execute the following command:
./dumpapp my_plugin
It should print the package name of your app:
Conclusion
In this tutorial, you learned how to use Stetho in your Android projects. You also learned how to use both the command line interface, dumpapp, and Google Chrome’s developer tools to debug your apps. By now, you must have realized that, though Stetho cannot fully replace Android Studio’s debugger yet, it definitely offers features that can significantly improve your debugging experience.
To learn more about Stetho, refer to the code and documentation that’s available on GitHub repository.
These days, you don’t need image editing software like Adobe Photoshop or Gimp to create user interface mock-ups for your mobile apps. An app that conforms to Google’s Material Design language is usually composed of only simple geometric shapes, solid colors, icons, and text. A user interface prototype for such an app can be created easily using just code.
In this two-part series, you are going to learn the basics of Framer, an open source Javascript framework that lets you programmatically create interactive and realistic prototypes with beautiful animations for iOS and Android apps.
a basic understanding of HTML, CSS, and Javascript
1. Initial Setup
Step 1: Create an HTML Page
Because a Framer prototype is nothing but an ordinary web page written in HTML, CSS, and Javascript, let’s start by creating a blank HTML page. I am going to call this page index.html.
To make use of Framer’s API on this page, you should add a script tag that points to the framer.js file you downloaded.
<script src="framer.js"></script>
Step 2: Create an HTTP Server
As Framer makes use of protocol-relative URLs to load various resources, you can’t simply double-click the file you created to open it in a browser. Doing so will lead to network errors. Instead, you should access it through an HTTP server.
To quickly create an HTTP server that is capable of serving your web page, you can use Python’s SimpleHTTPServer module.
Open a terminal, navigate to the directory that contains the web page you created, and execute the following command.
python -m SimpleHTTPServer 8000
This will start a server that runs on port 8000 by default. You can now open Google Chrome and view your web page by visiting http://localhost:8000/.
Step 3: Draw a Device
To make your prototype feel realistic on a desktop browser, you should display all its elements inside the frame of a mobile device. Framer lets you draw a variety of popular mobile devices, such iPhones, Nexus phones and tablets, iPads, Apple Watches, and more. For this tutorial, I will be using a pink iPhone 5c.
To draw a device, you should first create an instance of the DeviceComponent class and call its setupContext method. You can then change its deviceType to the device of your choosing. Add another script tag to the HTML page you created earlier and add the following code to it:
var device = new Framer.DeviceComponent();
device.setupContext();
device.deviceType = "iphone-5c-pink";
When you refresh your web page, you should see the following in your browser window:
If you want, you can also render a hand holding the device by adding -hand at the end of the deviceType string. If the device looks too big or too small, you can use the setDeviceScale method to change its size.
// Display a hand with the device
device.deviceType = "iphone-5c-pink-hand";
// Change the size
device.setDeviceScale(0.3);
This completes the initial setup. The result should looks like this:
2. Drawing Boxes, Text, and Images
Almost every element in your Framer prototype will be an instance of the Layer class. A Layer is very similar to an HTML div element and can be used to draw rectangles, images, and text.
To create a Layer you have to call its constructor and pass it a JSON object that defines various properties of the Layer. While creating a Layer, you usually specify its dimensions (width and height) and position (x and y). You can also use the centerX and centerY methods to center it horizontally and vertically. Here’s an example of how to create a Layer.
// Draw a white square
var whiteSquare = new Layer(
{
backgroundColor: "#FFFFFF",
width: 400,
height: 400,
y: 20
}
);
// Center horizontally
whiteSquare.centerX();
To display an image, you have to create a Layer whose image property points to the image file you want to display.
// Draw an image
var pic = new Layer(
{
image: "painting.jpg",
width: 400,
height: 400,
y: 440
}
);
pic.centerX();
To display text (or HTML), you can use the html property. You can also add CSS styling to a Layer using its style property.
// Write text
var text = new Layer(
{
width: Screen.width,
height: 100,
y: 860,
html: "This is a prototype",
style: {
fontSize: "50px",
textAlign: "center",
color: "#f1f2f3",
paddingTop: "18px"
}
}
);
With the three Layer objects we created in this step, the prototype would look like this:
3. Adding Events
You can attach event handlers to a Layer using the on method. The on method is much like Javascript’s addEventListener method. It takes the name of an event as its first parameter and a function as its second parameter.
Here’s how you add a click handler to the text layer we created in the previous step:
text.on("click", function(){
text.html = "I was clicked";
});
You will see more event handlers later in this tutorial.
4. Adding Animation
Framer stands out from its competition thanks to its advanced animation effects. With Framer, you can animate nearly every property of your Layer objects using the animate method. The animate method takes as input a JSON object that specifies the properties that should be animated.
The JSON object can also include various configuration details of the animation, such as its duration and behavior.
As an example, let me show you how to create an animation that turns whiteSquare into a circle by changing its borderRadius.
Here’s another example that shows how to animate the shadow of whiteSquare when it is clicked.
// Animate Shadow
whiteSquare.on("click", function(){
// Set the shadow color first
whiteSquare.shadowColor = "#555555";
whiteSquare.animate({
"properties": {
shadowBlur: 40,
shadowSpread: 10,
}
});
});
Note that only those properties whose values are numbers can be animated. As shadowColor is not a number, it should be set before calling animate.
5. Using States
If you are using Framer, it is likely that you are trying to create a highly interactive prototype with lots of animations. Calling the animate method multiple times on a Layer can get tedious. Instead, you can associate a list of states with a Layer and just switch between the states when needed.
Every Layer has a states property that is an instance of the LayerStates class. To add new states to a Layer, you call the add method on the states property. In the following code snippet, we add two new states to the pic object.
// Add two states
pic.states.add({
"myState1" : {
borderRadius: 100
},
"myState2": {
borderRadius: 200
}
});
Adding a state doesn’t result in an immediate visual change. However, when a Layer switches from one state to another, you will be able to see the animation. To change the state of a Layer, you call the switch method on the states property of the Layer object. The following code snippet shows you how to change the state of pic when it is clicked.
// Change state when clicked
pic.on("click", function() {
// Switch to myState2
pic.states.switch("myState2");
});
To cycle through the states of a Layer, you can call the next method of its states object.
pic.states.next();
6. Changing Backgrounds
To add a background color or image to your prototype, you create a BackgroundLayer object. A BackgroundLayer is a Layer whose dimensions are equal to the dimensions of the device’s screen. Here’s how you add a grey BackgroundLayer:
var bg = new BackgroundLayer({
backgroundColor: "#BDBDBD"
});
Because the Framer prototype is just an ordinary HTML page, you can also use CSS to style it. For example, if you aren’t happy with the white color surrounding the device, you can change it by applying a new style to the web page’s body tag.
With these changes, the prototype will look like this when the animations are finished:
7. Handling Drag Operations
To make a Layer draggable, all you have to do is set its draggable.enabled property to true.
// Allow dragging
pic.draggable.enabled = true;
If a Layer is draggable, you are able to add event listeners to it that respond to various dragging related events, such as dragend and dragmove. For example, here’s a dragend event handler that returns pic to its original position:
// Handle dragend
pic.on("dragend", function(){
pic.animate({
"properties": {
x: Screen.width/2 - pic.width/2, // Place at Center
y: 440 // Original Y
}
});
});
Conclusion
In this tutorial, you learned the basics of creating interactive prototypes for mobile apps using Framer. As your Framer prototypes are static web pages, you can upload them to any HTTP server to share them with your friends and clients.
I’d also like to tell you that, if you are competent with Adobe Photoshop, you don’t have to create the user interface elements of your prototypes programmatically. You can design the layout in Photoshop and convert the layer groups in your PSD into Framer’s Layer objects. You also have the option to purchase and use Framer Studio, an IDE built specifically for working with Framer projects.
To learn more about the Framer framework, you can refer to Framer’s documentation. In the second part of this series, I will dive deeper into navigation, scrolling, and animation.
These days, you don’t need image editing software like Adobe Photoshop or Gimp to create user interface mock-ups for your mobile apps. An app that conforms to Google’s Material Design language is usually composed of only simple geometric shapes, solid colors, icons, and text. A user interface prototype for such an app can be created easily using just code.
In this two-part series, you are going to learn the basics of Framer, an open source Javascript framework that lets you programmatically create interactive and realistic prototypes with beautiful animations for iOS and Android apps.
a basic understanding of HTML, CSS, and Javascript
1. Initial Setup
Step 1: Create an HTML Page
Because a Framer prototype is nothing but an ordinary web page written in HTML, CSS, and Javascript, let’s start by creating a blank HTML page. I am going to call this page index.html.
To make use of Framer’s API on this page, you should add a script tag that points to the framer.js file you downloaded.
<script src="framer.js"></script>
Step 2: Create an HTTP Server
As Framer makes use of protocol-relative URLs to load various resources, you can’t simply double-click the file you created to open it in a browser. Doing so will lead to network errors. Instead, you should access it through an HTTP server.
To quickly create an HTTP server that is capable of serving your web page, you can use Python’s SimpleHTTPServer module.
Open a terminal, navigate to the directory that contains the web page you created, and execute the following command.
python -m SimpleHTTPServer 8000
This will start a server that runs on port 8000 by default. You can now open Google Chrome and view your web page by visiting http://localhost:8000/.
Step 3: Draw a Device
To make your prototype feel realistic on a desktop browser, you should display all its elements inside the frame of a mobile device. Framer lets you draw a variety of popular mobile devices, such iPhones, Nexus phones and tablets, iPads, Apple Watches, and more. For this tutorial, I will be using a pink iPhone 5c.
To draw a device, you should first create an instance of the DeviceComponent class and call its setupContext method. You can then change its deviceType to the device of your choosing. Add another script tag to the HTML page you created earlier and add the following code to it:
var device = new Framer.DeviceComponent();
device.setupContext();
device.deviceType = "iphone-5c-pink";
When you refresh your web page, you should see the following in your browser window:
If you want, you can also render a hand holding the device by adding -hand at the end of the deviceType string. If the device looks too big or too small, you can use the setDeviceScale method to change its size.
// Display a hand with the device
device.deviceType = "iphone-5c-pink-hand";
// Change the size
device.setDeviceScale(0.3);
This completes the initial setup. The result should looks like this:
2. Drawing Boxes, Text, and Images
Almost every element in your Framer prototype will be an instance of the Layer class. A Layer is very similar to an HTML div element and can be used to draw rectangles, images, and text.
To create a Layer you have to call its constructor and pass it a JSON object that defines various properties of the Layer. While creating a Layer, you usually specify its dimensions (width and height) and position (x and y). You can also use the centerX and centerY methods to center it horizontally and vertically. Here’s an example of how to create a Layer.
// Draw a white square
var whiteSquare = new Layer(
{
backgroundColor: "#FFFFFF",
width: 400,
height: 400,
y: 20
}
);
// Center horizontally
whiteSquare.centerX();
To display an image, you have to create a Layer whose image property points to the image file you want to display.
// Draw an image
var pic = new Layer(
{
image: "painting.jpg",
width: 400,
height: 400,
y: 440
}
);
pic.centerX();
To display text (or HTML), you can use the html property. You can also add CSS styling to a Layer using its style property.
// Write text
var text = new Layer(
{
width: Screen.width,
height: 100,
y: 860,
html: "This is a prototype",
style: {
fontSize: "50px",
textAlign: "center",
color: "#f1f2f3",
paddingTop: "18px"
}
}
);
With the three Layer objects we created in this step, the prototype would look like this:
3. Adding Events
You can attach event handlers to a Layer using the on method. The on method is much like Javascript’s addEventListener method. It takes the name of an event as its first parameter and a function as its second parameter.
Here’s how you add a click handler to the text layer we created in the previous step:
text.on("click", function(){
text.html = "I was clicked";
});
You will see more event handlers later in this tutorial.
4. Adding Animation
Framer stands out from its competition thanks to its advanced animation effects. With Framer, you can animate nearly every property of your Layer objects using the animate method. The animate method takes as input a JSON object that specifies the properties that should be animated.
The JSON object can also include various configuration details of the animation, such as its duration and behavior.
As an example, let me show you how to create an animation that turns whiteSquare into a circle by changing its borderRadius.
Here’s another example that shows how to animate the shadow of whiteSquare when it is clicked.
// Animate Shadow
whiteSquare.on("click", function(){
// Set the shadow color first
whiteSquare.shadowColor = "#555555";
whiteSquare.animate({
"properties": {
shadowBlur: 40,
shadowSpread: 10,
}
});
});
Note that only those properties whose values are numbers can be animated. As shadowColor is not a number, it should be set before calling animate.
5. Using States
If you are using Framer, it is likely that you are trying to create a highly interactive prototype with lots of animations. Calling the animate method multiple times on a Layer can get tedious. Instead, you can associate a list of states with a Layer and just switch between the states when needed.
Every Layer has a states property that is an instance of the LayerStates class. To add new states to a Layer, you call the add method on the states property. In the following code snippet, we add two new states to the pic object.
// Add two states
pic.states.add({
"myState1" : {
borderRadius: 100
},
"myState2": {
borderRadius: 200
}
});
Adding a state doesn’t result in an immediate visual change. However, when a Layer switches from one state to another, you will be able to see the animation. To change the state of a Layer, you call the switch method on the states property of the Layer object. The following code snippet shows you how to change the state of pic when it is clicked.
// Change state when clicked
pic.on("click", function() {
// Switch to myState2
pic.states.switch("myState2");
});
To cycle through the states of a Layer, you can call the next method of its states object.
pic.states.next();
6. Changing Backgrounds
To add a background color or image to your prototype, you create a BackgroundLayer object. A BackgroundLayer is a Layer whose dimensions are equal to the dimensions of the device’s screen. Here’s how you add a grey BackgroundLayer:
var bg = new BackgroundLayer({
backgroundColor: "#BDBDBD"
});
Because the Framer prototype is just an ordinary HTML page, you can also use CSS to style it. For example, if you aren’t happy with the white color surrounding the device, you can change it by applying a new style to the web page’s body tag.
With these changes, the prototype will look like this when the animations are finished:
7. Handling Drag Operations
To make a Layer draggable, all you have to do is set its draggable.enabled property to true.
// Allow dragging
pic.draggable.enabled = true;
If a Layer is draggable, you are able to add event listeners to it that respond to various dragging related events, such as dragend and dragmove. For example, here’s a dragend event handler that returns pic to its original position:
// Handle dragend
pic.on("dragend", function(){
pic.animate({
"properties": {
x: Screen.width/2 - pic.width/2, // Place at Center
y: 440 // Original Y
}
});
});
Conclusion
In this tutorial, you learned the basics of creating interactive prototypes for mobile apps using Framer. As your Framer prototypes are static web pages, you can upload them to any HTTP server to share them with your friends and clients.
I’d also like to tell you that, if you are competent with Adobe Photoshop, you don’t have to create the user interface elements of your prototypes programmatically. You can design the layout in Photoshop and convert the layer groups in your PSD into Framer’s Layer objects. You also have the option to purchase and use Framer Studio, an IDE built specifically for working with Framer projects.
To learn more about the Framer framework, you can refer to Framer’s documentation. In the second part of this series, I will dive deeper into navigation, scrolling, and animation.
The new operating system for Apple Watch, watchOS 2, was introduced a couple of weeks ago at WWDC 2015. It brings a lot of improvements, mostly for developers looking to create an Apple Watch app. These are the things that I find to be most important for developers:
WatchKit apps are now running natively on the watch. This brings the much needed improvement in speed, resulting in a better user experience.
The new Watch Connectivity framework enables all sorts of communication and data sharing between the parent iOS app and the watchOS app.
watchOS 2 apps have access to hardware data, such as data from the motion sensor, audio recording, and they can even access heart rate data.
watchOS 2 also introduced animations. On watchOS 1, the only option to perform an animation was to generate a series of images and then iterate through them. watchOS 2 brings true animations to the Apple Watch. You can animate the user interface by changing layout properties inside an animation block. That’s where this tutorial comes in.
1. Why Care About Animations?
Before we get to the nuts and bolts, I’d like to spend a minute talking about the purpose of animations on Apple Watch apps.
The obvious reason is that they make the user interface more enjoyable if used appropriately. And when it comes to Apple Watch, that is a big if. Since most app interactions only last for a few seconds, you really don’t want to go overboard with animations.
The second, and I believe more important reason, is that they allow for custom navigation hierarchies inside Apple Watch apps. Let's suppose you need to present a screen that the user can only leave by taking a specific action. Normally, Apple Watch apps always have a cancel button in the top left corner when a modal interface controller is presented. With animations and clever layout manipulation, you could create your own "present view controller" routine that shows your app's content full-screen, dismissing it by that specific action. That is one of the things you’ll learn in this tutorial.
2. Prerequisites
Before you delve into this tutorial, you should have a basic knowledge of how the layout system works on WatchKit. Even if you are an experienced iOS developer, the group-based layout in WatchKit is very different than what you are used to on iOS. You need to think about the layout in a very different way. But once you get used to it, you will be able to create most layouts without much effort.
If you are new to layout on WatchKit, there is a great tutorial on Tuts+ by my friend Patrick Balestra, Understanding the WatchKit Layout system. Using an example app, he explains everything you need to know to get up to speed.
The principle of animations on watchOS 2 is simple, you set one or more of the animatable properties inside an animation block. The following example illustrates how this works.
This method causes the circleGroup to be aligned to the right, with an animation with a duration of 0.5 seconds. As you can see, we are calling animateWithDuration:animations: on self, which is an instance of WKInterfaceController. This is different from iOS where the animation methods are class methods on UIView.
The below list shows which properties are animatable:
opacity
alignment
width and height
background color
color and tint color
Bear in mind that it’s still not possible on watchOS 2 to create user interface elements at runtime. But since you can hide them or set their alpha to 0 in the storyboard, this shouldn’t be that big of a problem.
That’s it. Armed with your knowledge about the WatchKit layout system, you are now ready to start working with native animations on watchOS. Let’s get started by creating a sample app so I can show you a couple of examples of how this all fits together.
4. Basic Animations
We are going to create a simple watchOS 2 app that will introduce a couple of these animation concepts. It is by no means trying to provide a complete overview of all the things that are possible. Instead, it shows the basic idea, which will hopefully enable you to come up with solutions to what you need.
Step 1: Create the Project
At the time of writing, Xcode 7 is still in beta. To create a watchOS 2 app, you need to use Xcode 7 so that’s what I am going to use.
Launch Xcode and select File > New > Project….
Choose iOS Application with Single View Application template and click Next.
When asked for Product Name, enter WatchAnimations. You can uncheck Include Unit Testsand Include UI Tests as we won’t be needing those for this tutorial.
Click Next, choose a location to save the project, and click Create.
Step 2: Add WatchKit Target
In Xcode, select File > New > Target….
From the list of templates, choose WatchKit App from the watchOS > Application section and click Next to continue.
For Product name, you can choose anything you like. I have named mine WatchApp.
Uncheck Include Notification Scene, because we won’t need it. When you click Finish, your WatchKit target will be created.
When prompted to activate the WatchApp scheme, click Activate. Just note that you can change the scheme at any time in the top left of your Xcode window.
Step 3: Create the User Interface
Open Interface.storyboard in the WatchApp group as shown below.
Add a group to the interface by dragging it from the Object Library on the right. In the Attributes Inspector on the right, change its layout to Vertical and set its height to Relative to Container.
Add a second group to the group we just added. In the Attributes Inspector, set its vertical position to Bottom.
Add four buttons to the second group. For each button, set Size to Relative to Container with a value of 0.25. Set the titles of the buttons to ←, →, ↑, and ↓. After this step, the user interface should look like this:
To finish the first part of the user interface, add one more group to the main group and configure it as follows:
For clarity, set its name to Circle by changing its name in the Document Outline on the left.
Set its color to red.
Set its radius to 20 points.
Set its size, width and height, to 40 points.
The following screenshot shows you how the circle should be configured.
Step 4: Add Animations
In the Project navigator, expand the WatchApp Extension group and select InterfaceController.m. Replace the implementation of the InterfaceController class with the following:
These actions will move the red circle in a specific direction. And as you can see, we accomplish that by settings its vertical and/or horizontal alignment inside an animation block.
Step 5: Connect the Outlets
Open Interface.storyboard and connect the outlets as shown below.
That should do it. Run the project and, if you've followed the above steps, you should be able to move the red circle around the screen using the arrow buttons.
5. More Complex Animations
In the second part of this tutorial, we’ll create a push animation. Since most of the steps involved are similar, I will move a bit faster this time.
Step 1: Create Animation
Open InterfaceController.m and create a new outlet, firstScreenGroup, of type WKInterfaceGroup in the class extension of the InterfaceController class.
In pushButtonPressed, we shrink the first screen group (we will create it in the next step) and in popButtonPressed we expand that group again. We are also animating the alpha of the first screen group to make the animation a little bit more appealing.
Step 2: Extend the User Interface
Open Interface.storyboard and add a new group to the user interface. Put the group that was already there, the one containing Circle and the group with buttons, inside that new group. Set its Layout to Horizontal and rename the contained group to First screen. This will come in handy later. The result should look like this:
Next, add a second group that will be on the same level as the First screen group. Set its Layout to Vertical. Add an image and a button to the group. You can add literally any image, just make sure you put something there because otherwise the animation would look somewhat dry. Set the title of the button to "< Pop". Connect the button to the popButtonPressed action we created earlier. The user interface should now look like this:
Add a button to the First screen group. Set its title to "Push >" and its vertical position to Bottom. Connect the button to the pushButtonPressed action. The user interface should now look like this:
There's one thing we need to do, connecting the firstScreenGroup outlet to the group we named First screen.
Step 3: Build and Run
When you build and run the app, you should be able to present the second screen by tapping the button with title "Push >" at the bottom. You can dismiss the second screen by tapping the button with title "< Pop". It should look like this:
Conclusion
In this tutorial, we've taken a look at native animations on watchOS 2. I hope it has given you a taste of what you can achieve with animations on watchOS. If you have any questions, you can post a comment below or can contact me on Twitter.
2015-07-10T16:25:26.000Z2015-07-10T16:25:26.000ZLukas Petr
The new operating system for Apple Watch, watchOS 2, was introduced a couple of weeks ago at WWDC 2015. It brings a lot of improvements, mostly for developers looking to create an Apple Watch app. These are the things that I find to be most important for developers:
WatchKit apps are now running natively on the watch. This brings the much needed improvement in speed, resulting in a better user experience.
The new Watch Connectivity framework enables all sorts of communication and data sharing between the parent iOS app and the watchOS app.
watchOS 2 apps have access to hardware data, such as data from the motion sensor, audio recording, and they can even access heart rate data.
watchOS 2 also introduced animations. On watchOS 1, the only option to perform an animation was to generate a series of images and then iterate through them. watchOS 2 brings true animations to the Apple Watch. You can animate the user interface by changing layout properties inside an animation block. That’s where this tutorial comes in.
1. Why Care About Animations?
Before we get to the nuts and bolts, I’d like to spend a minute talking about the purpose of animations on Apple Watch apps.
The obvious reason is that they make the user interface more enjoyable if used appropriately. And when it comes to Apple Watch, that is a big if. Since most app interactions only last for a few seconds, you really don’t want to go overboard with animations.
The second, and I believe more important reason, is that they allow for custom navigation hierarchies inside Apple Watch apps. Let's suppose you need to present a screen that the user can only leave by taking a specific action. Normally, Apple Watch apps always have a cancel button in the top left corner when a modal interface controller is presented. With animations and clever layout manipulation, you could create your own "present view controller" routine that shows your app's content full-screen, dismissing it by that specific action. That is one of the things you’ll learn in this tutorial.
2. Prerequisites
Before you delve into this tutorial, you should have a basic knowledge of how the layout system works on WatchKit. Even if you are an experienced iOS developer, the group-based layout in WatchKit is very different than what you are used to on iOS. You need to think about the layout in a very different way. But once you get used to it, you will be able to create most layouts without much effort.
If you are new to layout on WatchKit, there is a great tutorial on Tuts+ by my friend Patrick Balestra, Understanding the WatchKit Layout system. Using an example app, he explains everything you need to know to get up to speed.
The principle of animations on watchOS 2 is simple, you set one or more of the animatable properties inside an animation block. The following example illustrates how this works.
This method causes the circleGroup to be aligned to the right, with an animation with a duration of 0.5 seconds. As you can see, we are calling animateWithDuration:animations: on self, which is an instance of WKInterfaceController. This is different from iOS where the animation methods are class methods on UIView.
The below list shows which properties are animatable:
opacity
alignment
width and height
background color
color and tint color
Bear in mind that it’s still not possible on watchOS 2 to create user interface elements at runtime. But since you can hide them or set their alpha to 0 in the storyboard, this shouldn’t be that big of a problem.
That’s it. Armed with your knowledge about the WatchKit layout system, you are now ready to start working with native animations on watchOS. Let’s get started by creating a sample app so I can show you a couple of examples of how this all fits together.
4. Basic Animations
We are going to create a simple watchOS 2 app that will introduce a couple of these animation concepts. It is by no means trying to provide a complete overview of all the things that are possible. Instead, it shows the basic idea, which will hopefully enable you to come up with solutions to what you need.
Step 1: Create the Project
At the time of writing, Xcode 7 is still in beta. To create a watchOS 2 app, you need to use Xcode 7 so that’s what I am going to use.
Launch Xcode and select File > New > Project….
Choose iOS Application with Single View Application template and click Next.
When asked for Product Name, enter WatchAnimations. You can uncheck Include Unit Testsand Include UI Tests as we won’t be needing those for this tutorial.
Click Next, choose a location to save the project, and click Create.
Step 2: Add WatchKit Target
In Xcode, select File > New > Target….
From the list of templates, choose WatchKit App from the watchOS > Application section and click Next to continue.
For Product name, you can choose anything you like. I have named mine WatchApp.
Uncheck Include Notification Scene, because we won’t need it. When you click Finish, your WatchKit target will be created.
When prompted to activate the WatchApp scheme, click Activate. Just note that you can change the scheme at any time in the top left of your Xcode window.
Step 3: Create the User Interface
Open Interface.storyboard in the WatchApp group as shown below.
Add a group to the interface by dragging it from the Object Library on the right. In the Attributes Inspector on the right, change its layout to Vertical and set its height to Relative to Container.
Add a second group to the group we just added. In the Attributes Inspector, set its vertical position to Bottom.
Add four buttons to the second group. For each button, set Size to Relative to Container with a value of 0.25. Set the titles of the buttons to ←, →, ↑, and ↓. After this step, the user interface should look like this:
To finish the first part of the user interface, add one more group to the main group and configure it as follows:
For clarity, set its name to Circle by changing its name in the Document Outline on the left.
Set its color to red.
Set its radius to 20 points.
Set its size, width and height, to 40 points.
The following screenshot shows you how the circle should be configured.
Step 4: Add Animations
In the Project navigator, expand the WatchApp Extension group and select InterfaceController.m. Replace the implementation of the InterfaceController class with the following:
These actions will move the red circle in a specific direction. And as you can see, we accomplish that by settings its vertical and/or horizontal alignment inside an animation block.
Step 5: Connect the Outlets
Open Interface.storyboard and connect the outlets as shown below.
That should do it. Run the project and, if you've followed the above steps, you should be able to move the red circle around the screen using the arrow buttons.
5. More Complex Animations
In the second part of this tutorial, we’ll create a push animation. Since most of the steps involved are similar, I will move a bit faster this time.
Step 1: Create Animation
Open InterfaceController.m and create a new outlet, firstScreenGroup, of type WKInterfaceGroup in the class extension of the InterfaceController class.
In pushButtonPressed, we shrink the first screen group (we will create it in the next step) and in popButtonPressed we expand that group again. We are also animating the alpha of the first screen group to make the animation a little bit more appealing.
Step 2: Extend the User Interface
Open Interface.storyboard and add a new group to the user interface. Put the group that was already there, the one containing Circle and the group with buttons, inside that new group. Set its Layout to Horizontal and rename the contained group to First screen. This will come in handy later. The result should look like this:
Next, add a second group that will be on the same level as the First screen group. Set its Layout to Vertical. Add an image and a button to the group. You can add literally any image, just make sure you put something there because otherwise the animation would look somewhat dry. Set the title of the button to "< Pop". Connect the button to the popButtonPressed action we created earlier. The user interface should now look like this:
Add a button to the First screen group. Set its title to "Push >" and its vertical position to Bottom. Connect the button to the pushButtonPressed action. The user interface should now look like this:
There's one thing we need to do, connecting the firstScreenGroup outlet to the group we named First screen.
Step 3: Build and Run
When you build and run the app, you should be able to present the second screen by tapping the button with title "Push >" at the bottom. You can dismiss the second screen by tapping the button with title "< Pop". It should look like this:
Conclusion
In this tutorial, we've taken a look at native animations on watchOS 2. I hope it has given you a taste of what you can achieve with animations on watchOS. If you have any questions, you can post a comment below or can contact me on Twitter.
2015-07-10T16:25:26.000Z2015-07-10T16:25:26.000ZLukas Petr
Bluetooth has become a very popular technology, especially on mobile devices. It's a technology to discover and transfer data between nearby devices. Virtually every modern mobile device has Bluetooth capabilities these days. If you want to make an app interface with another Bluetooth enabled device, ranging from phones to speakers, you must know how to use Android's Bluetooth API.
In this tutorial, we will be making an app that is similar to the built-in Bluetooth app in Android's settings. It will include the following features:
enable Bluetooth on a device
display a list of paired devices
discover and list nearby Bluetooth devices
We will also go over the basics to connect and send data to another Bluetooth device. I've created a project to get us started, which you can download from GitHub. The below screenshot illustrates what the starter project looks like. If you get stuck or run into problems, then you can take a look at the finished project on GitHub.
1. Enabling Bluetooth
Before we can enable Bluetooth on an Android device, we need to request the necessary permissions. We do this in the app's manifest. The BLUETOOTH permission allows our app to connect, disconnect, and transfer data with another Bluetooth device. The BLUETOOTH_ADMIN permission allows our app to discover new Bluetooth devices and change the device's Bluetooth settings.
We will use the Bluetooth adapter to interface with Bluetooth. We instantiate the adapter in the ListActivity class. If the adapter is null, this means Bluetooth is not supported by the device and the app will not work on the current device. We handle this situation by showing an alert dialog to the user and exiting the app.
@Override
protected void onCreate(Bundle savedInstanceState) {
...
BTAdapter = BluetoothAdapter.getDefaultAdapter();
// Phone does not support Bluetooth so let the user know and exit.
if (BTAdapter == null) {
new AlertDialog.Builder(this)
.setTitle("Not compatible")
.setMessage("Your phone does not support Bluetooth")
.setPositiveButton("Exit", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
System.exit(0);
}
})
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
}
}
If Bluetooth is available on the device, we need to enable it. To enable Bluetooth, we start an intent provided to us by the Android SDK, BluetoothAdapter.ACTION_REQUEST_ENABLE. This will present a dialog to the user, asking them for permission to enable Bluetooth on the device. REQUEST_BLUETOOTH is a static integer we set to identify the activity request.
public class ListActivity extends ActionBarActivity implements DeviceListFragment.OnFragmentInteractionListener {
public static int REQUEST_BLUETOOTH = 1;
...
protected void onCreate(Bundle savedInstanceState) {
...
if (!BTAdapter.isEnabled()) {
Intent enableBT = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBT, REQUEST_BLUETOOTH);
}
}
}
2. Obtaining a List of Paired Devices
In this step, we scan for paired Bluetooth devices and display them in a list. In the context of a mobile device, a Bluetooth device can either be:
unknown
paired
connected
It is important to know the difference between a paired and a connected Bluetooth device. Paired devices are aware of each other’s existence and share a link key, which can be used to authenticate, resulting in a connection. Devices are automatically paired once an encrypted connection is established.
Connected devices share an RFCOMM channel, allowing them to send and receive data. A device may have many paired devices, but it can only be connected to one device at a time.
Bluetooth devices are represented by the BluetoothDevice object. A list of paired devices can be obtained by invoking the getBondedDevices() method, which returns a set of BluetoothDevice objects. We invoke the getBondedDevices() method in the DeviceListFragment's onCreate() method.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("DEVICELIST", "Super called for DeviceListFragment onCreate\n");
deviceItemList = new ArrayList<DeviceItem>();
Set<BluetoothDevice> pairedDevices = bTAdapter.getBondedDevices();
}
We use the getName() and getAddress() methods to obtain more information about the Bluetooth devices. The getName() method returns the public identifier of the device while the getAddress() method returns the device's MAC address, an identifier uniquely identifying the device.
Now that we have a list of the paired devices, we create a DeviceItem object for each BluetoothDevice object. We then add each DeviceItem object to an array named deviceItemList. We'll use this array to display the list of paired Bluetooth devices in our app. The code for displaying the list of DeviceItem objects is already present in the starter project.
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
DeviceItem newDevice= new DeviceItem(device.getName(),device.getAddress(),"false");
deviceItemList.add(newDevice);
}
}
3. Discover Nearby Bluetooth Devices
The next step is to discover devices the device isn't paired with yet, unknown devices, and add them to the list of paired devices. We do this when the user taps the scan button. The code to handle this is located in DeviceListFragment.
We first need to make a BroadcastReceiver and override the onReceive() method. The onReceive() method is invoked whenever a a Bluetooth device is found.
The onReceive() method takes an intent as its second argument. We can check what kind of intent is broadcasting with by invoking getAction(). If the action is BluetoothDevice.ACTION_FOUND, then we know we have found a Bluetooth device. When this occurs, we create a DeviceItem object using the device's name and MAC address. Finally, we add the DeviceItem object to the ArrayAdapter to display it in our app.
public class DeviceListFragment extends Fragment implements AbsListView.OnItemClickListener{
...
private final BroadcastReceiver bReciever = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Create a new device item
DeviceItem newDevice = new DeviceItem(device.getName(), device.getAddress(), "false");
// Add it to our adapter
mAdapter.add(newDevice);
}
}
};
}
When the scan button is toggled on, we simply need to register the receiver we just made and invoke the startDiscovery() method. If the scan button is toggled off, we unregister the receiver and invoke cancelDiscovery(). Keep in mind that discovery takes up a lot of resources. If your application connects with another Bluetooth device, you should always cancel discovery prior to connecting.
We also clear the ArrayAdapter object, mAdapter, when discovery begins. When we start scanning, we don't want to include old devices that may no longer be in range of the device.
That's it. We have finished our Bluetooth scanner.
4. Connecting to a Device
Bluetooth connections work like any other connection. There is a server and a client, which communicate via RFCOMM sockets. On Android, RFCOMM sockets are represented as a BluetoothSocket object. Fortunately for us, most of the technical code for servers is handled by the Android SDK and available through the Bluetooth API.
Connecting as a client is simple. Your first obtain the RFCOMM socket from the desired BluetoothDevice by calling createRfcommSocketToServiceRecord(), passing in a UUID, a 128-bit value that you create. The UUID is similar to a port number.
For example, let's assume you are making a chat app that uses Bluetooth to chat with other nearby users. To find other users to chat with, you would want to look for other devices with your chat app installed. To do this, we would look for the UUID in the list of services of the nearby devices. Using a UUID to listen and accept Bluetooth connections automatically adds that UUID to the phone's list of services, or service discovery protocol.
Once the BluetoothSocket is created, you call connect() on the BluetoothSocket. This will initialize a connection with the BluetoothDevice through the RFCOMM socket. Once our device is connected, we can use the socket to exchange data with the connected device. Doing this is similar to any standard server implementation.
Maintaining a Bluetooth connection is costly so we need to close the socket when we no longer need it. To close the socket, we call close() on the BluetoothSocket.
The following code snippet shows how to connect with a given BluetoothDevice:
public class ConnectThread extends Thread{
private BluetoothSocket bTSocket;
public boolean connect(BluetoothDevice bTDevice, UUID mUUID) {
BluetoothSocket temp = null;
try {
temp = bTDevice.createRfcommSocketToServiceRecord(mUUID);
} catch (IOException e) {
Log.d("CONNECTTHREAD","Could not create RFCOMM socket:" + e.toString());
return false;
}
try {
bTSocket.connect();
} catch(IOException e) {
Log.d("CONNECTTHREAD","Could not connect: " + e.toString());
try {
bTSocket.close();
} catch(IOException close) {
Log.d("CONNECTTHREAD", "Could not close connection:" + e.toString());
return false;
}
}
return true;
}
public boolean cancel() {
try {
bTSocket.close();
} catch(IOException e) {
Log.d("CONNECTTHREAD","Could not close connection:" + e.toString());
return false;
}
return true;
}
}
Connecting as a server is slightly more difficult. First, from your BluetoothAdapter, you must get a BluetoothServerSocket, which will be used to listen for a connection. This is only used to obtain the connection's shared RFCOMM socket. Once the connection is established, the server socket is no longer need and can be closed by calling close() on it.
We instantiate a server socket by calling listenUsingRfcommWithServiceRecord(String name, UUID mUUID). This method takes two parameters, a name of type String and a unique identifier of type UUID. The name parameter is the name we give the service when it is added to the phone's SDP (Service Discovery Protocol) entry. The unique identifier should match the UUID the client trying to connect is using.
We then call accept() on the newly obtained BluetoothServerSocket to wait for a connection. When the accept() call returns something that isn't null, we assign it to our BluetoothSocket, which we can then use to exchange data with the connected device.
The following code snippet shows how to accept a connection as a server:
public class ServerConnectThread extends Thread{
private BluetoothSocket bTSocket;
public ServerConnectThread() { }
public void acceptConnect(BluetoothAdapter bTAdapter, UUID mUUID) {
BluetoothServerSocket temp = null;
try {
temp = bTAdapter.listenUsingRfcommWithServiceRecord("Service_Name", mUUID);
} catch(IOException e) {
Log.d("SERVERCONNECT", "Could not get a BluetoothServerSocket:" + e.toString());
}
while(true) {
try {
bTSocket = temp.accept();
} catch (IOException e) {
Log.d("SERVERCONNECT", "Could not accept an incoming connection.");
break;
}
if (bTSocket != null) {
try {
temp.close();
} catch (IOException e) {
Log.d("SERVERCONNECT", "Could not close ServerSocket:" + e.toString());
}
break;
}
}
}
public void closeConnect() {
try {
bTSocket.close();
} catch(IOException e) {
Log.d("SERVERCONNECT", "Could not close connection:" + e.toString());
}
}
}
Reading and writing to the connection is done using streams, InputStream and OutputStream. We can get a reference to these streams by calling getInputStream() and getOutputStream() on the BluetoothSocket. To read from and write to these streams, we call read() and write() respectively.
The following code snippet shows how to do this for a single integer:
public class ManageConnectThread extends Thread {
public ManageConnectThread() { }
public void sendData(BluetoothSocket socket, int data) throws IOException{
ByteArrayOutputStream output = new ByteArrayOutputStream(4);
output.write(data);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(output.toByteArray());
}
public int receiveData(BluetoothSocket socket) throws IOException{
byte[] buffer = new byte[4];
ByteArrayInputStream input = new ByteArrayInputStream(buffer);
InputStream inputStream = socket.getInputStream();
inputStream.read(buffer);
return input.read();
}
}
Bluetooth has become a very popular technology, especially on mobile devices. It's a technology to discover and transfer data between nearby devices. Virtually every modern mobile device has Bluetooth capabilities these days. If you want to make an app interface with another Bluetooth enabled device, ranging from phones to speakers, you must know how to use Android's Bluetooth API.
In this tutorial, we will be making an app that is similar to the built-in Bluetooth app in Android's settings. It will include the following features:
enable Bluetooth on a device
display a list of paired devices
discover and list nearby Bluetooth devices
We will also go over the basics to connect and send data to another Bluetooth device. I've created a project to get us started, which you can download from GitHub. The below screenshot illustrates what the starter project looks like. If you get stuck or run into problems, then you can take a look at the finished project on GitHub.
1. Enabling Bluetooth
Before we can enable Bluetooth on an Android device, we need to request the necessary permissions. We do this in the app's manifest. The BLUETOOTH permission allows our app to connect, disconnect, and transfer data with another Bluetooth device. The BLUETOOTH_ADMIN permission allows our app to discover new Bluetooth devices and change the device's Bluetooth settings.
We will use the Bluetooth adapter to interface with Bluetooth. We instantiate the adapter in the ListActivity class. If the adapter is null, this means Bluetooth is not supported by the device and the app will not work on the current device. We handle this situation by showing an alert dialog to the user and exiting the app.
@Override
protected void onCreate(Bundle savedInstanceState) {
...
BTAdapter = BluetoothAdapter.getDefaultAdapter();
// Phone does not support Bluetooth so let the user know and exit.
if (BTAdapter == null) {
new AlertDialog.Builder(this)
.setTitle("Not compatible")
.setMessage("Your phone does not support Bluetooth")
.setPositiveButton("Exit", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
System.exit(0);
}
})
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
}
}
If Bluetooth is available on the device, we need to enable it. To enable Bluetooth, we start an intent provided to us by the Android SDK, BluetoothAdapter.ACTION_REQUEST_ENABLE. This will present a dialog to the user, asking them for permission to enable Bluetooth on the device. REQUEST_BLUETOOTH is a static integer we set to identify the activity request.
public class ListActivity extends ActionBarActivity implements DeviceListFragment.OnFragmentInteractionListener {
public static int REQUEST_BLUETOOTH = 1;
...
protected void onCreate(Bundle savedInstanceState) {
...
if (!BTAdapter.isEnabled()) {
Intent enableBT = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBT, REQUEST_BLUETOOTH);
}
}
}
2. Obtaining a List of Paired Devices
In this step, we scan for paired Bluetooth devices and display them in a list. In the context of a mobile device, a Bluetooth device can either be:
unknown
paired
connected
It is important to know the difference between a paired and a connected Bluetooth device. Paired devices are aware of each other’s existence and share a link key, which can be used to authenticate, resulting in a connection. Devices are automatically paired once an encrypted connection is established.
Connected devices share an RFCOMM channel, allowing them to send and receive data. A device may have many paired devices, but it can only be connected to one device at a time.
Bluetooth devices are represented by the BluetoothDevice object. A list of paired devices can be obtained by invoking the getBondedDevices() method, which returns a set of BluetoothDevice objects. We invoke the getBondedDevices() method in the DeviceListFragment's onCreate() method.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("DEVICELIST", "Super called for DeviceListFragment onCreate\n");
deviceItemList = new ArrayList<DeviceItem>();
Set<BluetoothDevice> pairedDevices = bTAdapter.getBondedDevices();
}
We use the getName() and getAddress() methods to obtain more information about the Bluetooth devices. The getName() method returns the public identifier of the device while the getAddress() method returns the device's MAC address, an identifier uniquely identifying the device.
Now that we have a list of the paired devices, we create a DeviceItem object for each BluetoothDevice object. We then add each DeviceItem object to an array named deviceItemList. We'll use this array to display the list of paired Bluetooth devices in our app. The code for displaying the list of DeviceItem objects is already present in the starter project.
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
DeviceItem newDevice= new DeviceItem(device.getName(),device.getAddress(),"false");
deviceItemList.add(newDevice);
}
}
3. Discover Nearby Bluetooth Devices
The next step is to discover devices the device isn't paired with yet, unknown devices, and add them to the list of paired devices. We do this when the user taps the scan button. The code to handle this is located in DeviceListFragment.
We first need to make a BroadcastReceiver and override the onReceive() method. The onReceive() method is invoked whenever a a Bluetooth device is found.
The onReceive() method takes an intent as its second argument. We can check what kind of intent is broadcasting with by invoking getAction(). If the action is BluetoothDevice.ACTION_FOUND, then we know we have found a Bluetooth device. When this occurs, we create a DeviceItem object using the device's name and MAC address. Finally, we add the DeviceItem object to the ArrayAdapter to display it in our app.
public class DeviceListFragment extends Fragment implements AbsListView.OnItemClickListener{
...
private final BroadcastReceiver bReciever = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Create a new device item
DeviceItem newDevice = new DeviceItem(device.getName(), device.getAddress(), "false");
// Add it to our adapter
mAdapter.add(newDevice);
}
}
};
}
When the scan button is toggled on, we simply need to register the receiver we just made and invoke the startDiscovery() method. If the scan button is toggled off, we unregister the receiver and invoke cancelDiscovery(). Keep in mind that discovery takes up a lot of resources. If your application connects with another Bluetooth device, you should always cancel discovery prior to connecting.
We also clear the ArrayAdapter object, mAdapter, when discovery begins. When we start scanning, we don't want to include old devices that may no longer be in range of the device.
That's it. We have finished our Bluetooth scanner.
4. Connecting to a Device
Bluetooth connections work like any other connection. There is a server and a client, which communicate via RFCOMM sockets. On Android, RFCOMM sockets are represented as a BluetoothSocket object. Fortunately for us, most of the technical code for servers is handled by the Android SDK and available through the Bluetooth API.
Connecting as a client is simple. Your first obtain the RFCOMM socket from the desired BluetoothDevice by calling createRfcommSocketToServiceRecord(), passing in a UUID, a 128-bit value that you create. The UUID is similar to a port number.
For example, let's assume you are making a chat app that uses Bluetooth to chat with other nearby users. To find other users to chat with, you would want to look for other devices with your chat app installed. To do this, we would look for the UUID in the list of services of the nearby devices. Using a UUID to listen and accept Bluetooth connections automatically adds that UUID to the phone's list of services, or service discovery protocol.
Once the BluetoothSocket is created, you call connect() on the BluetoothSocket. This will initialize a connection with the BluetoothDevice through the RFCOMM socket. Once our device is connected, we can use the socket to exchange data with the connected device. Doing this is similar to any standard server implementation.
Maintaining a Bluetooth connection is costly so we need to close the socket when we no longer need it. To close the socket, we call close() on the BluetoothSocket.
The following code snippet shows how to connect with a given BluetoothDevice:
public class ConnectThread extends Thread{
private BluetoothSocket bTSocket;
public boolean connect(BluetoothDevice bTDevice, UUID mUUID) {
BluetoothSocket temp = null;
try {
temp = bTDevice.createRfcommSocketToServiceRecord(mUUID);
} catch (IOException e) {
Log.d("CONNECTTHREAD","Could not create RFCOMM socket:" + e.toString());
return false;
}
try {
bTSocket.connect();
} catch(IOException e) {
Log.d("CONNECTTHREAD","Could not connect: " + e.toString());
try {
bTSocket.close();
} catch(IOException close) {
Log.d("CONNECTTHREAD", "Could not close connection:" + e.toString());
return false;
}
}
return true;
}
public boolean cancel() {
try {
bTSocket.close();
} catch(IOException e) {
Log.d("CONNECTTHREAD","Could not close connection:" + e.toString());
return false;
}
return true;
}
}
Connecting as a server is slightly more difficult. First, from your BluetoothAdapter, you must get a BluetoothServerSocket, which will be used to listen for a connection. This is only used to obtain the connection's shared RFCOMM socket. Once the connection is established, the server socket is no longer need and can be closed by calling close() on it.
We instantiate a server socket by calling listenUsingRfcommWithServiceRecord(String name, UUID mUUID). This method takes two parameters, a name of type String and a unique identifier of type UUID. The name parameter is the name we give the service when it is added to the phone's SDP (Service Discovery Protocol) entry. The unique identifier should match the UUID the client trying to connect is using.
We then call accept() on the newly obtained BluetoothServerSocket to wait for a connection. When the accept() call returns something that isn't null, we assign it to our BluetoothSocket, which we can then use to exchange data with the connected device.
The following code snippet shows how to accept a connection as a server:
public class ServerConnectThread extends Thread{
private BluetoothSocket bTSocket;
public ServerConnectThread() { }
public void acceptConnect(BluetoothAdapter bTAdapter, UUID mUUID) {
BluetoothServerSocket temp = null;
try {
temp = bTAdapter.listenUsingRfcommWithServiceRecord("Service_Name", mUUID);
} catch(IOException e) {
Log.d("SERVERCONNECT", "Could not get a BluetoothServerSocket:" + e.toString());
}
while(true) {
try {
bTSocket = temp.accept();
} catch (IOException e) {
Log.d("SERVERCONNECT", "Could not accept an incoming connection.");
break;
}
if (bTSocket != null) {
try {
temp.close();
} catch (IOException e) {
Log.d("SERVERCONNECT", "Could not close ServerSocket:" + e.toString());
}
break;
}
}
}
public void closeConnect() {
try {
bTSocket.close();
} catch(IOException e) {
Log.d("SERVERCONNECT", "Could not close connection:" + e.toString());
}
}
}
Reading and writing to the connection is done using streams, InputStream and OutputStream. We can get a reference to these streams by calling getInputStream() and getOutputStream() on the BluetoothSocket. To read from and write to these streams, we call read() and write() respectively.
The following code snippet shows how to do this for a single integer:
public class ManageConnectThread extends Thread {
public ManageConnectThread() { }
public void sendData(BluetoothSocket socket, int data) throws IOException{
ByteArrayOutputStream output = new ByteArrayOutputStream(4);
output.write(data);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(output.toByteArray());
}
public int receiveData(BluetoothSocket socket) throws IOException{
byte[] buffer = new byte[4];
ByteArrayInputStream input = new ByteArrayInputStream(buffer);
InputStream inputStream = socket.getInputStream();
inputStream.read(buffer);
return input.read();
}
}
In my previous tutorial about Framer, you learned how to use layers, states, and events to create simple mock-ups for your iOS and Android apps. Framer has a lot more to offer though. In this tutorial, I am going to focus on a few more advanced features of the framework that allow you to add more complex interactions and effects to your mock-ups.
1. Nesting Layers
Because a layer can be embedded inside another layer, you can create complex layer hierarchies with Framer. An embedded layer is called a sublayer and the layer that it is embedded in is called its superlayer. The following code snippet shows you how to embed a layer within another layer using the superLayer property.
var layer1 = new Layer({
width:200,
height:200
});
var layer2 = new Layer({
width:100,
height:100,
superLayer: layer1 // makes this layer a child of layer1
});
You can make use of layer hierarchies to simulate multiple screens in your mock-ups. Let me show you how to do this with the help of an example. Consider a mock-up that has two screens, a login screen and a welcome screen.
The login screen contains input fields for the username and password, and a submit button. The welcome screen contains an image and a few lines of text.
To create such a mock-up, you could consider the login and welcome screens as superlayers and the user interface elements they contain as sublayers. Let’s start by creating the superlayers first.
var loginScreen = new Layer({
width: Screen.width,
height: Screen.height,
});
var welcomeScreen = new Layer({
width: Screen.width,
height: Screen.height,
});
As you can see, loginScreen and welcomeScreen are blank Layer objects for now. Let’s add some sublayers to the loginScreen layer.
// The CSS styles to be applied to the layers
var style = {
paddingTop: "25px",
color: "#999",
background: "#FFF"
};
//Create the username field
var usernameField = new Layer({
width: 500,
height: 100,
y: 100,
html: "Username",
style: style,
superLayer: loginScreen // child of loginScreen
});
usernameField.centerX();
// Create the password field
var passwordField = new Layer({
width: 500,
height: 100,
y: 220,
html: "Password",
style: style,
superLayer: loginScreen // child of loginScreen
});
passwordField.centerX();
// Create the submit button
var submitButton = new Layer({
width: Screen.width,
height: 100,
y: Screen.height - 100,
html: "LOGIN",
style: {
paddingTop: "25px",
color: "#FFFFFF",
fontWeight: "bold"
},
backgroundColor: "#2196F3",
superLayer: loginScreen // child of loginScreen
});
In the same way, let’s now add some sublayers to the welcomeScreen layer.
// Create a layer to show a profile pic
var profilePic = new Layer({
width:400,
height: 400,
borderRadius: 200,
image: 'profile.jpg',
y:100,
superLayer: welcomeScreen // child of welcomeScreen
});
profilePic.centerX();
// Create a layer for the welcome text
var text = new Layer({
width: 400,
height:100,
y:600,
html: "Welcome Jenny",
backgroundColor: "",
style: {
color: "#FFFFFF"
},
superLayer: welcomeScreen // child of welcomeScreen
});
text.centerX();
At this point, if you try to view your prototype in a browser, it will look a bit messy. Both superlayers (loginScreen and welcomeScreen) are visible at the same time.
To hide a layer, you set its visible property to false. Here’s how you keep the welcomeScreen layer hidden when the mock-up starts:
welcomeScreen.visible = false;
To change the view from the login screen to the welcome screen, you can simply hide the loginScreen layer and turn on the visibility of the welcomeScreen layer. The following code snippet shows you how to do so inside the click handler of submitButton:
Go back to the browser and refresh the page to see your mock-up making more sense now. Remember that the browser you’re using to view the mock-up should be WebKit-based, such as Chrome or Safari.
If you don’t like the instant switch, you can animate the transition by using the animate function to animate, for example, the opacity of the layers.
submitButton.on("click", function(){
welcomeScreen.opacity = 0;
welcomeScreen.visible = true; // welcomeScreen is still invisible
// because its opacity is zero
loginScreen.animate({
properties: {
opacity: 0
}
});
welcomeScreen.animate({
properties: {
opacity: 1
},
time: 2 // Duration of animation is 2 seconds
});
});
This is what the transition looks like now:
2. Using Layer Effects
Framer allows you to apply a variety of image effects, such as gaussian blur, hue rotation, brightness/contrast adjustment, color inversion, sepia tone addition, and more, to your Layer objects.
The following code snippet shows you how to use the blur effect to create a blurred background:
// Create a background layer
var bg = new BackgroundLayer({
image: "bg.jpg"
});
bg.blur = 5; // Set the gaussian blur to 5 pixels
This is what the result looks like:
The rest of the effects are used in a similar manner. For example, to reduce the contrast of the bg layer, you need to modify the contrast property:
bg.contrast = 50;
3. Creating Scrollable Layers
To make a layer scrollable, you need to embed it in the content layer of a ScrollComponent object. Creating a ScrollComponent is similar to creating a Layer object:
var scroll1 = new ScrollComponent({
width: Screen.width,
height: Screen.height,
});
The following code block shows you how to embed a Layer object within the content of scroll1:
var layer3 = new Layer({
width: 2000,
height: 2000,
image: "pattern.jpg",
superLayer: scroll1.content // Embed inside content of scroll1
});
Here’s what a ScrollComponent looks like in action:
Note that scrolling is possible only if the dimensions of the layer are larger than the dimensions of the ScrollComponent it is embedded in.
4. Displaying Paginated Data
Sometimes, instead of continuous scrolling, you might want to present your data as a set of pages. For example, you might want to create an interface where a user swipes through a set of images. This can be done using a PageComponent. The PageComponent class is derived from the ScrollComponent class and has an identical constructor.
To add a Layer to a PageComponent, you need to use the addPage function. The following code shows you how to create a PageComponent and add a few image layers to it:
// Create a PageComponent that fills up the screen
var pageComponent = new PageComponent({
width: Screen.width,
height: Screen.height
});
// Create three images layers
var image1 = new Layer({
width: Screen.width,
height: Screen.height,
image: "pink.jpg"
});
var image2 = new Layer({
width: Screen.width,
height: Screen.height,
image: "grey.jpg"
});
var image3 = new Layer({
width: Screen.width,
height: Screen.height,
image: "blue.jpg"
});
// Add all the image layers to pageComponent
pageComponent.addPage(image1);
pageComponent.addPage(image2);
pageComponent.addPage(image3);
The result looks like this:
5. Managing Animations
You already know that you can use the animate function to animate the properties of a Layer object. However, calling animate starts the animation immediately. If you want to have control over when an animation starts or stops, you can use Animation objects instead.
Here’s how you create an Animation object:
var animation1 = new Animation({
layer: mylayer, // name of the layer that should
// be animated
// Properties that should change
properties: {
x: 400
}
});
You can then use the intuitively named start, stop, and reverse functions to manage the animation. For example, to start the animation invoke the start function:
animation1.start();
You can also add event handlers to Animation objects, using the on function. This feature can be used to create chained animations. For example, here’s a code snippet that reverses animation1 when the animation ends:
animation1.on(Events.AnimationEnd, function(){
// Create a reversed copy of animation1 and play it
animation1.reverse().start();
});
The animation would look like this:
6. Changing Device Orientation
All the examples I showed you till now rendered the mobile device in portrait orientation. If you want to use the landscape orientation, you need to assign the value 90 to the orientation property of the DeviceComponent.
device.orientation = 90;
In the browser, the device will now look like this:
To animate the change in orientation, you use the setOrientation method, passing in true as its second parameter.
device.setOrientation(90, true);
Conclusion
In this tutorial, you learned how to make use of Framer’s more advanced features, such as nested or embedded layers, layer effects, scrolling containers, and animation events. With a little bit of creativity and effort, you can now create stunning prototypes using the Framer framework. Here are some beautiful mock-ups to inspire you.
Refer to Framer’s Documentation to learn more about this powerful prototyping framework.
In my previous tutorial about Framer, you learned how to use layers, states, and events to create simple mock-ups for your iOS and Android apps. Framer has a lot more to offer though. In this tutorial, I am going to focus on a few more advanced features of the framework that allow you to add more complex interactions and effects to your mock-ups.
1. Nesting Layers
Because a layer can be embedded inside another layer, you can create complex layer hierarchies with Framer. An embedded layer is called a sublayer and the layer that it is embedded in is called its superlayer. The following code snippet shows you how to embed a layer within another layer using the superLayer property.
var layer1 = new Layer({
width:200,
height:200
});
var layer2 = new Layer({
width:100,
height:100,
superLayer: layer1 // makes this layer a child of layer1
});
You can make use of layer hierarchies to simulate multiple screens in your mock-ups. Let me show you how to do this with the help of an example. Consider a mock-up that has two screens, a login screen and a welcome screen.
The login screen contains input fields for the username and password, and a submit button. The welcome screen contains an image and a few lines of text.
To create such a mock-up, you could consider the login and welcome screens as superlayers and the user interface elements they contain as sublayers. Let’s start by creating the superlayers first.
var loginScreen = new Layer({
width: Screen.width,
height: Screen.height,
});
var welcomeScreen = new Layer({
width: Screen.width,
height: Screen.height,
});
As you can see, loginScreen and welcomeScreen are blank Layer objects for now. Let’s add some sublayers to the loginScreen layer.
// The CSS styles to be applied to the layers
var style = {
paddingTop: "25px",
color: "#999",
background: "#FFF"
};
//Create the username field
var usernameField = new Layer({
width: 500,
height: 100,
y: 100,
html: "Username",
style: style,
superLayer: loginScreen // child of loginScreen
});
usernameField.centerX();
// Create the password field
var passwordField = new Layer({
width: 500,
height: 100,
y: 220,
html: "Password",
style: style,
superLayer: loginScreen // child of loginScreen
});
passwordField.centerX();
// Create the submit button
var submitButton = new Layer({
width: Screen.width,
height: 100,
y: Screen.height - 100,
html: "LOGIN",
style: {
paddingTop: "25px",
color: "#FFFFFF",
fontWeight: "bold"
},
backgroundColor: "#2196F3",
superLayer: loginScreen // child of loginScreen
});
In the same way, let’s now add some sublayers to the welcomeScreen layer.
// Create a layer to show a profile pic
var profilePic = new Layer({
width:400,
height: 400,
borderRadius: 200,
image: 'profile.jpg',
y:100,
superLayer: welcomeScreen // child of welcomeScreen
});
profilePic.centerX();
// Create a layer for the welcome text
var text = new Layer({
width: 400,
height:100,
y:600,
html: "Welcome Jenny",
backgroundColor: "",
style: {
color: "#FFFFFF"
},
superLayer: welcomeScreen // child of welcomeScreen
});
text.centerX();
At this point, if you try to view your prototype in a browser, it will look a bit messy. Both superlayers (loginScreen and welcomeScreen) are visible at the same time.
To hide a layer, you set its visible property to false. Here’s how you keep the welcomeScreen layer hidden when the mock-up starts:
welcomeScreen.visible = false;
To change the view from the login screen to the welcome screen, you can simply hide the loginScreen layer and turn on the visibility of the welcomeScreen layer. The following code snippet shows you how to do so inside the click handler of submitButton:
Go back to the browser and refresh the page to see your mock-up making more sense now. Remember that the browser you’re using to view the mock-up should be WebKit-based, such as Chrome or Safari.
If you don’t like the instant switch, you can animate the transition by using the animate function to animate, for example, the opacity of the layers.
submitButton.on("click", function(){
welcomeScreen.opacity = 0;
welcomeScreen.visible = true; // welcomeScreen is still invisible
// because its opacity is zero
loginScreen.animate({
properties: {
opacity: 0
}
});
welcomeScreen.animate({
properties: {
opacity: 1
},
time: 2 // Duration of animation is 2 seconds
});
});
This is what the transition looks like now:
2. Using Layer Effects
Framer allows you to apply a variety of image effects, such as gaussian blur, hue rotation, brightness/contrast adjustment, color inversion, sepia tone addition, and more, to your Layer objects.
The following code snippet shows you how to use the blur effect to create a blurred background:
// Create a background layer
var bg = new BackgroundLayer({
image: "bg.jpg"
});
bg.blur = 5; // Set the gaussian blur to 5 pixels
This is what the result looks like:
The rest of the effects are used in a similar manner. For example, to reduce the contrast of the bg layer, you need to modify the contrast property:
bg.contrast = 50;
3. Creating Scrollable Layers
To make a layer scrollable, you need to embed it in the content layer of a ScrollComponent object. Creating a ScrollComponent is similar to creating a Layer object:
var scroll1 = new ScrollComponent({
width: Screen.width,
height: Screen.height,
});
The following code block shows you how to embed a Layer object within the content of scroll1:
var layer3 = new Layer({
width: 2000,
height: 2000,
image: "pattern.jpg",
superLayer: scroll1.content // Embed inside content of scroll1
});
Here’s what a ScrollComponent looks like in action:
Note that scrolling is possible only if the dimensions of the layer are larger than the dimensions of the ScrollComponent it is embedded in.
4. Displaying Paginated Data
Sometimes, instead of continuous scrolling, you might want to present your data as a set of pages. For example, you might want to create an interface where a user swipes through a set of images. This can be done using a PageComponent. The PageComponent class is derived from the ScrollComponent class and has an identical constructor.
To add a Layer to a PageComponent, you need to use the addPage function. The following code shows you how to create a PageComponent and add a few image layers to it:
// Create a PageComponent that fills up the screen
var pageComponent = new PageComponent({
width: Screen.width,
height: Screen.height
});
// Create three images layers
var image1 = new Layer({
width: Screen.width,
height: Screen.height,
image: "pink.jpg"
});
var image2 = new Layer({
width: Screen.width,
height: Screen.height,
image: "grey.jpg"
});
var image3 = new Layer({
width: Screen.width,
height: Screen.height,
image: "blue.jpg"
});
// Add all the image layers to pageComponent
pageComponent.addPage(image1);
pageComponent.addPage(image2);
pageComponent.addPage(image3);
The result looks like this:
5. Managing Animations
You already know that you can use the animate function to animate the properties of a Layer object. However, calling animate starts the animation immediately. If you want to have control over when an animation starts or stops, you can use Animation objects instead.
Here’s how you create an Animation object:
var animation1 = new Animation({
layer: mylayer, // name of the layer that should
// be animated
// Properties that should change
properties: {
x: 400
}
});
You can then use the intuitively named start, stop, and reverse functions to manage the animation. For example, to start the animation invoke the start function:
animation1.start();
You can also add event handlers to Animation objects, using the on function. This feature can be used to create chained animations. For example, here’s a code snippet that reverses animation1 when the animation ends:
animation1.on(Events.AnimationEnd, function(){
// Create a reversed copy of animation1 and play it
animation1.reverse().start();
});
The animation would look like this:
6. Changing Device Orientation
All the examples I showed you till now rendered the mobile device in portrait orientation. If you want to use the landscape orientation, you need to assign the value 90 to the orientation property of the DeviceComponent.
device.orientation = 90;
In the browser, the device will now look like this:
To animate the change in orientation, you use the setOrientation method, passing in true as its second parameter.
device.setOrientation(90, true);
Conclusion
In this tutorial, you learned how to make use of Framer’s more advanced features, such as nested or embedded layers, layer effects, scrolling containers, and animation events. With a little bit of creativity and effort, you can now create stunning prototypes using the Framer framework. Here are some beautiful mock-ups to inspire you.
Refer to Framer’s Documentation to learn more about this powerful prototyping framework.
The importance of and attention for security on the web has increased substantially over the past few years. During this year's WWDC, Apple has made it clear that it plans to lead by example by improving security of its operating systems through a new feature, App Transport Security.
Of course, the security of a platform is only as strong as the security of its components and that includes third party applications. In other words, Apple expects developers to adopt App Transport Security in their applications.
In this article, I will explain what App Transport Securityentails, how it will affect your applications, and how you can update your applications to stick to Apple's guidelines and recommendations.
What Is App Transport Security?
App Transport Security, or ATS for short, is a new feature of iOS 9 and OS X El Capitan. While Apple didn't mention watchOS, we can assume App Transport Security also applies to watchOS 2. App Transport Security aims to improve the security of Apple's operating systems and any applications running on these operating systems.
Network requests that are made over HTTP transmit data as cleartext. It goes without saying that this poses a significant security risk. Apple stresses that every developer should strive to keep the data of their customers safe and secure, even if that data doesn't seem important or sensitive.
App Transport Security actively encourages security by imposing a number of security best practices, the most important being the requirement that network requests need to be sent over a secure connection. With App Transport Security enabled, network requests are automatically made over HTTPS instead of HTTP.
There are a number of other requirements to further improve security. For example, App Transport Security requires TLS (Transport Layer Security) 1.2 or higher. While you may be unfamiliar with TLS, I'm sure you've heard of SSL (Secure Sockets Layer). TLS is the successor of SSL and is a collection of cryptographic protocols to enforce security over network connections.
Apple recently published a public, prerelease technote about App Transport Security to give developers the opportunity to plan for App Transport Security. The document outlines what App Transport Security expects from your applications and the web services it interacts with.
Exceptions
Wait a second. My application uses a CDN (Content Delivery Network) that I don't have control over and it doesn't support HTTPS. Don't worry. Apple has your back covered. With regards to App Transport Security, an application falls into one of four categories. Let's go over each category to see how it impacts an application.
HTTPS Only
If your application only interfaces with servers that support HTTPS, then you're in luck. You're application won't have to make any changes. However, note that App Transport Security requires TLS 1.2 and it expects the domain to use ciphers that support forward secrecy. The certificate also needs to meet the requirements imposed by ATS. It's therefore important to double-check that the servers your application communicates with comply with the requirements of ATS.
Mix & Match
It is possible that your application talks to servers that don't meet the ATS requirements. In that case, you need to tell the operating system which domains are involved and specify in your application's Info.plist what requirements aren't met.
This means that App Transport Security is enforced for every endpoint your application talks to with the exception of the ones specified in your application's Info.plist. You can configure the exceptions using a number of predefined keys. In the following Info.plist, we define three exceptions.
The first exception we define tells ATS that communication with this subdomain overrides the requirement to use HTTPS. Note that this exception only applies to the subdomain specified in the exception. It's important to understand that the NSExceptionAllowsInsecureHTTPLoads key doesn't only relate to the use of HTTPS. The exception specifies that, for that domain, every requirement of App Transport Security is overridden.
cdn.domain.com
It's possible that your application talks to a server that serves its data over HTTPS, but isn't using TLS 1.2 or higher. In that case, you define an exception that specifies the minimum TLS version that should be used. This is a better and safer option than completely overriding App Transport Security for that particular domain.
thatotherdomain.com
The NSIncludesSubdomains key tells App Transport Security that the exception applies to every subdomain of the specified domain. The exception further defines that the domain can use ciphers that don't support forward secrecy (NSExceptionRequiresForwardSecrecy) by expanding the list of accepted ciphers. For more information about forward secrecy, I recommend reading Apple's technote on the topic.
Opt Out
If you're building a web browser, then you have a slightly bigger problem. Because you don't know which web pages your users are going to visit, you cannot possibly tell whether those web pages are served over HTTPS and meet the ATS requirements. In that case, there is no other option but to opt out of App Transport Security altogether.
It's important that you explicitly opt out of App Transport Security. Remember that App Transport Security is enforced by default. In your application's Info.plist, you add a dictionary for the key NSAppTransportSecurity. The dictionary should include one key, NSAllowsArbitraryLoads, and its value should be set to YES. This is what your application's Info.plist file should look like if you opt out of App Transport Security.
There is a fourth option in which your application opts out of App Transport Security, but defines a number of exceptions. This is useful if your application fetches data from a range of servers you don't control, but also talks to an API you maintain. In that case, you specify in your application's Info.plist that arbitrary loads are allowed, but you also define one or more exceptions for which App Transport Security is enabled. This is what the Info.plist could look like.
Apple has emphasized that applications automatically opt in to App Transport Security if they are built against iOS 9 or OS X El Capitan. This means that you won't have to make any changes to your applications as long as you build them against iOS 8 or OS X Yosemite.
Based on previous releases of iOS and OS X, however, we have learned that Apple requires developers to build their applications against the latest SDK fairly soon after their official release. In other words, even though you won't have to comply with App Transport Security when iOS 9 and OS X El Capitan are released later this year, it is very likely that Apple will require developers to build against the latest SDK in the first or second quarter of 2016. I therefore recommend that you investigate how App Transport Security will impact your applications sooner rather than later.
Conclusion
I hope this article has made it clear that App Transport Security is not something your applications can adopt some day. It's similar to Apple's requirement for 64-bit support not too long ago. Unless your applications only talk to servers over HTTPS that comply with the ATS requirements, you need to invest some time to investigate how App Transport Security will impact your applications. Apple's technote about App Transport Security can help you with this.
The importance of and attention for security on the web has increased substantially over the past few years. During this year's WWDC, Apple has made it clear that it plans to lead by example by improving security of its operating systems through a new feature, App Transport Security.
Of course, the security of a platform is only as strong as the security of its components and that includes third party applications. In other words, Apple expects developers to adopt App Transport Security in their applications.
In this article, I will explain what App Transport Securityentails, how it will affect your applications, and how you can update your applications to stick to Apple's guidelines and recommendations.
What Is App Transport Security?
App Transport Security, or ATS for short, is a new feature of iOS 9 and OS X El Capitan. While Apple didn't mention watchOS, we can assume App Transport Security also applies to watchOS 2. App Transport Security aims to improve the security of Apple's operating systems and any applications running on these operating systems.
Network requests that are made over HTTP transmit data as cleartext. It goes without saying that this poses a significant security risk. Apple stresses that every developer should strive to keep the data of their customers safe and secure, even if that data doesn't seem important or sensitive.
App Transport Security actively encourages security by imposing a number of security best practices, the most important being the requirement that network requests need to be sent over a secure connection. With App Transport Security enabled, network requests are automatically made over HTTPS instead of HTTP.
There are a number of other requirements to further improve security. For example, App Transport Security requires TLS (Transport Layer Security) 1.2 or higher. While you may be unfamiliar with TLS, I'm sure you've heard of SSL (Secure Sockets Layer). TLS is the successor of SSL and is a collection of cryptographic protocols to enforce security over network connections.
Apple recently published a public, prerelease technote about App Transport Security to give developers the opportunity to plan for App Transport Security. The document outlines what App Transport Security expects from your applications and the web services it interacts with.
Exceptions
Wait a second. My application uses a CDN (Content Delivery Network) that I don't have control over and it doesn't support HTTPS. Don't worry. Apple has your back covered. With regards to App Transport Security, an application falls into one of four categories. Let's go over each category to see how it impacts an application.
HTTPS Only
If your application only interfaces with servers that support HTTPS, then you're in luck. You're application won't have to make any changes. However, note that App Transport Security requires TLS 1.2 and it expects the domain to use ciphers that support forward secrecy. The certificate also needs to meet the requirements imposed by ATS. It's therefore important to double-check that the servers your application communicates with comply with the requirements of ATS.
Mix & Match
It is possible that your application talks to servers that don't meet the ATS requirements. In that case, you need to tell the operating system which domains are involved and specify in your application's Info.plist what requirements aren't met.
This means that App Transport Security is enforced for every endpoint your application talks to with the exception of the ones specified in your application's Info.plist. You can configure the exceptions using a number of predefined keys. In the following Info.plist, we define three exceptions.
The first exception we define tells ATS that communication with this subdomain overrides the requirement to use HTTPS. Note that this exception only applies to the subdomain specified in the exception. It's important to understand that the NSExceptionAllowsInsecureHTTPLoads key doesn't only relate to the use of HTTPS. The exception specifies that, for that domain, every requirement of App Transport Security is overridden.
cdn.domain.com
It's possible that your application talks to a server that serves its data over HTTPS, but isn't using TLS 1.2 or higher. In that case, you define an exception that specifies the minimum TLS version that should be used. This is a better and safer option than completely overriding App Transport Security for that particular domain.
thatotherdomain.com
The NSIncludesSubdomains key tells App Transport Security that the exception applies to every subdomain of the specified domain. The exception further defines that the domain can use ciphers that don't support forward secrecy (NSExceptionRequiresForwardSecrecy) by expanding the list of accepted ciphers. For more information about forward secrecy, I recommend reading Apple's technote on the topic.
Opt Out
If you're building a web browser, then you have a slightly bigger problem. Because you don't know which web pages your users are going to visit, you cannot possibly tell whether those web pages are served over HTTPS and meet the ATS requirements. In that case, there is no other option but to opt out of App Transport Security altogether.
It's important that you explicitly opt out of App Transport Security. Remember that App Transport Security is enforced by default. In your application's Info.plist, you add a dictionary for the key NSAppTransportSecurity. The dictionary should include one key, NSAllowsArbitraryLoads, and its value should be set to YES. This is what your application's Info.plist file should look like if you opt out of App Transport Security.
There is a fourth option in which your application opts out of App Transport Security, but defines a number of exceptions. This is useful if your application fetches data from a range of servers you don't control, but also talks to an API you maintain. In that case, you specify in your application's Info.plist that arbitrary loads are allowed, but you also define one or more exceptions for which App Transport Security is enabled. This is what the Info.plist could look like.
Apple has emphasized that applications automatically opt in to App Transport Security if they are built against iOS 9 or OS X El Capitan. This means that you won't have to make any changes to your applications as long as you build them against iOS 8 or OS X Yosemite.
Based on previous releases of iOS and OS X, however, we have learned that Apple requires developers to build their applications against the latest SDK fairly soon after their official release. In other words, even though you won't have to comply with App Transport Security when iOS 9 and OS X El Capitan are released later this year, it is very likely that Apple will require developers to build against the latest SDK in the first or second quarter of 2016. I therefore recommend that you investigate how App Transport Security will impact your applications sooner rather than later.
Conclusion
I hope this article has made it clear that App Transport Security is not something your applications can adopt some day. It's similar to Apple's requirement for 64-bit support not too long ago. Unless your applications only talk to servers over HTTPS that comply with the ATS requirements, you need to invest some time to investigate how App Transport Security will impact your applications. Apple's technote about App Transport Security can help you with this.
During Google I/O 2015, Google introduced the Design Support Library for Android developers. This library makes it simple for developers to implement more Material Design concepts into their applications, because many key elements were not available initially out of the box. On top of being easy to use, the Design Support Library is backwards compatible to API 7. The Design Support Library can be included in your Android projects by importing the Gradle dependency.
compile 'com.android.support:design:22.2.0'
1. Visual Components
There are two major categories of tools in the Design Support Library:
visual components
motion components
We'll start by taking a look at what new visual components are available to add polish to your apps.
Material Text Input
EditText views have been around in Android since the very beginning, and while they're simple to use, they haven't really changed much. With the Design Support Library, Google has introduced a new container view called TextInputLayout. This new view adds functionality to the standard EditText, such as support for error messages and animated hints to help make your user interface pop.
As shown in the snippet below, TextInputLayout can be included in your layout file as a wrapper for a standard EditText.
<android.support.design.widget.TextInputLayout
android:id="@+id/textinput"
android:layout_width="match_parent"
android:layout_height="wrap_content"><EditText
android:id="@+id/edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="What is your name?" /></android.support.design.widget.TextInputLayout>
Gianluca Segato will take a closer look at the TextInputLayout component in an upcoming tutorial.
Floating Action Buttons
One of the most predominate user interface components in Material Design apps is the floating action button. Since their introduction, developers have either had to create these buttons from scratch or choose one of the many third party libraries designed specifically around these buttons.
With the Design Support Library, floating action buttons can be included in a layout and anchored to a portion of the screen with a few simple lines of XML. Each button is easily customizable with icons and colors. Two sizes are available, standard (56dp) and mini (40dp). One of the biggest advantages is that these buttons will now be supported by Google as their design evolves.
While ViewPager and DrawerLayout components have been available for a while through the v4 support library, Google has expanded on them by providing two new related widgets. The first is an official version of the commonly used ViewPagerIndicator library by Jake Wharton called TabLayout. The second is the NavigationView, which provides support for drawer header views.
TabLayout
TabLayout components can have content added to them manually in code through using one of the addTab methods. Take a look at the following example.
Alternatively, you can associate TabLayout components with a ViewPager. This is accomplished by calling setupWithViewPager(), passing in the ViewPager as the first and only argument. This is another way to change sections in the ViewPager. It should be noted that getPageTitle() needs to be overridden when using TabLayout with a ViewPager to give each tab a proper name.
NavigationView
NavigationView is a new widget that extends the functionality of the DrawerLayout. Developers can now add header layouts to the drawer and mark selected sections with this easy to use component.
In addition to this, it's now very straightforward to create sections and subsections in the drawer through menu resource files. To get started, a NavigationView simply needs to be associated with a DrawerLayout in XML.
While the Toast message has been a staple of Android for years, a new user interface widget called Snackbar is available to provide similar functionality with an improved look. Not only does the Snackbar present information to the user for a short period of time, it also supports a single action for adding context based functionality to your apps and can be dismissed with a swiping gesture.
Snackbar is implemented similarly to Toast, though it should be noted that creating one requires a view that can be used to find the bottom of the app display.
How a user interface behaves and animates is very important in Material Design. To facilitate this, Google has released multiple components in the Design Support Library that help with common use cases. Kerry Perez-Huanca will take a closer look at this aspect of the Design Support Library in an upcoming tutorial.
Reactive Views
You may have noticed in the previous example that the FloatingActionButton moved up when the Snackbar view appeared. This is done using a new widget called the CoordinatorLayout, which wraps views that should be shifted to make room for other views.
Improved Quick Return and Toolbars
Many developers have asked for an easier way to display a parallax image that works with a quick return design pattern, disappearing or reappearing as the user scrolls. You can see this behavior in the Play Store for app listings. To let developers implement this without a lot of time spent writing redundant code, Google has released CollapsingToolBarLayout and AppBarLayout views. Using various options within these widgets, developers can pin views to the top of the screen or specify when those views should become visible as the user scrolls.
Conclusion
The Design Support Library has brought a lot of long awaited tools to Android. When paired with the AppCompat library, it becomes a lot easier to add Material Design to apps while maintaining backwards compatibility.
Many examples of how to work with these new components can be found in Google's official reference app, CheeseSquare, and Tuts+ will continue to provide in-depth tutorials on how to implement these new features.
tag:code.tutsplus.com,2005:PostPresenter/cms-24262What You'll Be Creating
In previous tutorials, we learned about application resources, the MAT (Multilingual App Toolkit), and how to test a localized app. In this tutorial, we will not only sum it all up in an easier way, but also create a localized application bar that you can use anywhere in your application.
Developers often have to use the same application bar on several pages in their project and they usually end up copying and pasting the XAML and C# code, which isn't recommended.
In this tutorial, you will learn how to use app resources and how to add localization and globalization to your apps. You will also learn to use the App.xaml and App.xaml.cs files to build a localized application bar that you can use anywhere in your Windows Phone app.
1. Why Build a Localized App?
Windows Phone users are not all native English speakers. In fact, only 34% of them speak English. This means that 66% speak a language other than English and that is why it's important to build localized apps.
One effective strategy to increase app downloads is by making your app more suitable for Windows Phone customers that don't speak English. The more languages your app supports, the more downloads it will get and the better the ratings of your app will be. By supporting French, German, Arabic, and Spanish, you support about 75% of the Windows Phone market.
In this tutorial, we will be translating all of our app resources into French and Arabic. Here are some quick tips to keep in mind before we start:
Make sure to name all the resources you'll be using with meaningfulnames, because we will be referring to string resources by their name,not their value. Try to give each resource a uniquename that makes sense.
Try to gather all of your string resources in AppResources.resx, including button titles and error messages.
Enable multiline support and text wrap in controls.
Always save your work and make sure to rebuild your project often to implement the changes you make to AppResources.resx.
2. Culture & Language Support
Currencies, numbers, date, time, and region formats differ from culture to culture. Fortunately, the CultureInfo class takes care of these details for each language. Actually, you can even retrieve the current cultural data of the phone and show its information in a MessageBox using a single line of code:
MessageBox.Show(CultureInfo.CurrentCulture.Name);
However, the InitializeLanguage() function in App.xaml.cs does the work automatically each time the app is launched and sets RootFrame.Language based on the value of the AppResources.ResourceLanguage resource. Note that if your app doesn't support any language other than en-US (English, United States), the app uses the neutral and default AppResources.resx file of the project.
3. Localizing Your App
Step 1: Adding Resources and Binding Text Elements
In this tutorial, we will build a one-page app that will show the user some wise, old sayings. To start, in MainPage.xaml, we'll add a few TextBlock elements without specifying their content by adding the following two lines of code in the parent ContentPanelgrid:
We also make the following changes in the default AppResources.resx file:
changing the ApplicationTitle string value
adding two strings as shown in the following screenshot
Let's now go back to MainPage.xaml. After adding the strings we'll need in our app, we set the Text property of the TextBlock control to specify the contentof each TextBlock.
Instead of showing "MY APPLICATION" as the title, we use the string "My Localized App". We can accomplish this by using a binding that makes the reference to the app resources, binding the value of the string. To make this reference, we won't be using the string value itself but its attribute(name), ApplicationTitle.
Each of the two TextBlock elements will have a saying as well. We'll just use the same line of code, replacing the ApplicationTitle attribute with our saying's attribute. This is what your code should look like:
We now only need to add other languages and translate.
Step 2: Adding Other Languages
To add another language, go to the project's properties by right-clicking the project in the Solution Explorer window. Make sure to access the project's properties, not the properties of the solution.
In the Application Tab you should see the Supported Languages section. Select the language that you want to support. As mentioned earlier in this tutorial, we will add French and Arabic. As you can see in the list, you can even select the culture depending on the region, such as French (France), French (Belgium). For this tutorial, we'll just choose the general one, ignoring dialect and region.
Once you save the changes, you'll notice that Visual Studio has automatically generated two new .resx files:
AppResources.ar.resx for Arabic
AppResources.fr.resx for French
Note that the newly generated files have the same content as AppResources.resx. You shouldn't change the attributes (names). You only need to translate the values.
3. Using the Multilingual App Toolkit
The Multilingual App Toolkit is very helpful for translating string resources. It is integrated into Visual Studio, providing support for building localized Windows and Windows Phone apps, and helping with translating app resources. The Multilingual App Toolkit makes adding other languages easier and you can import and export translation files easily.
You can download the Multilingual App Toolkit as a Visual Studio extension from Microsoft's developer website. After installing the toolkit, select Enable Multilingual App Toolkit from the Tools menu.
After enabling the MAT, Visual Studio generates new .xlf files for each of the supported languages you added earlier. This means that you can generate machine translations by right-clicking an .xlf file and choosing Generate machine translations. You can also modify the translated string resources in the target tag in all the .xlf files.
4. How to Test a Localized App?
You can test a localized app using the emulator.
Debug your project and go to Emulator Settings.
Navigate to the Language tab and add a new language. It's important to not restart your phone.
Navigate to the Region tab and choose your region.
Restart the phone.
When the phone restarts, Visual Studio may throw an error, losing the connection with the emulator. After restarting the phone, debug the project again. This is what the app looks like for Arabic:
Now that we're done with string resources, we'll add a localized application bar to our app.
5. Creating a Localized Application Bar
Step 1: Creating the Application Bar
In this step, we'll create an application bar that we can use anywhere in our app. To do so, we'll make use of the App.xaml file, in which we define global XAML styles and resources that will be used across the application.
In the ApplicationResources tag in App.xaml, we add an application bar with only an icon and a menu item. Don't forget to give a nameto the application bar using the x:key attribute so that we can reference it later.
In the RateReview_Click event handler, we use one of the phone tasks to navigate users to the store if they want to leave a review or rate the app. As for the Help_Click event handler, we just add some C# code to navigate between the different pages. Note that I added a new XAML page, AboutTheApp.xaml, in which we show information about our app.
In App.xamls.cs, add the following statement so that we can benefit from the phone tasks class:
using Microsoft.Phone.Shell;
When the user taps the rate and review menu item, the app will open the store on a page where the user can rate and/or review the app. We use a MarketPlaceReviewTask like this:
MarketplaceReviewTask review = new MarketplaceReviewTask();
review.Show();
As for the Help_Click event handler, the following code snippet takes care of navigating between the two pages:
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/AboutTheApp.xaml", UriKind.RelativeOrAbsolute));
Step 2: Referencing the Application Bar
After creating the application bar that we want to use, we add a new instance of the application bar using the below code snippet in MainPage.xaml.cs and then we make it refer to the one in App.xaml.
InitializeComponent();
ApplicationBar = new ApplicationBar();
ApplicationBar = ((ApplicationBar)Application.Current.Resources["myAppBar"]);
We haven't used the AppResources at all for building the application bar, which means that text properties are already set independently from the phone's culture. Actually, we can't really use bindings when dealing with application bars.
This is why we'll reference the application bar we created earlier in App.xaml.cs and change the strings value, using a simple block of a code just after the InitializeComponent() method. The strings used are also added to AppResources.resx.
// Standard XAML initialization
InitializeComponent();
var appBar = App.Current.Resources["myAppBar"] as ApplicationBar;
((ApplicationBarIconButton)appBar.Buttons[0]).Text = AppResources.AboutAppBarIconText;
((ApplicationBarMenuItem)appBar.MenuItems[0]).Text = AppResources.RateAppBarMenuItemText;
After adding the string resources, translate them and rebuildthe project. Finally, add the information of the AboutTheApp.xaml page. This is what the result looks like:
Conclusion
In this tutorial, we learned about phone culture, how to use app resources, how to build a localized application and application bar, and how to build an app bar that we can reuse anywhere in our application. We also learned how to make a reference to a string value in the AppResources files using bindings.
And finally, we got to know the way we use phone tasks to help users rate and review the app in the store. Feel free to download the sample project and ask any question that crosses your mind in the comments below.
In the early days of iOS, tools to facilitate testing were not a major focus of Apple's Developer Tools team. This has gradually changed over the years with the introduction of the XCTest framework and, more recently, support for asynchronous and performance testing.
With Xcode 7 and Swift 2, Apple is taking another big leap to improve testing in Xcode. In this tutorial, I'm going to walk you through three significant additions that will make testing easier and more enjoyable.
1. Access Control
In Swift 1, developers have to jump through a number of hoops to test their code. For example, a unit test target only has access to public entities of another module. This is not surprising if you're familiar with Swift's access control. Testing internal routines, however, becomes a pain.
Apple's Swift team was aware of this problem and, as a result, Swift 2 introduces the @testable attribute to make testing less painful. With the @testable attribute, the unit test target has access to every internal entity of another module. The following code snippet shows how to use the @testable attribute.
@testable import AnotherModule
Let's create a simple project to see how it works. Open Xcode 7, create a project based on the Single View Application template, and tick the checkbox labeled Include Unit Tests.
To show how the @testable attribute works, I've implemented a structure named User. It has two stored properties, firstName and lastName, and a private computed property, fullName.
import Foundation
struct User {
var lastName: String = ""
var firstName: String = ""
var fullName: String {
get {
return firstName + " " + lastName
}
}
}
Because we didn't specify an access level for the fullName computed property, it's access level is internal. If we want to unit test fullName, we could mark it as public. This isn't great and it defeats the purpose of Swift's access control.
Fortunately, Swift 2 solves this issue with the @testable attribute. The following code snippet shows how the @testable attribute is used. By prefixing the import statement with the @testable attribute, we can access the fullName property of the User structure in the testFullName method.
import XCTest
@testable import Testing
class TestingTests: XCTestCase {
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
func testFullName() {
var me = User()
me.firstName = "Bart"
me.lastName = "Jacobs"
XCTAssertEqual(me.fullName, "Bart Jacobs", "The full name should be equal to \"Bart Jacobs\".")
}
}
Note that the documentation clearly states that the @testable attribute can only do its job if the product is compiled with testing enabled. In other words, you can't and shouldn't use the @testable attribute for purposes other than testing.
2. Code Coverage
If you're testing the code you write, then you're on the right track. However, it's equally important to understand how well the tests you write cover the code you think they cover. While previous versions of Xcode have had support for code coverage to some extent, it has always been a bit finicky to get it to work. That's no longer the case with Xcode 7.
Instead of showing you a bunch of screenshots, I'd like to walk you through the steps involved to enable code coverage in an Xcode project. Let's take the project we created a few minutes ago as an example.
To enable code coverage, we only need to edit the active scheme. Click the scheme in the top left and choose Edit Scheme....
To enable code coverage, select Test from the left and tick the checkbox labeled Gather Code Coverage. That's it. If you've tried to enable code coverage in earlier versions of Xcode, then I'm sure you're happy to see how easy it is to enable code coverage in Xcode 7.
Run the unit tests by pressing Command-U or selecting Test from Xcode's Product menu. When the tests have finished running, open the Report Navigator on the right and select the Coverage tab at the top. This shows you how well your unit tests cover the code you've written.
It looks like we've done a pretty good job unit testing the User structure. It gets better though. Do you see the tiny arrow following the method name? If you click it, Xcode will take you to the source code the unit test tested. Because Xcode has collected coverage data, it can also show that coverage data in the source code editor.
The coverage data is shown on the right of the source code editor. The number in the gutter on the right indicates how often the unit tests have triggered that specific routine.
Code that wasn't executed during the tests are highlighted. This is very useful for writing better tests and for improving the code coverage of your test suites.
3. UI Testing
One of the most anticipated features of Xcode 7 is the integration of user interface testing. In the past, I've worked with KIF and UI Automation, but I've never been quite happy with either solution. Xcode's UI testing looks like the solution many of us have been waiting for.
XCTest Framework
User interface testing in Xcode 7 expands on Apple's robust XCTest framework through the addition of three classes, XCUIApplication, XCUIElement, and XCUIElementQuery. To make the creation of user interface tests easier, Xcode 7 provides the ability to record tests. This is a very powerful feature that some of you may already be familiar with if you've ever worked with Apple's UI Automation.
As with UI Automation, Xcode's user interface testing relies heavily on accessibility. It's therefore more important than ever to spend time making your applications more accessible to users with a disability.
Note that UI testing is only available on iOS 9 and OS X 10.11. Apple emphasizes that unit tests and user interace tests complement each other. In other words, it's not a matter of choosing one or the other.
While it's easy to create user interace tests for an application, you are not expected to write UI tests for every possible scenario or use case. Start by focusing on common and critical use cases, such as user onboarding.
Test Reports
Test reports have also improved substantially. What I like most about the new tests reports is the addition of screenshots for failed UI tests. In addition to an error message, Xcode takes a snapshot of your application's user interface when a UI test fails. This makes it very easy to find and fix the problem.
Learn More in Our Testing With Swift Course
If you want to learn more about testing with Swift and the XCTest framework, then I encourage you to take a look at Derek Jensen's course about unit testing with Swift and XCTest.
Conclusion
Apple has put a lot of effort into improving testing and debugging with the release of Xcode 7. While integrated code coverage is fantastic, the addition of user interface testing is going to have an even bigger impact.
I hope this brief overview has wet your appetite for testing in Xcode 7. We will be covering these new features in more detail in future tutorials.
In the early days of iOS, tools to facilitate testing were not a major focus of Apple's Developer Tools team. This has gradually changed over the years with the introduction of the XCTest framework and, more recently, support for asynchronous and performance testing.
With Xcode 7 and Swift 2, Apple is taking another big leap to improve testing in Xcode. In this tutorial, I'm going to walk you through three significant additions that will make testing easier and more enjoyable.
1. Access Control
In Swift 1, developers have to jump through a number of hoops to test their code. For example, a unit test target only has access to public entities of another module. This is not surprising if you're familiar with Swift's access control. Testing internal routines, however, becomes a pain.
Apple's Swift team was aware of this problem and, as a result, Swift 2 introduces the @testable attribute to make testing less painful. With the @testable attribute, the unit test target has access to every internal entity of another module. The following code snippet shows how to use the @testable attribute.
@testable import AnotherModule
Let's create a simple project to see how it works. Open Xcode 7, create a project based on the Single View Application template, and tick the checkbox labeled Include Unit Tests.
To show how the @testable attribute works, I've implemented a structure named User. It has two stored properties, firstName and lastName, and a private computed property, fullName.
import Foundation
struct User {
var lastName: String = ""
var firstName: String = ""
var fullName: String {
get {
return firstName + " " + lastName
}
}
}
Because we didn't specify an access level for the fullName computed property, it's access level is internal. If we want to unit test fullName, we could mark it as public. This isn't great and it defeats the purpose of Swift's access control.
Fortunately, Swift 2 solves this issue with the @testable attribute. The following code snippet shows how the @testable attribute is used. By prefixing the import statement with the @testable attribute, we can access the fullName property of the User structure in the testFullName method.
import XCTest
@testable import Testing
class TestingTests: XCTestCase {
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
func testFullName() {
var me = User()
me.firstName = "Bart"
me.lastName = "Jacobs"
XCTAssertEqual(me.fullName, "Bart Jacobs", "The full name should be equal to \"Bart Jacobs\".")
}
}
Note that the documentation clearly states that the @testable attribute can only do its job if the product is compiled with testing enabled. In other words, you can't and shouldn't use the @testable attribute for purposes other than testing.
2. Code Coverage
If you're testing the code you write, then you're on the right track. However, it's equally important to understand how well the tests you write cover the code you think they cover. While previous versions of Xcode have had support for code coverage to some extent, it has always been a bit finicky to get it to work. That's no longer the case with Xcode 7.
Instead of showing you a bunch of screenshots, I'd like to walk you through the steps involved to enable code coverage in an Xcode project. Let's take the project we created a few minutes ago as an example.
To enable code coverage, we only need to edit the active scheme. Click the scheme in the top left and choose Edit Scheme....
To enable code coverage, select Test from the left and tick the checkbox labeled Gather Code Coverage. That's it. If you've tried to enable code coverage in earlier versions of Xcode, then I'm sure you're happy to see how easy it is to enable code coverage in Xcode 7.
Run the unit tests by pressing Command-U or selecting Test from Xcode's Product menu. When the tests have finished running, open the Report Navigator on the right and select the Coverage tab at the top. This shows you how well your unit tests cover the code you've written.
It looks like we've done a pretty good job unit testing the User structure. It gets better though. Do you see the tiny arrow following the method name? If you click it, Xcode will take you to the source code the unit test tested. Because Xcode has collected coverage data, it can also show that coverage data in the source code editor.
The coverage data is shown on the right of the source code editor. The number in the gutter on the right indicates how often the unit tests have triggered that specific routine.
Code that wasn't executed during the tests are highlighted. This is very useful for writing better tests and for improving the code coverage of your test suites.
3. UI Testing
One of the most anticipated features of Xcode 7 is the integration of user interface testing. In the past, I've worked with KIF and UI Automation, but I've never been quite happy with either solution. Xcode's UI testing looks like the solution many of us have been waiting for.
XCTest Framework
User interface testing in Xcode 7 expands on Apple's robust XCTest framework through the addition of three classes, XCUIApplication, XCUIElement, and XCUIElementQuery. To make the creation of user interface tests easier, Xcode 7 provides the ability to record tests. This is a very powerful feature that some of you may already be familiar with if you've ever worked with Apple's UI Automation.
As with UI Automation, Xcode's user interface testing relies heavily on accessibility. It's therefore more important than ever to spend time making your applications more accessible to users with a disability.
Note that UI testing is only available on iOS 9 and OS X 10.11. Apple emphasizes that unit tests and user interace tests complement each other. In other words, it's not a matter of choosing one or the other.
While it's easy to create user interace tests for an application, you are not expected to write UI tests for every possible scenario or use case. Start by focusing on common and critical use cases, such as user onboarding.
Test Reports
Test reports have also improved substantially. What I like most about the new tests reports is the addition of screenshots for failed UI tests. In addition to an error message, Xcode takes a snapshot of your application's user interface when a UI test fails. This makes it very easy to find and fix the problem.
Learn More in Our Testing With Swift Course
If you want to learn more about testing with Swift and the XCTest framework, then I encourage you to take a look at Derek Jensen's course about unit testing with Swift and XCTest.
Conclusion
Apple has put a lot of effort into improving testing and debugging with the release of Xcode 7. While integrated code coverage is fantastic, the addition of user interface testing is going to have an even bigger impact.
I hope this brief overview has wet your appetite for testing in Xcode 7. We will be covering these new features in more detail in future tutorials.
In this tutorial,
I will talk again about Material Design. Google I/O 2015 was an important event for
every Android developer and design was of course part of the discussion.
Google has realized that backward compatibility is the trickiest part of the material design implementation. Sure, support libraries, such as appcompat-v4 and appcompat-v7, are part
of the solution. However, Theme.AppCompat doesn’t implement every material item
used in Google’s official applications. One of the features that is not
present in the AppCompat theme is the possibility to position a floating label on
top of an EditText widget. You can see what I mean in the below example.
During
Google I/O 2015, the Android team released a brand new support library, the Design
Support Library. It comes in very handy for this kind of problem. This tutorial
will show you how to use the new TextInputLayout widget that's included in the Design Support Library.
1. Implementing TextInputLayout
Step 1: Create a New Project
In Android Studio, choose New > New project from the File menu. Enter the required information to configure the project and create the project. In my
example, I targeted the project to API 7, which is the minimum API level supported by the Design Support Library. By targeting such a low API level,
your app will run on almost every Android device. I've named the main activity LoginActivity and its layout file activity_login.xml.
After setting up the project, remove in the main activity the onCreateOptionsMenuand onOptionsItemSelected method that are automatically generated by Android Studio. The login screen we're about to create doesn’t need a menu so it's fine to delete these methods. Remember also to delete the XML menu file that lives in the res/menu folder.
Step 2: Import the Support Libraries
To
use the TextInputLayout widget, you have to import two libraries. The first one is
appcompat-v7, which ensures that the material styles are backward compatible. The second one is the Design Support Library.
In your project's build.gradle file, add the following lines in the project's dependencies:
If Gradle doesn’t automatically ask you to synchronize your project, choose Make module 'app' from the Build menu or press F9. By doing so, the Android
Studio build system will automatically fetch the necessary resources and you will
be able to import any required classes.
Step 3: Design the User Interface
The user interface of this project is very simple. It shows a welcome label (which can be easily replaced by a logo if you have one) and two EditText elements, one
for the username and one for the password. The layout also includes a button that triggers the login sequence. The background color is a nice, flat, light grey.
Another important detail worth remembering is the correct setting of the inputType attribute of the EditText elements. The inputType of the first EditText element should be set to textEmail while that of the second one should be set to textPassword. This is what the layout should look like.
We have finally
arrived at the most interesting part of this tutorial. A TextInputLayout widget behaves
exactly as a LinearLayout does, it’s just a wrapper. TextInputLayout only accepts
one child element, similar to a ScrollView. The child element needs to be an EditText element.
Note that I
specified another parameter in the EditText item, a hint. As you already know,
this attribute allows you to show a custom hint when there’s no content in the
EditText. Once the user starts typing, the hint disappears. This isn't great, because they lose context of the information they are entering.
Thanks toTextInputLayout, this won’t be a problem anymore. While the EditText alone will
hide the hint after the first character is typed, when wrapped in aTextInputLayout the hint will become a floating label above the EditText. A nice material animation is included too.
If you run
the application now, nothing will happen. Sure, the EditText hint attribute
will behave as expected. However, there's no material animation and no floating labels. Why is that? We're still missing some code to make everything work.
Step 5: Setting Hints
Below the setContentView method, initialize the references to the
TextInputLayout views.
final TextInputLayout usernameWrapper = (TextInputLayout) findViewById(R.id.usernameWrapper);
final TextInputLayout passwordWrapper = (TextInputLayout) findViewById(R.id.passwordWrapper);
To
animate the floating label, you just need to set a hint, using the setHint
method.
And you’re done. Your login screen now properly follows the material design
guidelines. Run the application to see your beautiful login screen.
2. Handling Errors
Another
nice feature of TextInputLayout is the way it can handle errors. By validating the input, you prevent users from misspelling their email address or
entering a password that is too short.
With input validation, incorrect credentials would be processed by the backend, errors would be generated and sent to the client, and shown to the (waiting) user. A
considerable loss of time and a poor user experience. You should check the user's input before sending it to the backend.
Step 1: Implementing the onClick Method
You first have to handle the button click. There are plenty of ways to
handle button clicks. One of them is by writing a custom method and specifying it in
your XML file via the onClick attribute. I prefer setOnClickListener, but it’s
really just a matter of personal taste.
We know
that if this method is called, the user doesn’t need the keyboard anymore.
Unfortunately, Android doesn't hide the virtual keyboard automatically, unless
you tell it to. Call hideKeyboard in the onClick method body.
Before setting the error labels, we need to define what's an error and what isn't. We're assuming that the username must be an email address and we want
to prevent users from entering an invalid email address.
Validating an email address is a little bit complex. We have to rely on regular expressions. You can use the Apache Commons library too if you wish.
I've written the following regular expression, using the guidelines suggested by Wikipedia about email validity.
The meaning
of this regular expression is quite simple. It's comprised of three capturing groups. The first one matches the letters of the alphabet (upper and lower case), numbers, and a series
of accepted symbols. Because of the + quantifier, this group matches a string
that’s composed of at least one character.
Next, there’s the @ symbol, which is of course required in every email address. The second group accepts only letters, numbers, and hyphens. The length also needs to be at least one (]+).
Finally, there's the last group, which contains a dot and whose purpose is matching subdomains and the TLD. Its quantifier is a star, *, which means that this
group looks for a string whose length can be zero or more. In fact, email addresses with a domain, but no TLD, are actually valid.
Since we
want to validate a String, we have to rely on
Pattern and Matcher classes, included in the java.util.regex package. Import
these classes in your activity and then implement the following method:
The
validation of the password field is much simpler. Most organizations implement
different policies for password validity, but everyone imposes a minimum
length. A reasonable rule of thumb is that the password should never be shorter
than six characters.
public boolean validatePassword(String password) {
return password.length() > 5;
}
Step 3: Retrieving Data
As I said, TextInputLayout
is just a wrapper, but unlike LinearLayout and ScrollView, you can get its child
element using a specific method, getEditText. There's no need to use findViewById.
If TextInputLayout doesn't contain an EditText, getEditText returns null so be careful of a NullPointException.
TextInputLayout error handling is easy and fast. The required methods are setErrorEnabled and setError.
setError
sets a red error message that will be displayed below the EditText. If the parameter
passed is null, the error message is cleared. It also changes the color of the
whole EditText widget to red.
setErrorEnabled enables the error
functionality. This directly affects the size of the layout, increasing the
lower padding to make room for the error label. Enabling this functionality
before setting an error message via setError means that this layout will
not change size when an error is displayed. You should do some tests combining these two methods so that you actually seewhat I am talking about.
Another
interesting fact is that if the error functionality has not been enabled yet
and you call setError passing a non-null parameter, then setErrorEnabled(true)
will be automatically called.
Now that we
have defined what’s right and what’s wrong, and we know how to retrieve
data and display possible errors, the implementation of the onClick method becomes
trivial.
public void onClick(View v) {
hideKeyboard();
String username = usernameWrapper.getEditText().getText().toString();
String password = usernameWrapper.getEditText().getText().toString();
if (!validateEmail(username)) {
usernameWrapper.setError("Not a valid email address!");
} else if (!validatePassword(password)) {
passwordWrapper.setError("Not a valid password!");
} else {
usernameWrapper.setErrorEnabled(false);
passwordWrapper.setErrorEnabled(false);
doLogin();
}
}
I've added a doLogin method, but it's currently empty since this is beyond the scope of this tutorial.
public void doLogin() {
Toast.makeText(getApplicationContext(), "OK! I'm performing login.", Toast.LENGTH_SHORT).show();
// TODO: login procedure; not within the scope of this tutorial.
}
3. Styling
You might
want to do one last thing, changing the color of the TextInputLayout widget. By
default, the AppCompact theme sets it to green, but quite often this color conflicts
with your color palette.
Google wrote the Design Support Library very well. Every
widget’s color is drawn directly from the theme’s colors, specified in your
style.xml file. Just open it and add the colorAccent item to your active theme to change the form's color scheme.
In this tutorial, we saw how to implement the new layout item TextInputLayout, thanks to the just introduced Design Support Library.
The design paradigm that this widget implements allows users to never never lose context of the information they are entering and it was actually introduced by Google last year, along with Material Design.
At that time, there was no support library giving developers the possibility to put this widget into action in their projects, until Google I/O 2015. Now, if your application expects some sort of data input, you will finally be truly material design compliant.