Quantcast
Channel: Envato Tuts+ Code - Mobile Development
Viewing all 1836 articles
Browse latest View live

Sharing Files With NFC on Android

$
0
0

In this tutorial, you will learn the basics of P2P (peer-to-peer) communication and create an application to share large files, such as images and videos, from one device to another using NFC (near field communication) on Android.

1. Introduction

NFC or Near Field Communication is a set of short-range wireless technologies. It enables the exchange of data between an NFC tag and an NFC-enabled device, or between NFC-enabled devices within a distance of no more than 4 cm.

There are three modes of NFC operation:

  • Reading and writing contactless tags: These tags are generally very small and do not require any battery power. They can be embedded in all sorts of objects, such as movie posters, products, stickers, and so on.
  • Card emulation mode: Think smart credit cards. This allows an Android device to act like a smart card. The obvious benefit of this is that your device can act like one card and then act like a different one at the touch of a button. This is one way an Android device can replace your wallet. Whatever credit card, bus pass, or ticket you're using, your Android device could impersonate—securely, of course—that item. The reader on the other side of the transaction thinks that it's interacting with that item, when in fact, it's dealing with an Android device.
  • Peer-to-peer communication: Each side recognizes that it's talking to another device and not just a tag. The protocol has been developed by Google and allows two devices to send messages back and forth.

The peer-to-peer (P2P) data exchange feature was added to Android in API level 14 (Android 4.0, Ice Cream Sandwich) and is called Android Beam. It enables rapid short-range exchange of data between two NFC-enabled Android devices.

2. P2P Communication With Android Beam

The Android Beam data exchange feature has two APIs, the NDEF transfer API and the file transfer API.

NDEF Transfer API

This API was introduced in API level 14 (Android 4.0, Ice Cream Sandwich) and enables the transfer of small amounts of data like URLs, contacts, etc. The data to be transferred must be formatted in NDEF (NFC Data Exchange Format) and is sent as an NDEF message.

File Transfer API

The file transfer API was introduced in API level 16 (Android 4.1, Jelly Bean) and enables the transfer of large files, such as images, videos, etc.

There are some caveats though. Android Beam only works when the application sending the data is running in the foreground and the device receiving the data is unlocked.

The Android Beam file transfer API has two additional requirements:

  • The files that need to be transferred must be located in external storage.
  • The files that need to be transferred must be world-readable.

In this tutorial, we will use the Android Beam file transfer API of the Android SDK to create an application that enables users to share files between devices.

3. Requirements

Due to the limitations of the emulator, the application needs to be tested with two physical NFC-enabled Android devices running Android 4.1 or higher.

4. Getting Started

Using Eclipse, create a new Android application project and name it NFCDemo

Project Settings

Since Android Beam file transfer is only available on devices running Android 4.1+, we need to set Minimum Required SDK to API 16: Android 4.1 (Jelly Bean).

5. Configuring Manifest File

To use NFC in an Android app, we have to declare the NFC permission in the manifest file as shown below.

<uses-permission 
    android:name="android.permission.NFC" />

In addition, to read files from external storage, declare the READ_EXTERNAL_STORAGE permission as shown below.

<uses-permission 
    android:name="android.permission.READ_EXTERNAL_STORAGE" /> 

Not every Android device supports NFC. To make sure that our app only shows up in Google Play for those devices that support NFC, add the <uses-feature> element to the manifest file.

<uses-feature 
    android:name="android.hardware.nfc" 
    android:required="true" />

If NFC is an optional feature of your app, then you can omit the <uses-feature> element from the manifest file and set the minimum SDK version to a lower API level. In that case, you need to check if the device supports NFC and the Android Beam API, and update the user interface accordingly.

6. Creating Layouts

Open the activity_main.xml layout file and add a Button as shown below. As you can see, we've added a Button that the user can tap to initiate the transfer of a file.

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp" ><Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:text="Send File"
        android:onClick="sendFile" /></RelativeLayout>

7. Implementing File Transfer

Open the MainActivity class and replace the current implementation with the one shown below. Don't worry about the implementation for now. I'll explain every step in a moment.

package com.tutsplus.nfcdemo;

import java.io.File;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {
    
	private NfcAdapter nfcAdapter;	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		PackageManager pm = this.getPackageManager();
		// Check whether NFC is available on device		
        if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
        	// NFC is not available on the device.
            Toast.makeText(this, "The device does not has NFC hardware.", 
            				Toast.LENGTH_SHORT).show();           
        } 
        // Check whether device is running Android 4.1 or higher 
        else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            // Android Beam feature is not supported.            
        	Toast.makeText(this, "Android Beam is not supported.", 
        					Toast.LENGTH_SHORT).show();                     
        }         
        else {
        	// NFC and Android Beam file transfer is supported.        	
        	Toast.makeText(this, "Android Beam is supported on your device.", 
        					Toast.LENGTH_SHORT).show();
        }
	}			
	public void sendFile(View view) {
		nfcAdapter = NfcAdapter.getDefaultAdapter(this);  
		// Check whether NFC is enabled on device
        if(!nfcAdapter.isEnabled()){
        	// NFC is disabled, show the settings UI
        	// to enable NFC
        	Toast.makeText(this, "Please enable NFC.", 
                            Toast.LENGTH_SHORT).show(); 
        	startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
        }        
        // Check whether Android Beam feature is enabled on device
        else if(!nfcAdapter.isNdefPushEnabled()) {
        	// Android Beam is disabled, show the settings UI
        	// to enable Android Beam 
        	Toast.makeText(this, "Please enable Android Beam.", 
        					Toast.LENGTH_SHORT).show();
        	startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
        }
        else {
        	// NFC and Android Beam both are enabled 
        	// File to be transferred    
        	// For the sake of this tutorial I've placed an image 
        	// named 'wallpaper.png' in the 'Pictures' directory
        	String fileName = "wallpaper.png";
        	// Retrieve the path to the user's public pictures directory 
        	File fileDirectory = Environment
                                    .getExternalStoragePublicDirectory(
                                            Environment.DIRECTORY_PICTURES);
        	// Create a new file using the specified directory and name
        	File fileToTransfer = new File(fileDirectory, fileName);
        	fileToTransfer.setReadable(true, false);
        	nfcAdapter.setBeamPushUris(
                            new Uri[]{Uri.fromFile(fileToTransfer)}, this);		        
        }               	
	}
}

8. Testing the App

Before we dive into the code, let's see what steps we need to take to transfer a file from one device to another.

Step 1

Connect one of the two Android devices to your development workstation via USB with USB debugging enabled. Let's refer to that device as the sender.

Step 2

Enable NFC and Android Beam on the sender. Press F11 to debug the application. This will install and launch NFCDemo on the sender.

Step 3

Enable NFC on the second device, the receiver.

Step 4

Tap the Send File button and position the devices close together to let NFC do its work. You should see a Touch to beam message appear on the sender. Tap the screen to initiate the transfer.

The Touch to beam UI as displayed on the Sender

Step 5

The receiver should show a notification in the status bar to indicate the progress of the file transfer.

Incoming beam notification as displayed on Receiver  

Step 6

If the file transfer is completed successfully, a Beam complete message is displayed to the user.

Beam complete notification as displayed on Receiver

9. Decoding the Code

Let's take a look at the code that makes all this possible.

Determine Device Capabilities

As mentioned earlier, if NFC is an optional feature of our app we should check for NFC and Android Beam support. This check can be performed anywhere in our app. In this example, I've put the code in the onCreate method of the MainActivity class.

Step 1

Obtain a reference to the PackageManager.

PackageManager pm = this.getPackageManager();

The PackageManager class holds information about all the packages installed on the device.

Step 2

Call the hasSystemFeature method on the PackageManager object to determine if the device has NFC support. This method returns trueif the desired feature is supported by the device.

if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
  // NFC is not available on the device.
}
else {
  // NFC is available on the device.
}

Step 3

If the device has NFC support, then we have to check the device's Android version. The Android version (API level) running on a device is available via  android.os.Build.VERSION.SDK_INT. If the version is greater than or equal to 16, Build.VERSION_CODES.JELLY_BEAN, then the device supports Android Beam file transfer.

if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
  // NFC is not available on the device.
}
// Check whether device is running Android 4.1 or higher 
else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
    // Android Beam feature is not supported.            
}         
else {
  // NFC and Android Beam file transfer is supported.         
}

Initiate File Transfer

Once we have determined that the device has the required capabilities, we can initiate the file transfer using Android Beam.

Step 1

Obtain a reference to the NfcAdapter.

nfcAdapter = NfcAdapter.getDefaultAdapter(this); 

The job of the NfcAdapter is to manage the exchange of data between an NFC tag and a NFC-enabled device or between two NFC-enabled devices.

Step 2

The adapter can be enabled or disabled. To determine whether the adapter is enabled, call the isEnabled method on the NfcAdapter object.

// Check whether NFC is enabled on device
if (!nfcAdapter.isEnabled()) {
  // NFC is disabled, show the settings UI to enable NFC
}        
else {
  // NFC is enabled
}

This method returns true if NFC is enabled on the device. If NFC is disabled, we prompt the user to enable it and show the NFC settings user interface. 

startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));

Step 3

Similarly, the Android Beam feature can also be enabled or disabled. To check its state, call the isNdefPushEnabled method on the NfcAdapter object.

// Check whether NFC is enabled on device
if (!nfcAdapter.isEnabled()) {
  // NFC is disabled, show the settings UI to enable NFC
}        
// Check whether Android Beam feature is enabled on device
else if (!nfcAdapter.isNdefPushEnabled()) {
  // Android Beam is disabled, show the settings UI to enable Android Beam 
}
else {
  // NFC and Android Beam both are enabled 
}

If this method returns false, we prompt the user to enable it and show the Android Beam settings user interface.

startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
NFC Settings UI

If both NFC and Android Beam are enabled, we can proceed with the file transfer.

Step 4

Create a new File using the directory where the file is located on the device and the name of the file. To test the file transfer, I've added an image named wallpaper.png in the Pictures directory in the external storage.

// Create a new file using the specified directory and name
File fileToTransfer = new File(fileDirectory, fileName);

Step 5

Call the setBeamPushUris method on the NfcAdapter object and pass the URI of the file to be transferred.

nfcAdapter.setBeamPushUris(new Uri[]{Uri.fromFile(fileToTransfer)}, 
                            this);

The setBeamPushUris method accepts an array of Uri objects. If you want to send more than one file, you can pass multiple URIs to the adapter.

The URIs passed to the setBeamPushUris method are queued by the adapter and are transferred to the receiving device as soon as it comes in close proximity of the sending device.

Conclusion

In this tutorial, you learned about the basics of NFC on Android. You've also learned how to restrict access to an app by unsupported devices using the manifest file and determine device capabilities at runtime. To send files using NFC, we made use of the NfcAdapter class.

While I've tried to cover the basics of working with the Android Beam file transfer API to help you get started, there is still more explore. If you like to learn more, then I encourage you to visit the Android developer portal for more information.

2014-11-19T15:50:18.000Z2014-11-19T15:50:18.000ZAvinash Gupta

Sharing Files With NFC on Android

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22501

In this tutorial, you will learn the basics of P2P (peer-to-peer) communication and create an application to share large files, such as images and videos, from one device to another using NFC (near field communication) on Android.

1. Introduction

NFC or Near Field Communication is a set of short-range wireless technologies. It enables the exchange of data between an NFC tag and an NFC-enabled device, or between NFC-enabled devices within a distance of no more than 4 cm.

There are three modes of NFC operation:

  • Reading and writing contactless tags: These tags are generally very small and do not require any battery power. They can be embedded in all sorts of objects, such as movie posters, products, stickers, and so on.
  • Card emulation mode: Think smart credit cards. This allows an Android device to act like a smart card. The obvious benefit of this is that your device can act like one card and then act like a different one at the touch of a button. This is one way an Android device can replace your wallet. Whatever credit card, bus pass, or ticket you're using, your Android device could impersonate—securely, of course—that item. The reader on the other side of the transaction thinks that it's interacting with that item, when in fact, it's dealing with an Android device.
  • Peer-to-peer communication: Each side recognizes that it's talking to another device and not just a tag. The protocol has been developed by Google and allows two devices to send messages back and forth.

The peer-to-peer (P2P) data exchange feature was added to Android in API level 14 (Android 4.0, Ice Cream Sandwich) and is called Android Beam. It enables rapid short-range exchange of data between two NFC-enabled Android devices.

2. P2P Communication With Android Beam

The Android Beam data exchange feature has two APIs, the NDEF transfer API and the file transfer API.

NDEF Transfer API

This API was introduced in API level 14 (Android 4.0, Ice Cream Sandwich) and enables the transfer of small amounts of data like URLs, contacts, etc. The data to be transferred must be formatted in NDEF (NFC Data Exchange Format) and is sent as an NDEF message.

File Transfer API

The file transfer API was introduced in API level 16 (Android 4.1, Jelly Bean) and enables the transfer of large files, such as images, videos, etc.

There are some caveats though. Android Beam only works when the application sending the data is running in the foreground and the device receiving the data is unlocked.

The Android Beam file transfer API has two additional requirements:

  • The files that need to be transferred must be located in external storage.
  • The files that need to be transferred must be world-readable.

In this tutorial, we will use the Android Beam file transfer API of the Android SDK to create an application that enables users to share files between devices.

3. Requirements

Due to the limitations of the emulator, the application needs to be tested with two physical NFC-enabled Android devices running Android 4.1 or higher.

4. Getting Started

Using Eclipse, create a new Android application project and name it NFCDemo

Project Settings

Since Android Beam file transfer is only available on devices running Android 4.1+, we need to set Minimum Required SDK to API 16: Android 4.1 (Jelly Bean).

5. Configuring Manifest File

To use NFC in an Android app, we have to declare the NFC permission in the manifest file as shown below.

<uses-permission 
    android:name="android.permission.NFC" />

In addition, to read files from external storage, declare the READ_EXTERNAL_STORAGE permission as shown below.

<uses-permission 
    android:name="android.permission.READ_EXTERNAL_STORAGE" /> 

Not every Android device supports NFC. To make sure that our app only shows up in Google Play for those devices that support NFC, add the <uses-feature> element to the manifest file.

<uses-feature 
    android:name="android.hardware.nfc" 
    android:required="true" />

If NFC is an optional feature of your app, then you can omit the <uses-feature> element from the manifest file and set the minimum SDK version to a lower API level. In that case, you need to check if the device supports NFC and the Android Beam API, and update the user interface accordingly.

6. Creating Layouts

Open the activity_main.xml layout file and add a Button as shown below. As you can see, we've added a Button that the user can tap to initiate the transfer of a file.

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp" ><Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:text="Send File"
        android:onClick="sendFile" /></RelativeLayout>

7. Implementing File Transfer

Open the MainActivity class and replace the current implementation with the one shown below. Don't worry about the implementation for now. I'll explain every step in a moment.

package com.tutsplus.nfcdemo;

import java.io.File;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {
    
	private NfcAdapter nfcAdapter;	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		PackageManager pm = this.getPackageManager();
		// Check whether NFC is available on device		
        if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
        	// NFC is not available on the device.
            Toast.makeText(this, "The device does not has NFC hardware.", 
            				Toast.LENGTH_SHORT).show();           
        } 
        // Check whether device is running Android 4.1 or higher 
        else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            // Android Beam feature is not supported.            
        	Toast.makeText(this, "Android Beam is not supported.", 
        					Toast.LENGTH_SHORT).show();                     
        }         
        else {
        	// NFC and Android Beam file transfer is supported.        	
        	Toast.makeText(this, "Android Beam is supported on your device.", 
        					Toast.LENGTH_SHORT).show();
        }
	}			
	public void sendFile(View view) {
		nfcAdapter = NfcAdapter.getDefaultAdapter(this);  
		// Check whether NFC is enabled on device
        if(!nfcAdapter.isEnabled()){
        	// NFC is disabled, show the settings UI
        	// to enable NFC
        	Toast.makeText(this, "Please enable NFC.", 
                            Toast.LENGTH_SHORT).show(); 
        	startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
        }        
        // Check whether Android Beam feature is enabled on device
        else if(!nfcAdapter.isNdefPushEnabled()) {
        	// Android Beam is disabled, show the settings UI
        	// to enable Android Beam 
        	Toast.makeText(this, "Please enable Android Beam.", 
        					Toast.LENGTH_SHORT).show();
        	startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
        }
        else {
        	// NFC and Android Beam both are enabled 
        	// File to be transferred    
        	// For the sake of this tutorial I've placed an image 
        	// named 'wallpaper.png' in the 'Pictures' directory
        	String fileName = "wallpaper.png";
        	// Retrieve the path to the user's public pictures directory 
        	File fileDirectory = Environment
                                    .getExternalStoragePublicDirectory(
                                            Environment.DIRECTORY_PICTURES);
        	// Create a new file using the specified directory and name
        	File fileToTransfer = new File(fileDirectory, fileName);
        	fileToTransfer.setReadable(true, false);
        	nfcAdapter.setBeamPushUris(
                            new Uri[]{Uri.fromFile(fileToTransfer)}, this);		        
        }               	
	}
}

8. Testing the App

Before we dive into the code, let's see what steps we need to take to transfer a file from one device to another.

Step 1

Connect one of the two Android devices to your development workstation via USB with USB debugging enabled. Let's refer to that device as the sender.

Step 2

Enable NFC and Android Beam on the sender. Press F11 to debug the application. This will install and launch NFCDemo on the sender.

Step 3

Enable NFC on the second device, the receiver.

Step 4

Tap the Send File button and position the devices close together to let NFC do its work. You should see a Touch to beam message appear on the sender. Tap the screen to initiate the transfer.

The Touch to beam UI as displayed on the Sender

Step 5

The receiver should show a notification in the status bar to indicate the progress of the file transfer.

Incoming beam notification as displayed on Receiver  

Step 6

If the file transfer is completed successfully, a Beam complete message is displayed to the user.

Beam complete notification as displayed on Receiver

9. Decoding the Code

Let's take a look at the code that makes all this possible.

Determine Device Capabilities

As mentioned earlier, if NFC is an optional feature of our app we should check for NFC and Android Beam support. This check can be performed anywhere in our app. In this example, I've put the code in the onCreate method of the MainActivity class.

Step 1

Obtain a reference to the PackageManager.

PackageManager pm = this.getPackageManager();

The PackageManager class holds information about all the packages installed on the device.

Step 2

Call the hasSystemFeature method on the PackageManager object to determine if the device has NFC support. This method returns trueif the desired feature is supported by the device.

if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
  // NFC is not available on the device.
}
else {
  // NFC is available on the device.
}

Step 3

If the device has NFC support, then we have to check the device's Android version. The Android version (API level) running on a device is available via  android.os.Build.VERSION.SDK_INT. If the version is greater than or equal to 16, Build.VERSION_CODES.JELLY_BEAN, then the device supports Android Beam file transfer.

if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
  // NFC is not available on the device.
}
// Check whether device is running Android 4.1 or higher 
else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
    // Android Beam feature is not supported.            
}         
else {
  // NFC and Android Beam file transfer is supported.         
}

Initiate File Transfer

Once we have determined that the device has the required capabilities, we can initiate the file transfer using Android Beam.

Step 1

Obtain a reference to the NfcAdapter.

nfcAdapter = NfcAdapter.getDefaultAdapter(this); 

The job of the NfcAdapter is to manage the exchange of data between an NFC tag and a NFC-enabled device or between two NFC-enabled devices.

Step 2

The adapter can be enabled or disabled. To determine whether the adapter is enabled, call the isEnabled method on the NfcAdapter object.

// Check whether NFC is enabled on device
if (!nfcAdapter.isEnabled()) {
  // NFC is disabled, show the settings UI to enable NFC
}        
else {
  // NFC is enabled
}

This method returns true if NFC is enabled on the device. If NFC is disabled, we prompt the user to enable it and show the NFC settings user interface. 

startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));

Step 3

Similarly, the Android Beam feature can also be enabled or disabled. To check its state, call the isNdefPushEnabled method on the NfcAdapter object.

// Check whether NFC is enabled on device
if (!nfcAdapter.isEnabled()) {
  // NFC is disabled, show the settings UI to enable NFC
}        
// Check whether Android Beam feature is enabled on device
else if (!nfcAdapter.isNdefPushEnabled()) {
  // Android Beam is disabled, show the settings UI to enable Android Beam 
}
else {
  // NFC and Android Beam both are enabled 
}

If this method returns false, we prompt the user to enable it and show the Android Beam settings user interface.

startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
NFC Settings UI

If both NFC and Android Beam are enabled, we can proceed with the file transfer.

Step 4

Create a new File using the directory where the file is located on the device and the name of the file. To test the file transfer, I've added an image named wallpaper.png in the Pictures directory in the external storage.

// Create a new file using the specified directory and name
File fileToTransfer = new File(fileDirectory, fileName);

Step 5

Call the setBeamPushUris method on the NfcAdapter object and pass the URI of the file to be transferred.

nfcAdapter.setBeamPushUris(new Uri[]{Uri.fromFile(fileToTransfer)}, 
                            this);

The setBeamPushUris method accepts an array of Uri objects. If you want to send more than one file, you can pass multiple URIs to the adapter.

The URIs passed to the setBeamPushUris method are queued by the adapter and are transferred to the receiving device as soon as it comes in close proximity of the sending device.

Conclusion

In this tutorial, you learned about the basics of NFC on Android. You've also learned how to restrict access to an app by unsupported devices using the manifest file and determine device capabilities at runtime. To send files using NFC, we made use of the NfcAdapter class.

While I've tried to cover the basics of working with the Android Beam file transfer API to help you get started, there is still more explore. If you like to learn more, then I encourage you to visit the Android developer portal for more information.

2014-11-19T15:50:18.000Z2014-11-19T15:50:18.000ZAvinash Gupta

iOS 8: Creating a Custom Keyboard in Swift

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22344

Starting with iOS 8, your applications can extend custom functionality and content beyond your app, and make it available to users while they're using other apps or the operating system. One way of extending the operating system is by creating a custom keyboard.

In this tutorial I'm going to show you how to make your own custom keyboard using Swift and the new app extension APIs. Before we do that, we are going to go over what a keyboard extension can do, what it can't do, and what it should to get approved for the App Store.

1. Overview

A custom keyboard replaces the system keyboard for users who want capabilities, such as a novel text input method or the ability to enter text in a language not otherwise supported by the operating system.

The essential function of a custom keyboard is simple, respond to taps, gestures, or other input events, and provide text in the form of an unattributed NSString object at the text insertion point of the current text input object.

After a user chooses a keyboard, it remains as the default one whenever they open an app. For this reason the keyboard must allow the user to switch to another keyboard.

There are two development essentials for every custom keyboard:
Trust. Your custom keyboard gives you access to what a user types, so trust between you and your user is essential.
A “next keyboard” key. The affordance that lets a user switch to another keyboard is part of a keyboard’s user interface; you must provide one in your keyboard. - App Extension Programming Guide

If you only need to add a few buttons to the system keyboard, then you should look into custom views for data input.

2. Requirements & Limitations

What a Custom Keyboard Can't Do

There are certain text input objects that your custom keyboard is not eligible to type into. These include secure text fields for entering passwords and phone pad objects, such as the phone number fields in the Contacts application.

Your custom keyboard does not have access to the view hierarchy of the input, it cannot control the cursor, and is unable to select text. Also, the custom keyboard cannot display anything above the top row. The system keyboard isn't limited by these constraints. For example, it shows an extension when you tap a key to show the user what key was tapped.

The red line shows the top limit of a custom keyboard.

Sandboxing

By default, a keyboard has no network access and cannot share files with its containing app. To enable these capabilities, set the value of the RequestsOpenAccess key in the Info.plist file to YES. Doing so expands the keyboard's sandbox as described in Apple's App Extension Programming Guide.

If you do request open access, your keyboard gains the following capabilities, each with a concomitant responsibility:

  • access to location services and the address book database, each requiring the user's permission on first access
  • option to use a shared container with the keyboard's containing app, which enables features, such as providing a custom lexicon management user interface in the containing app
  • ability to send keystrokes and other input events for server-side processing
  • access to iCloud, which you can use, for example, to ensure that keyboard settings and your custom autocorrect lexicon are up to date on all devices owned by the user
  • access to Game Center and in-app purchase through the containing app
  • ability to work with managed apps if you design your keyboard to support mobile device management (MDM)

Be sure to read Apple's Designing for User Trust document, which describes your responsibilities for respecting and protecting user data in case you request open access.

3. How It Works

In the most basic form we have an application that contains a keyboard extension and a UIInputViewController that controls the keyboard and responds to user events.

The Custom Keyboard template contains a subclass of UIInputViewController, which is the primary view controller of your keyboard. Let's look at the interface to get a feel of how it works.

class UIInputViewController : UIViewController, UITextInputDelegate, NSObjectProtocol {

    var inputView: UIInputView!

    var textDocumentProxy: NSObject! { get }

    func dismissKeyboard()
    func advanceToNextInputMode()

    // This will not provide a complete repository of a language's vocabulary.
    // It is solely intended to supplement existing lexicons.
    func requestSupplementaryLexiconWithCompletion(completionHandler: ((UILexicon!) -> Void)!)
}
  • inputView is the view used for the keyboard, it is the same as the view property
  • dismissKeyboard method can be called to dismiss the keyboard
  • advanceToNextInputMode is used to change between keyboards
  • textDocumentProxy is the object that you'll use to interact with the current text input
self.textDocumentProxy.insertText("Tuts+") // inserts the string "Tuts+" at the insertion point

self.textDocumentProxy.deleteBackward() // Deletes the character to the left of the insertion point

  • UIInputViewController conforms to the UITextInputDelegate protocol, notifying you when the text or text selection changes through the the selectionWillChangeselectionDidChangetextWillChange and textDidChangeevents

4. Making a Calculator Keyboard

Let's create a custom keyboard to make all this a little bit more tangible. We'll make a simple keyboard that can handle numeric input and simple operations. We're going to use a XIB file for the keyboard's user interface.

Step 1: Create a New Project

Open Xcode 6, create a new Single View Application and select Swift as the programming language. Name it CalculatorKeyboard.

Step 2: Add a Text Field

Open Main.storyboard and drag a text field from the Objects Library. We'll use this to test the keyboard later. Center the text field and add the necessary layout constraints as shown below.

If you call textField.becomeFirstResponder() in viewDidLoad the keyboard will open when you start the app.

Step 3: Add the Keyboard Extension

Select the project file in the Project Navigator and add a new target by clicking the plus button at the bottom.

Select Application Extension on the left, choose the Custom Keyboard template, and name it Calculator.

This will create a new group named Calculator, containing two files KeyboardViewController.swift and Info.plist.

Step 4: Cleaning Up

Open KeyboardViewController.swift. The template keyboard has one button, letting the user switch between keyboards. Remove the code in the viewDidLoad method.

Step 5: Creating the User Interface

Right click the Calculator group and select New File.... Select the User Interface section on the left, choose the View template, and name it Calculator. This should create a file named Calculator.xib.

Open the XIB file and, in the Attributes Inspector on the right, set the size to Freeform and the status bar to None.

In the Size Inspector set the width of the view to 320 and the height to 160.

Drag a button from the Objects Library to the view. In the Attributes Inspector, set the title to 1. In the Size Inspector, set the button's width and height to 30. Move the button to the top right corner of the view until it aligns with the margins.

Copy the button by clicking and dragging the button while pressing the Option key. Position the second button below the first one.

Select the buttons by pressing Command-A and copy the buttons. Position the new buttons below the first and second button.

Repeat the process to create another column of buttons until you have four columns of buttons.


Next, select the column on the left and make a copy that aligns with the left border of the view.

Set the width of the buttons to 140 points. Replace the top left button with a label that has the same size as the button. Rename the buttons like in the screenshot bellow.

Give the view a blueish background color and set the background color for the buttons to white with an opacity of 15%. And for the display label, make it black with an opacity of 15%. Set the text size to 18 points for every user interface object and set the text color to white. The user interface should now look like this:

Step 6: Loading the User Interface

We first need to create a property in which to store the user interface.

class KeyboardViewController: UIInputViewController {
    var calculatorView: UIView!

    ...
}

Create a method named loadInterface and call it in the viewDidLoad method of the KeyboardViewController.

class KeyboardViewController: UIInputViewController {
    ...

    override func viewDidLoad() {
        super.viewDidLoad()

        loadInterface()
    }

    func loadInterface() {
        // load the nib file
        var calculatorNib = UINib(nibName: "Calculator", bundle: nil)
        // instantiate the view
        calculatorView = calculatorNib.instantiateWithOwner(self, options: nil)[0] as UIView

        // add the interface to the main view
        view.addSubview(calculatorView)

        // copy the background color
        view.backgroundColor = calculatorView.backgroundColor
    }

    ...    
} 

Step 7: Testing the Keyboard

At this point you should be able to test your new keyboard. With the CalculatorKeyboard scheme selected, build and run the application on your device. This will add a new keyboard to your device. However, before you can use it you first need to install it.

Go to Settings > General > Keyboard > Keyboards and select Add new Keyboard. There you'll find the Calculator keyboard in the list of third-party keyboards. Select and install the keyboard. The next time you open the keyboard you should be able to see your new keyboard by pressing the next keyboard button.

If you are using the iOS Simulator, the custom keyboard might not work inside your app. To see the keyboard press home and open Spotlight.

Step 8: Next Keyboard

Create a property for the next keyboard button in the KeyboardViewController class.

class KeyboardViewController: UIInputViewController {

    @IBOutlet var nextKeyboardButton: UIButton!

    ...
}

Open Calculator.xib , Select File's Owner, and in the Identity Inspector change its class to KeyboardViewController.

Right click on the Next Keyboard button and connect a referencing outlet to the File's Owner.

In the loadInterface method, we add an action to the nextKeyboard button as shown below.

class KeyboardViewController: UIInputViewController {
    ...

    func loadInterface() {
        ... 

        // This will make the button call advanceToNextInputMode() when tapped
        nextKeyboardButton.addTarget(self, action: "advanceToNextInputMode", forControlEvents: .TouchUpInside)
    }

}

Step 9: Number Display

Create a property for the display and connect the referencing outlet in Interface Builder.

class KeyboardViewController: UIInputViewController {

    @IBOutlet var display: UILabel!

    ...
}

Create a method named clearDisplay and call it in the viewDidLoad method, after invoking loadInterface. The display should now show 0 when you open the keyboard.

class KeyboardViewController: UIInputViewController { 
    ... 
    override func viewDidLoad() { 
        super.viewDidLoad() 
        loadInterface() 
        clearDisplay() 
    } 
    ... 
    @IBAction func clearDisplay() { 
        display.text = "0" 
    } 
}

Connect the C button's touch up inside event to the clearDisplay method in Interface Builder.

Step 10: Number Input

Time to handle numeric input. When you open the keyboard it shows 0 on the display. If you tap a number key, it should replace the display to that number. Create a property named shouldClearDisplayBeforeInserting to implement this behavior.

Create a method named didTapNumber and connect it in Interface Builder to all the number buttons for the touch up inside event. The method uses the titleLabel of the button to determine which number was tapped.

class KeyboardViewController: UIInputViewController {
    var shouldClearDisplayBeforeInserting = true

    ...

    @IBAction func didTapNumber(number: UIButton) {
        if shouldClearDisplayBeforeInserting {
            display.text = ""
            shouldClearDisplayBeforeInserting = false
        }

        if var numberAsString = number.titleLabel?.text {
            var numberAsNSString = numberAsString as NSString
            if var oldDisplay = display?.text! {
                display.text = "\(oldDisplay)\(numberAsNSString.intValue)"
            } else {
                display.text = "\(numberAsNSString.intValue)"
            }
        }
    }
}

Update the clearDisplay method as shown below.

class KeyboardViewController: UIInputViewController {
    ...

    @IBAction func clearDisplay() {
        display.text = "0"
        shouldClearDisplayBeforeInserting = true
    }
}

The keyboard code is in a different target than your app. Because of this the debug logs aren't visible. To see the logs for the Calculator target, open the system log from the iOS Simulator.

Step 11: Dot Input

The button to insert a dot should add a dot to the display, but only if there isn't a dot present yet.

class KeyboardViewController: UIInputViewController {
    ...

    @IBAction func didTapDot() {
        if let input = display?.text {
            var hasDot = false
            for ch in input.unicodeScalars {
                if ch == "." {
                    hasDot = true
                    break
                }
            }
            if hasDot == false {
                display.text = "\(input)."
            }
        }
    }
} 

Step 12: Inserting Text

The button to insert text should add the calculator display text to the insertion point. To do this, we use the textDocumentProxy property as shown below.

class KeyboardViewController: UIInputViewController {
    ...

    @IBAction func didTapInsert() {
        var proxy = textDocumentProxy as UITextDocumentProxy

        if let input = display?.text as String? {
            proxy.insertText(input)
        }
    }
}

Step 13: Handling Operations

Because we're implementing a simple keyboard that doesn't support expression trees, 1 + 2 * 3 will equal 9. We're going to use a simpler model in which the calculator has an internal memory slot on which it can apply operations.

Let's take a simple input in order to understand how the calculator algorithm works:

  • user taps 1, the display should change from 0 to 1
  • user taps +, the calculator should remember to add the next inputted number to 1
  • user taps 2, the display should change from 1 to 2
  • user taps *, the display and the internal memory of the calculator should change to 3, the calculator should remember to multiply the internal memory with the next inputted number
  • user taps 3, the display should remain 3
  • user taps =, the calculator should apply the last operation and the display should change to 9

Observations:

  • the calculator should remember the next operation to apply
  • after inputting a number if an operation or equal is pressed, the calculator should apply the last remembered operation
  • if the user presses two or more operations without inputting a number, the calculator should remember the last one
  • after an operation is applied, the display should update with the result
  • after a result is displayed, the display should clear before writing another number

In order to implement the calculator, we are going to need:

  • an internalMemory property that stores the temporary result
  • a property that stores the nextOperation
  • another one that to remember if it should apply the nextOperation after an operation is pressed
enum Operation {
    case Addition
    case Multiplication
    case Subtraction
    case Division
    case None
}

class KeyboardViewController: UIInputViewController {
    var internalMemory = 0.0
    var nextOperation = Operation.None
    var shouldCompute = false

    ...
}

Create a method named didTapOperation and connect it to the operation buttons touch up inside event in Interface Builder. The method will use the button title to determine which operation was pressed.

class KeyboardViewController: UIInputViewController {
    ...

    @IBAction func didTapOperation(operation: UIButton) {
        if shouldCompute {
            computeLastOperation()
        }

        if var op = operation.titleLabel?.text {
            switch op {
                case "+":
                    nextOperation = Operation.Addition
                case "-":
                    nextOperation = Operation.Subtraction
                case "X":
                    nextOperation = Operation.Multiplication
                case "%":
                    nextOperation = Operation.Division
                default:
                    nextOperation = Operation.None
            }
        }
    }
}

Create and implement the computeLastOperation method.

class KeyboardViewController: UIInputViewController {
    ...

    @IBAction func computeLastOperation() {
        // remember not to compute if another operation is pressed without inputing another number first
        shouldCompute = false

        if var input = display?.text {
            var inputAsDouble = (input as NSString).doubleValue
            var result = 0.0

            // apply the operation
            switch nextOperation {
            case .Addition:
                result = internalMemory + inputAsDouble
            case .Subtraction:
                result = internalMemory - inputAsDouble
            case .Multiplication:
                result = internalMemory * inputAsDouble
            case .Division:
                result = internalMemory / inputAsDouble
            default:
                result = 0.0
            }

            nextOperation = Operation.None

            var output = "\(result)"

            // if the result is an integer don't show the decimal point
            if output.hasSuffix(".0") {
                output = "\(Int(result))"
            }

            // truncatingg to last five digits
            var components = output.componentsSeparatedByString(".")
            if components.count >= 2 {
                var beforePoint = components[0]
                var afterPoint = components[1]
                if afterPoint.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) > 5 {
                    let index: String.Index = advance(afterPoint.startIndex, 5)
                    afterPoint = afterPoint.substringToIndex(index)
                }
                output = beforePoint + "." + afterPoint
            }


            // update the display
            display.text = output

            // save the result
            internalMemory = result

            // remember to clear the display before inserting a new number
            shouldClearDisplayBeforeInserting = true
        }
    }
}

Update the clearDisplayMethod as shown below. When the user starts to write the first number, the internal memory should be set to 0 and nextOperation should be addition. That way, after the user inputs the first number and presses an operation, the calculator will remember the inputted number.

class KeyboardViewController: UIInputViewController {
    ...

    @IBAction func clearDisplay() {
        display.text = "0"
        internalMemory = 0
        nextOperation = Operation.Addition
        shouldClearDisplayBeforeInserting = true
    }
}

Step 14: Finishing Touches

Let's use the IBInspectable declaration attribute to add a corner radius to the buttons and display. First, create a subclass of UIButton and UILabel.

class RoundButton: UIButton {
    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
}

class RoundLabel: UILabel {
    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
}

In Interface Builder, select the buttons and change their class to RoundButton in the Identity Inspector. In the Attributes inspector, you should see the new corner radius attribute.

Do the same for the display label. Your keyboard should now look like this.

Conclusion

You should now be able to make a custom keyboard in iOS using the app extension APIs. Remember that every custom keyboard must have a way to switch to the next keyboard and that your keyboard cannot connect to the internet, access location services, or talk with its containing app by default, but you can request these capabilities.

The system will use the default keyboard for secure fields, such as password and phone number fields. Don't forget that the code for the custom keyboard lives in a separate target. Because of this the debug logs aren't visible. To see them, open the system log from the iOS Simulator.

2014-11-24T18:45:29.000Z2014-11-24T18:45:29.000ZAndrei Puni

How To Localize an Android Application

$
0
0

Localization is more than just the translation of the strings in your application in other languages. It also involves displaying data, such as dates and times, in the right format for your users. Even if you only want an English version of your application it's good to know what localization involves. That way you'll be ready if you decide to add support for additional locales and you won't have to look for strings and values all over your application's code base.

With the Android SDK, the language of the localized strings and the format used to display values depends on the locale of the device. The locale is a combination of the language and the country. The language is defined by the ISO 639-1 standard while the country is defined by the ISO 3166-1 standard. This means that the Android locale is compatible with the culture or locale used by most other programming languages and frameworks.

The only locale guaranteed to be present on every Android device is English as spoken in the United States. It is represented by the en_US code, with en being the language code for English and US being the country code for the United States. You can also use the enlanguage code onlyas the locale to represent all the English-speaking countries and regions.

To localize an application for a language or for a specific country and language, you need to include the resources for that locale in the /res folder of your application. This usually includes string resources in the /res/values folder, but it can also include other types of resources, such as images and layouts. The Android SDK identifies the various alternative resources with configuration qualifiers, which are then used to name the folder containing those resources. The configuration qualifier for the locale is the language code, optionally followed by the country code.So, the localized string resources for your application must be in the /res/values-<language code> or /res/values-<language code>-r<country code> folder.

You should keep in mind that the resources identified with a configuration qualifier for a specific locale have precedence over most other resources. Other common configuration qualifiers for resources with lower priority include screen size and screen pixel density to describe different layouts depending on the size of the screen. Only resources identified with a mobile country code (MCC) configuration qualifier have a greater priority than string resources identified with a locale configuration qualifier. The mobile country code is used to define resources for a country, which can optionally be followed with the mobile network code (MNC) from a SIM card to target a specific mobile provider in that country. It is used to provide content specific to a country like a contract or a privacy policy.

1. Localizing Strings

Every Android application should have all its string resources in the /res/values/strings.xml file. This allows for string reuse in your application even if no localization is needed. Also, if no file is available for the device's current locale this file is used. If you don't have one and your application tries to access a string resource that is only available in a strings.xml file specific to a locale, your application will crash without warning since resources are loaded at runtime and not during compilation.

A strings.xml file specific to a locale doesn't need to have all the string resources from the /res/values/strings.xml file. This means that if a resource, like the name of your application, doesn't need to be localized, you don't need to include it in every strings file.

At runtime, the /res/values-<language code>-r<country code>/strings.xml file for the locale of the device is checked followed by the /res/values-<language code>/strings.xml file for the language only. If the specified string is not available in those files, then the application falls back to the /res/values/strings.xml.

For example, if you want to localize your application for all the French-speaking regions and countries without targeting a specific one, you put the strings for the application in the /res/values-fr/strings.xml file. However, if you want to localize some strings with spelling specific to French as written in France, you have to put them in the /res/values-fr-rFr/strings.xml file. Make sure to add the r before the country code in the name of the folder or it won't be found at runtime when the application is localized.

Here is an example of a /res/values/strings.xml file with a few string resources in English:

<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">Android Localization Demo</string><string name="hello_world">Hello world!</string><string name="hello_world_name">Hello %s!</string></resources> 

And here is a /res/values-fr/strings.xml file with the same resources in French:

<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">Démo de localisation Android</string><string name="hello_world">Bonjour le monde!</string><string name="hello_world_name">Bonjour %s!</string></resources>

Localizing Strings in Layouts

The strings in your layouts should be string resources. In fact, a Lint warning is displayed if a string is hard-coded. You can use resources everywhere where you would set strings in layouts. String resources are identified by @string/ followed by the name of the resource from the strings.xml file. The following displays the hello_world string from the previous example in a TextView according to the locale of the device:

<TextView
        android:id="@+id/hello_world_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

This shows "Bonjour le monde!"on an Android device set to a locale that includes the French language. For all the other locales, the "Hello world!" string is shown.

Localizing Strings in Code

You will also need to localize strings from your code if you create or modify controls outside your layouts. To localize those strings you must first get the android.content.res.Resources object that contains all the resources in the package of the application for the current context. You then call the getString method on that object, passing in the identifier of the string to display. For example, the following code block shows you how to set the text of a TextView object:

// Get the translated string from the string resource
android.content.res.Resources res = context.getResources();
String helloWorld = res.getString(R.string.hello_world);

// Set the string to the textview
TextView helloTextView;
helloTextView = (TextView)findViewById(R.id.hello_world_textview);
helloTextView.setText(helloWorld);

More complex strings, with one or more variable components, you need to use format strings. Format strings let you to specify the values for parameters to display. The parameters in the format string are identified with format specifiers. The most commonly used format specifier is %s, representing a string. In fact, all the format strings from the java.util.Formatter class are available. Also, if multiple parameters are required, you must number your format specifiers. In the case of string format specifiers, the first should be %1$s, the second %2$s, and so on.

Once format strings are specified, you use the String.format method to pass in the parameters to display to your string. Here is how to set the text to show in a TextView with the hello_world_name format string from the strings.xml file. This format string includes a format specifier for the name of the user:

// Get the format string
android.content.res.Resources res = context.getResources();
String helloFormatString = res.getString(R.string.hello_world_name);
String userName = "Bob";

// Insert the name in the format string
String helloWorldName = String.format(helloFormatString, userName);

// Set text of the textview
TextView helloTextView;
helloTextView = (TextView)findViewById(R.id.hello_world_textview);
helloTextView.setText(helloWorldName);

This shows the "Bonjour Bob!"string for an Android device with a locale that includes the French language. For all the other locales the "Hello Bob!" string is shown.

2. Localizing Dates and Times

If the strings of your application are already localized, your application is already in pretty good shape. However, you may need to show dates and times to the user in many cases. Those dates and times should also be adapted to the current locale of the device. Fortunately, the Android SDK includes classes that format dates and times according to the locale.

In the Android SDK, dates and times are managed by the Date class from the java.util namespace. The current date and time of the device is returned by the java.util.Calendar.

Localizing Dates

Dates can be formatted using an instance of the DateFormat formatter from the java.text namespace. You must use the DateFormat class from the Android SDK in the android.text.format namespace to get the right formatter for the locale of the device. The following code snippet shows how to get a string with the current date formatted for the device's locale:

// Gets the current date and time
Date currentDate = Calendar.getInstance().getTime();

// Gets the standard date formatter for the current locale of 
// the device
java.text.DateFormat dateFormat;
dateFormat = android.text.format.DateFormat.getDateFormat(this);

// Formats the current date according to the locale
String formattedCurrentDate = dateFormat.format(currentDate);

If the current locale of the device is English as spoken in the United States, the string will contain the current date in a short date format, 11/30/2014. The DateFormat class includes a few other date formats. For example, the getLongDateFormat method returns a long date format, Sunday, November 30, 2014.

Localizing Times

Since times are represented as Date objects by the Android SDK, they must also be displayed using a formatter returned by the DateFormat class from the android.text.format namespace. The getTimeFormat method returns a format that only displays the time of a Date object. The following code snippet show how to get a string with the current time formatted for the device's locale:

// Gets the current date and time
java.util.Date currentDate = Calendar.getInstance().getTime();

// Gets a date formatter for the current locale of the device
// that shows times.
java.text.DateFormat timeFormat;
timeFormat = android.text.format.DateFormat.getTimeFormat(this);

// Formats the current time according to the locale
String formattedTime = timeFormat.format(currentDate);

If the current locale of the device is English as spoken in the United States, the string will contain the current time in the following format, 8:15 PM.

Conclusion

It's important to only localize the strings that are used in the application's user interface. For example, there's no need to localize debug messages that are only used during development or for diagnostic purposes. Also note that you shouldn't write localized dates and times to disk or to a database. Not only can the locale of a device change, working with localized dates and times makes development unnecessarily difficult and complex.


2014-11-26T15:25:26.000Z2014-11-26T15:25:26.000ZCindy Potvin

How To Localize an Android Application

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22154

Localization is more than just the translation of the strings in your application in other languages. It also involves displaying data, such as dates and times, in the right format for your users. Even if you only want an English version of your application it's good to know what localization involves. That way you'll be ready if you decide to add support for additional locales and you won't have to look for strings and values all over your application's code base.

With the Android SDK, the language of the localized strings and the format used to display values depends on the locale of the device. The locale is a combination of the language and the country. The language is defined by the ISO 639-1 standard while the country is defined by the ISO 3166-1 standard. This means that the Android locale is compatible with the culture or locale used by most other programming languages and frameworks.

The only locale guaranteed to be present on every Android device is English as spoken in the United States. It is represented by the en_US code, with en being the language code for English and US being the country code for the United States. You can also use the enlanguage code onlyas the locale to represent all the English-speaking countries and regions.

To localize an application for a language or for a specific country and language, you need to include the resources for that locale in the /res folder of your application. This usually includes string resources in the /res/values folder, but it can also include other types of resources, such as images and layouts. The Android SDK identifies the various alternative resources with configuration qualifiers, which are then used to name the folder containing those resources. The configuration qualifier for the locale is the language code, optionally followed by the country code.So, the localized string resources for your application must be in the /res/values-<language code> or /res/values-<language code>-r<country code> folder.

You should keep in mind that the resources identified with a configuration qualifier for a specific locale have precedence over most other resources. Other common configuration qualifiers for resources with lower priority include screen size and screen pixel density to describe different layouts depending on the size of the screen. Only resources identified with a mobile country code (MCC) configuration qualifier have a greater priority than string resources identified with a locale configuration qualifier. The mobile country code is used to define resources for a country, which can optionally be followed with the mobile network code (MNC) from a SIM card to target a specific mobile provider in that country. It is used to provide content specific to a country like a contract or a privacy policy.

1. Localizing Strings

Every Android application should have all its string resources in the /res/values/strings.xml file. This allows for string reuse in your application even if no localization is needed. Also, if no file is available for the device's current locale this file is used. If you don't have one and your application tries to access a string resource that is only available in a strings.xml file specific to a locale, your application will crash without warning since resources are loaded at runtime and not during compilation.

A strings.xml file specific to a locale doesn't need to have all the string resources from the /res/values/strings.xml file. This means that if a resource, like the name of your application, doesn't need to be localized, you don't need to include it in every strings file.

At runtime, the /res/values-<language code>-r<country code>/strings.xml file for the locale of the device is checked followed by the /res/values-<language code>/strings.xml file for the language only. If the specified string is not available in those files, then the application falls back to the /res/values/strings.xml.

For example, if you want to localize your application for all the French-speaking regions and countries without targeting a specific one, you put the strings for the application in the /res/values-fr/strings.xml file. However, if you want to localize some strings with spelling specific to French as written in France, you have to put them in the /res/values-fr-rFr/strings.xml file. Make sure to add the r before the country code in the name of the folder or it won't be found at runtime when the application is localized.

Here is an example of a /res/values/strings.xml file with a few string resources in English:

<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">Android Localization Demo</string><string name="hello_world">Hello world!</string><string name="hello_world_name">Hello %s!</string></resources> 

And here is a /res/values-fr/strings.xml file with the same resources in French:

<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">Démo de localisation Android</string><string name="hello_world">Bonjour le monde!</string><string name="hello_world_name">Bonjour %s!</string></resources>

Localizing Strings in Layouts

The strings in your layouts should be string resources. In fact, a Lint warning is displayed if a string is hard-coded. You can use resources everywhere where you would set strings in layouts. String resources are identified by @string/ followed by the name of the resource from the strings.xml file. The following displays the hello_world string from the previous example in a TextView according to the locale of the device:

<TextView
        android:id="@+id/hello_world_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

This shows "Bonjour le monde!"on an Android device set to a locale that includes the French language. For all the other locales, the "Hello world!" string is shown.

Localizing Strings in Code

You will also need to localize strings from your code if you create or modify controls outside your layouts. To localize those strings you must first get the android.content.res.Resources object that contains all the resources in the package of the application for the current context. You then call the getString method on that object, passing in the identifier of the string to display. For example, the following code block shows you how to set the text of a TextView object:

// Get the translated string from the string resource
android.content.res.Resources res = context.getResources();
String helloWorld = res.getString(R.string.hello_world);

// Set the string to the textview
TextView helloTextView;
helloTextView = (TextView)findViewById(R.id.hello_world_textview);
helloTextView.setText(helloWorld);

More complex strings, with one or more variable components, you need to use format strings. Format strings let you to specify the values for parameters to display. The parameters in the format string are identified with format specifiers. The most commonly used format specifier is %s, representing a string. In fact, all the format strings from the java.util.Formatter class are available. Also, if multiple parameters are required, you must number your format specifiers. In the case of string format specifiers, the first should be %1$s, the second %2$s, and so on.

Once format strings are specified, you use the String.format method to pass in the parameters to display to your string. Here is how to set the text to show in a TextView with the hello_world_name format string from the strings.xml file. This format string includes a format specifier for the name of the user:

// Get the format string
android.content.res.Resources res = context.getResources();
String helloFormatString = res.getString(R.string.hello_world_name);
String userName = "Bob";

// Insert the name in the format string
String helloWorldName = String.format(helloFormatString, userName);

// Set text of the textview
TextView helloTextView;
helloTextView = (TextView)findViewById(R.id.hello_world_textview);
helloTextView.setText(helloWorldName);

This shows the "Bonjour Bob!"string for an Android device with a locale that includes the French language. For all the other locales the "Hello Bob!" string is shown.

2. Localizing Dates and Times

If the strings of your application are already localized, your application is already in pretty good shape. However, you may need to show dates and times to the user in many cases. Those dates and times should also be adapted to the current locale of the device. Fortunately, the Android SDK includes classes that format dates and times according to the locale.

In the Android SDK, dates and times are managed by the Date class from the java.util namespace. The current date and time of the device is returned by the java.util.Calendar.

Localizing Dates

Dates can be formatted using an instance of the DateFormat formatter from the java.text namespace. You must use the DateFormat class from the Android SDK in the android.text.format namespace to get the right formatter for the locale of the device. The following code snippet shows how to get a string with the current date formatted for the device's locale:

// Gets the current date and time
Date currentDate = Calendar.getInstance().getTime();

// Gets the standard date formatter for the current locale of 
// the device
java.text.DateFormat dateFormat;
dateFormat = android.text.format.DateFormat.getDateFormat(this);

// Formats the current date according to the locale
String formattedCurrentDate = dateFormat.format(currentDate);

If the current locale of the device is English as spoken in the United States, the string will contain the current date in a short date format, 11/30/2014. The DateFormat class includes a few other date formats. For example, the getLongDateFormat method returns a long date format, Sunday, November 30, 2014.

Localizing Times

Since times are represented as Date objects by the Android SDK, they must also be displayed using a formatter returned by the DateFormat class from the android.text.format namespace. The getTimeFormat method returns a format that only displays the time of a Date object. The following code snippet show how to get a string with the current time formatted for the device's locale:

// Gets the current date and time
java.util.Date currentDate = Calendar.getInstance().getTime();

// Gets a date formatter for the current locale of the device
// that shows times.
java.text.DateFormat timeFormat;
timeFormat = android.text.format.DateFormat.getTimeFormat(this);

// Formats the current time according to the locale
String formattedTime = timeFormat.format(currentDate);

If the current locale of the device is English as spoken in the United States, the string will contain the current time in the following format, 8:15 PM.

Conclusion

It's important to only localize the strings that are used in the application's user interface. For example, there's no need to localize debug messages that are only used during development or for diagnostic purposes. Also note that you shouldn't write localized dates and times to disk or to a database. Not only can the locale of a device change, working with localized dates and times makes development unnecessarily difficult and complex.


2014-11-26T15:25:26.000Z2014-11-26T15:25:26.000ZCindy Potvin

An Introduction to Xamarin.Forms and Messaging

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22526

Ever since Xamarin 3 was introduced, there's been quite a bit of buzz around it. The idea of being able to create a mobile application, including the user interface, using a single code base is amazing. But Xamarin.Forms is so much more than that. One of the often overlooked features included in Xamarin.Forms is the concept of Messaging and that is the topic of this tutorial.

1. What is messaging?

If you have experience writing software in enterprise environments, you may also have experience with—or have at least heard of—messaging in your applications. Messaging is a term used to describe applications that take advantage of the publish/subscribe (pub/sub) design pattern. The publish/subscribe architecture is made up of three primary players:

  • messages
  • publishers
  • subscribers

The central focus of this process is around the messages. Messages are simply a way to transmit information about a certain event that has occurred. This event is typically tied to some sort of business process and it contains information required by systems that will ultimately use this data to perform some other operation.

The other two players, publishers and subscribers, are the purveyors of these messages. As the names imply, the publishers are the senders of the messages and the subscribers are the receivers of the messages.

How does messaging work?

The basic process of messaging is rather simple. Within a system, a certain event occurs. This event could be something simple, like a user clicking a button, or as complex as a business rule firing in a large financial firm's trading application. In the world of messaging, there is little difference between these two things. All you need to know is that something has happened.

When an event occurs, a publisher will take the information about this event and bundle it up into a message. This message will have some sort of type tied to it as well as additional data describing the event. The type is very important later in the process. Once this message is created, the publisher will send it out into some system. From that point, the publisher no longer cares about this event or message. There are some types of publishers that need to get a response, but that isn't very important in our case.

On the other end of the equation are the subscribers. Subscribers are the exact opposite of the publishers. They wait for messages of a certain type to be published. This is where their name comes from, they are subscribed to certain types of events. When a subscriber sees that an event to which they are subscribed has occurred, it will take this message and all of its data, and do something with it. Once this process is complete, the subscriber goes back to waiting for more messages.

Why should you use messaging?

With this architecture in mind, you may be wondering why or when you should use it. Typically, developers choose to use this type of architecture for two reasons, loose coupling and scalability.

Loose Coupling

In terms of software development, loose coupling is the concept of keeping different components within your application as separate as possible to the point that they know as little about each other as possible. This separation allows developers to focus more on the functionality of a system and less on the interaction of different components or pieces of the system. This allows for easier changes of functionality and testing of different components.

Scalability

Scalability has to do with how a system can grow without having to constantly rework and re-architect the entire solution. When it comes to messaging, if you need to do some sort of additional processing when a particular message is detected, a new subscriber is created to handle that functionality as opposed to having to open up an existing piece of code and make changes.

Now, let's see how we can put this extremely powerful architecture to use in our mobile applications using Xamarin.

2. Utilizing Messaging in Xamarin.Forms

Entering into the world of messaging in Xamarin.Forms is actually quite simple now that we understand the basic concepts of messaging. All we need now is a construct to access them. Luckily for us, there is only one class we really need to worry about and that is MessagingCenter. The MessagingCenter class is part of Xamarin.Forms and has methods to help us with both publishing and subscribing to messages. Let's take a look at each.

Publish

The first method we are going to take a look at in the MessagingCenter class is Send. Although the concept in a messaging context is publish, the Xamarin.Forms implementation uses Send. There are two versions of the Send method that can be used to publish data.

  • MessagingCenter.Send<TSender>(TSender sender, string message)
  • MessagingCenter.Send<TSender, TArgs>(TSender sender, string message, TArgs args)

Both of these options are generic. The first allows you to specify the sender of this message and the second also allows a secondary argument, the payload type.

It's important to point out that the TArgs generic parameter can be any type. It can be something simple like a string or as complex as a custom type.

Subscribe

Now that we understand how to publish, or Send, messages into the system, it's time for something to subscribe to them. To do that, we will be using the Subscribe method on the MessagingCenter class.

Similar to the two versions of the Send method, the Subscribe method also has two overloads.

  • MessagingCenter.Subscribe<TSender>(object subscriber, string message, Action<TSender> callback, TSender sender = null)
  • MessagingCenter.Subscribe<TSender, TArgs>(object subscriber, string message, Action<TSender, TArgs> callback, TSender sender = null)

For the Subscribe methods, we are specifying who the subscriber is. Typically we will be specifying this for the first parameter. The second parameter will contain the same message name that was specified in the Send method so we can be notified when that particular message has been sent. The third parameter is a generic delegate that accepts the sender, and possibly args, depending on which version of the method is used. The delegate is a callback that is executed when a message of this type is published. Finally is a nullable parameter specifying which TSender this message should be subscribed for. It can be left as null to accept this type of message from any sender.

Great, now we are subscribing to messages, but what if we don't care about certain messages anymore? Good question. There is one more method to take into consideration.

Unsubscribe

An often overlooked process in the world of messaging is unsubscribing. When you are working in a very large enterprise class infrastructure, maybe it isn't quite as important. In contrast, when you are working in a smaller environment, such as a phone, it becomes more important.

Even though using the messaging architecture allows more flexibility and scalability, it still takes system resources. Because of this, we can't continue to increase the number of subscribers in a system infinitely. That being the case, we need to be respectful of resources as best we can. In Xamarin.Forms, the way that we do this is through calling the Unsubscribe method. This method allows us to say that we no longer care about a particular message being published out to the system.

The Unsubscribe method also has two overloads.

  • MessagingCenter.Unsubscribe<TSender>(object subscriber, string message)
  • MessagingCenter.Unsubscribe<TSender, TArgs>(object subscriber, string message)

You may notice that the generic arguments don't manifest themselves in the parameter list. Only the developers have a good idea as to why exactly, but I feel that it is probably more of a formality. Either way, I choose to be consistent across all my calls to Send, Subscribe, and Unsubscribe, and use the same signatures and arguments to eliminate any sort of confusion.

Now that we understand the basic concepts of messaging, it's time to create a simple example utilizing these ideas.

3. Creating an Example

In this example, we will create a simple Xamarin.Forms app that will use all three methods of the MessagingCenter class mentioned previously in this article. The app itself may seem rather inconsequential, but it will provide a useful illustration of how to use these concepts in your apps going forward.

Step 1: Create a Xamarin.Forms App

We'll start by creating a new Xamarin.Forms app. To accomplish this, simply open Xamarin Studio (or Visual Studio) and select File > New Solution. In the New Solution dialog, select the Mobile Apps template family and choose one of the templates. I will select the PCL version, but you could use the Shared Project version if you like.

Step 2: Add Some Code

Once the solution is created, let's add some code. In the shared project, create a new class and name it MainPage. This will be the screen in the application that will contain the user interface as well as the logic. This can obviously be broken out into more logical pieces, but this application is simple enough that I didn't think it was necessary.

Replace the contents of the MainPage.cs file with the following:

using System;
using Xamarin.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace MessagingSample
{
    public class MainPage : ContentPage
	{
		private List<string> _eventTimes;
		private bool _isSubscribed = false;
		private ListView _eventList;

		public MainPage ()
		{
			_eventTimes = new List<string> ();

			var clearButton = new Button {
				Text = "Clear"
			};

			clearButton.Clicked += (sender, e) => {
				_eventTimes.Clear();
				UpdateList();
			};

			var publishButton = new Button {
				Text = "Publish"
			};

			publishButton.Clicked += (sender, e) => {
				MessagingCenter.Send<MainPage, DateTime>(this, "boom", DateTime.Now);
			};

			var subUnsubButton = new Button {
				Text = "Subscribe"
			};

			subUnsubButton.Clicked += (sender, e) => {
				_isSubscribed = !_isSubscribed;

				if(_isSubscribed) {
					subUnsubButton.Text = "Unsubscribe";
					MessagingCenter.Subscribe<MainPage, DateTime>(this, "boom", (page, time) => {
						_eventTimes.Add(time.ToString());
						UpdateList();
					});
				}else {
					subUnsubButton.Text = "Subscribe";
					MessagingCenter.Unsubscribe<MainPage, DateTime>(this, "boom");
				}
			};

			var buttonStack = new StackLayout {
				Spacing = 20,
				Padding = 20,
				Orientation = StackOrientation.Horizontal,
				Children = { publishButton, subUnsubButton, clearButton },
				HorizontalOptions = LayoutOptions.CenterAndExpand
			};

			_eventList = new ListView {
				ItemsSource = new ObservableCollection<string>(_eventTimes)
			};

			var mainStackLayout = new StackLayout {
				Children = { buttonStack, _eventList },
				HorizontalOptions = LayoutOptions.FillAndExpand,
				VerticalOptions = LayoutOptions.FillAndExpand
			};

			Content = mainStackLayout;
		}

		private void UpdateList() {
			_eventList.ItemsSource = new ObservableCollection<string> (_eventTimes);
		}
	}
}

This may initially seem a little overwhelming, but I assure you that it is quite simple. There are three buttons:

  • Publish: uses the Send method to publish a message with a name of boom
  • Subscribe/Unsubscribe: flips the page from being subscribed/unsubscribed from boom
  • Clear: clears the contents of ListView

The basic premise of this application is that it starts in an unsubscribed state. In this state, if you tap the Publish button, nothing seems to happen. We know that the boom message is being published, but since there are no subscribers, nothing happens.

Once you tap the Subscribe button, the application goes into a subscribed state in which it is now listening for the boom message. When it receives this message, it places a new DateTime value into the _eventTimes collection, which gets updated into the _eventListListView via an ObservableCollection.

The rest of the code is just some basic formatting and spacing. 

Step 3: Build and Run

Running the application in the iOS Simulator should look similar to this:

Similarly, the initial state in the Android Emulator should look like this:

After tapping the Subscribe button and publishing a few messages, you should begin to see the user interface being updated by getting some DateTime values in the ListView.

Conclusion

There you have it. You have successfully created a Xamarin.Forms app that takes advantage of an architecture that is typically reserved for large enterprises. You should feel rather excited at this point as you now have a new tool in your tool belt that will allow you to create very scalable and flexible—not to mention testable—applications that can run in the hand of the end user. And that can open the door to many more possibilities.

2014-11-29T15:00:51.000Z2014-11-29T15:00:51.000ZDerek Jensen

This Is What You Need to Know About WacthKit

$
0
0

In November, Apple did what it promised and released WatchKit to third-party developers. Not only did it release WatchKit, it provided developers with a number of resources, including Human Interface Guidelines for Apple Watch applications. In this article, I will tell you what you need to know about WatchKit and Apple Watch applications.

What is WatchKit?

You may think that WatchKit is a framework that contains everything you need to create an Apple Watch application, but that is only part of the story. The story is a bit more complex, because an Apple Watch application isn't what you may think it is.

WatchKit is an integral part of the Apple Watch experience for third-party applications. It contains the classes for creating Apple Watch applications and it ensures that an Apple Watch application can talk to its corresponding WatchKit extension. Wait. What? Didn't I tell you the story is a bit complicated?

Architecture

One of the most important concepts to grasp about Apple Watch applications is that an Apple Watch application requires an iPhone to do its work. To understand this concept, we need to take a closer look at the architecture of an Apple Watch application.

A third-party Apple Watch application can only do its work if the user's Apple Watch is paired with the their iPhone. The Apple Watch and the iPhone talk to each other using Bluetooth and the WatchKit framework. The following diagram summarizes this architecture.

As you can see in the above diagram, the WatchKit framework is responsible for the communication between the Apple Watch and the paired iPhone. But how does this work?

To put it simply, Apple Watch is only responsible for:

  • presenting the user interface
  • intercepting touch events

The Apple Watch application in the above diagram contains the application's storyboard and the static resources the Apple Watch application needs to present its user interface.

The WatchKit extension lives and runs on the iPhone and is part of the iOS application the user has installed on their iPhone. The previous sentence touches an important detail, that is, an Apple Watch application is nothing more than an extension of an existing iOS application. Let's explore this in more detail.

What is an Apple Watch application?

You may be wondering what an Apple Watch application is and how you can create one. Strictly speaking, an Apple Watch application is the component that runs on Apple Watch. An Apple Watch application presents the user interface and intercepts touch events.

This means that an Apple Watch application is unusable without the paired device on which the corresponding WatchKit extension runs. The Apple Watch application and WatchKit extension are part of the iOS application the user installs on their iPhone.

iPhone Required

As I mentioned earlier, it is key to understand that third-party Apple Watch applications require an iPhone and are unusable without a paired iPhone on which the corresponding WatchKit extension lives.

The good news is that the WatchKit framework takes care of the nitty-gritty details. It automatically pairs iPhone and Apple Watch, and handles the communication between the Apple Watch application and the WatchKit extension. Communication between iPhone and Apple Watch is handled by Bluetooth.

WatchKit Extension

When the user launches an Apple Watch application from the home screen, the paired iPhone launches the WatchKit extension for that application. Any user interaction is forwarded to the WatchKit extension and it is the latter's responsibility to decide how the Apple Watch application should respond to user interaction.

Apple Watch Application

Only the storyboard and static resources of the Apple Watch application are installed on the Apple Watch. As I mentioned a moment ago, none of the business logic is handled on the Apple Watch.

While this may seem like a major limitation, it certainly has advantages. For example, the Apple Watch application has, through the WatchKit extension, access to the data of the corresponding iOS application. The Apple Watch application can also take advantage of the device capabilities of the iPhone, such as location monitoring and network connectivity.

Installation

Installing an Apple Watch application is trivial. Whenever the user installs an iOS application that supports Apple Watch, the user is asked if it wishes to install the corresponding Apple Watch application. It's that simple.

Glances and Notifications

During Apple's keynote in september, you may have heard about glances and notifications. An Apple Watch application can present itself to the user in three ways.

Application

The most obvious representation of an Apple Watch application is when the user launches the application form the Apple Watch home screen. This launches the user interface of the Apple Watch application and lets the user interact with it.

Glances

In terms of functionality, a glance is similar to a today extension on iOS. It shows the user relevant information about the Apple Watch extension. The main difference with a today extension is that glances are read-only. Also note that it's perfectly possible to create an Apple Watch application without support for glances.

Notifications

In addition to the default user interface of local and remote notifications, it's possible for Apple Watch applications to include a custom user interface for local and remote notifications. As with glances, this is optional for Apple Watch applications.

Navigation & Interaction

The Apple Watch has a tiny screen compared to the iPhone 6 and 6 Plus. The result is that navigation and user interaction is different and more limited on Apple Watch.

Navigation

The WatchKit framework currently includes two types of navigation, hierarchical and page-based navigation. Hierarchical navigation is very similar to how UINavigationController handles navigation on iOS while page-based navigation is similar to the way UIPageViewController lets the user scroll between pages of content.

User Interaction

The more you learn about and play with WatchKit, the more you notice how different it is from iOS and the paradigms defined by the iOS ecosystem. User interaction, for example, is quite different. The WatchKit framework doesn't allow for complex gesture detection and it's also not possible to use custom gesture recognizers.

Understanding user interaction on Apple Watch is pretty straightforward. There are five types of user interaction:

  • Taps: The user taps the screen to select a table row.
  • Vertical Swipe: The user scrolls the contents of a table.
  • Horizontal Swipe: The user navigates between pages in a page-based navigation.
  • Left Edge Swipe: As on iOS, it is possible to navigate back to the previous screen by swiping from the left edge of the screen to the right.
  • Force Touch: Unique to Apple Watch, the user can press or force touch the screen to display a contextual menu. The display of Apple Watch not only detects touch, but it also detects force.

Digital Crown

In addition to manipulating the content on the screen, the user can also interact with the Apple Watch using its Digital Crown. With regards to third-party applications, the Digital Crown can only be used to scroll through content.

Limitations

There are a number of limitations when it comes to developing Apple Watch applications. We already learned that user interaction and navigation isn't as powerful on Apple Watch as it is on iOS and there are a few more restrictions you need to be aware of.

Animations, for example, aren't as powerful or easy to implement on Apple Watch. Animations are created by displaying a sequence of images to the user. Dynamically adding and removing views is also something that isn't possible on Apple Watch. In fact, views are non-existent on Apple Watch and the same is true for auto layout.

Instead, the WatchKit framework exposes the WKInterfaceController class, which manages one screen of content. The controls used on Apple Watch are also different than those used on iOS. There are many similarities, but it will require some getting used to.

These limitations shouldn't be a surprise. Apple Watch is different from the iOS ecosystem and it is understandable that the patterns and paradigms for each are different.

Resources

If you want to learn more about WatchKit, then you have a number of options. I recommend you first watch Apple's introductory video. If you want to dive deeper into the WatchKit framework, then the WatchKit Programming Guide is your next stop. Designers may be interested in browsing the Apple Watch Human Interface Guidelines.

If you're eager to get started, then I encourage you to download Xcode 6.2 and get your hands dirty with WatchKit. Xcode's simulator supports Apple Watch so there's nothing stopping you from creating your first Apple Watch application today.

Conclusion

Apple has inundated developers with information and resources to go through. While the WatchKit framework and its documentation are subject to change, it is already clear what possibilities Apple Watch opens up to developers. My suggestion is to download Xcode 6.2 and begin experimenting with the WatchKit framework today.

2014-11-30T15:30:47.000Z2014-11-30T15:30:47.000ZBart Jacobs

This Is What You Need to Know About WatchKit

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22784

In November, Apple did what it promised and released WatchKit to third-party developers. Not only did it release WatchKit, it provided developers with a number of resources, including Human Interface Guidelines for Apple Watch applications. In this article, I will tell you what you need to know about WatchKit and Apple Watch applications.

What is WatchKit?

You may think that WatchKit is a framework that contains everything you need to create an Apple Watch application, but that is only part of the story. The story is a bit more complex, because an Apple Watch application isn't what you may think it is.

WatchKit is an integral part of the Apple Watch experience for third-party applications. It contains the classes for creating Apple Watch applications and it ensures that an Apple Watch application can talk to its corresponding WatchKit extension. Wait. What? Didn't I tell you the story is a bit complicated?

Architecture

One of the most important concepts to grasp about Apple Watch applications is that an Apple Watch application requires an iPhone to do its work. To understand this concept, we need to take a closer look at the architecture of an Apple Watch application.

A third-party Apple Watch application can only do its work if the user's Apple Watch is paired with the their iPhone. The Apple Watch and the iPhone talk to each other using Bluetooth and the WatchKit framework. The following diagram summarizes this architecture.

As you can see in the above diagram, the WatchKit framework is responsible for the communication between the Apple Watch and the paired iPhone. But how does this work?

To put it simply, Apple Watch is only responsible for:

  • presenting the user interface
  • intercepting touch events

The Apple Watch application in the above diagram contains the application's storyboard and the static resources the Apple Watch application needs to present its user interface.

The WatchKit extension lives and runs on the iPhone and is part of the iOS application the user has installed on their iPhone. The previous sentence touches an important detail, that is, an Apple Watch application is nothing more than an extension of an existing iOS application. Let's explore this in more detail.

What is an Apple Watch application?

You may be wondering what an Apple Watch application is and how you can create one. Strictly speaking, an Apple Watch application is the component that runs on Apple Watch. An Apple Watch application presents the user interface and intercepts touch events.

This means that an Apple Watch application is unusable without the paired device on which the corresponding WatchKit extension runs. The Apple Watch application and WatchKit extension are part of the iOS application the user installs on their iPhone.

iPhone Required

As I mentioned earlier, it is key to understand that third-party Apple Watch applications require an iPhone and are unusable without a paired iPhone on which the corresponding WatchKit extension lives.

The good news is that the WatchKit framework takes care of the nitty-gritty details. It automatically pairs iPhone and Apple Watch, and handles the communication between the Apple Watch application and the WatchKit extension. Communication between iPhone and Apple Watch is handled by Bluetooth.

WatchKit Extension

When the user launches an Apple Watch application from the home screen, the paired iPhone launches the WatchKit extension for that application. Any user interaction is forwarded to the WatchKit extension and it is the latter's responsibility to decide how the Apple Watch application should respond to user interaction.

Apple Watch Application

Only the storyboard and static resources of the Apple Watch application are installed on the Apple Watch. As I mentioned a moment ago, none of the business logic is handled on the Apple Watch.

While this may seem like a major limitation, it certainly has advantages. For example, the Apple Watch application has, through the WatchKit extension, access to the data of the corresponding iOS application. The Apple Watch application can also take advantage of the device capabilities of the iPhone, such as location monitoring and network connectivity.

Installation

Installing an Apple Watch application is trivial. Whenever the user installs an iOS application that supports Apple Watch, the user is asked if it wishes to install the corresponding Apple Watch application. It's that simple.

Glances and Notifications

During Apple's keynote in september, you may have heard about glances and notifications. An Apple Watch application can present itself to the user in three ways.

Application

The most obvious representation of an Apple Watch application is when the user launches the application form the Apple Watch home screen. This launches the user interface of the Apple Watch application and lets the user interact with it.

Glances

In terms of functionality, a glance is similar to a today extension on iOS. It shows the user relevant information about the Apple Watch extension. The main difference with a today extension is that glances are read-only. Also note that it's perfectly possible to create an Apple Watch application without support for glances.

Notifications

In addition to the default user interface of local and remote notifications, it's possible for Apple Watch applications to include a custom user interface for local and remote notifications. As with glances, this is optional for Apple Watch applications.

Navigation & Interaction

The Apple Watch has a tiny screen compared to the iPhone 6 and 6 Plus. The result is that navigation and user interaction is different and more limited on Apple Watch.

Navigation

The WatchKit framework currently includes two types of navigation, hierarchical and page-based navigation. Hierarchical navigation is very similar to how UINavigationController handles navigation on iOS while page-based navigation is similar to the way UIPageViewController lets the user scroll between pages of content.

User Interaction

The more you learn about and play with WatchKit, the more you notice how different it is from iOS and the paradigms defined by the iOS ecosystem. User interaction, for example, is quite different. The WatchKit framework doesn't allow for complex gesture detection and it's also not possible to use custom gesture recognizers.

Understanding user interaction on Apple Watch is pretty straightforward. There are five types of user interaction:

  • Taps: The user taps the screen to select a table row.
  • Vertical Swipe: The user scrolls the contents of a table.
  • Horizontal Swipe: The user navigates between pages in a page-based navigation.
  • Left Edge Swipe: As on iOS, it is possible to navigate back to the previous screen by swiping from the left edge of the screen to the right.
  • Force Touch: Unique to Apple Watch, the user can press or force touch the screen to display a contextual menu. The display of Apple Watch not only detects touch, but it also detects force.

Digital Crown

In addition to manipulating the content on the screen, the user can also interact with the Apple Watch using its Digital Crown. With regards to third-party applications, the Digital Crown can only be used to scroll through content.

Limitations

There are a number of limitations when it comes to developing Apple Watch applications. We already learned that user interaction and navigation isn't as powerful on Apple Watch as it is on iOS and there are a few more restrictions you need to be aware of.

Animations, for example, aren't as powerful or easy to implement on Apple Watch. Animations are created by displaying a sequence of images to the user. Dynamically adding and removing views is also something that isn't possible on Apple Watch. In fact, views are non-existent on Apple Watch and the same is true for auto layout.

Instead, the WatchKit framework exposes the WKInterfaceController class, which manages one screen of content. The controls used on Apple Watch are also different than those used on iOS. There are many similarities, but it will require some getting used to.

These limitations shouldn't be a surprise. Apple Watch is different from the iOS ecosystem and it is understandable that the patterns and paradigms for each are different.

Resources

If you want to learn more about WatchKit, then you have a number of options. I recommend you first watch Apple's introductory video. If you want to dive deeper into the WatchKit framework, then the WatchKit Programming Guide is your next stop. Designers may be interested in browsing the Apple Watch Human Interface Guidelines.

If you're eager to get started, then I encourage you to download Xcode 6.2 and get your hands dirty with WatchKit. Xcode's simulator supports Apple Watch so there's nothing stopping you from creating your first Apple Watch application today.

Conclusion

Apple has inundated developers with information and resources to go through. While the WatchKit framework and its documentation are subject to change, it is already clear what possibilities Apple Watch opens up to developers. My suggestion is to download Xcode 6.2 and begin experimenting with the WatchKit framework today.

2014-11-30T15:30:47.000Z2014-11-30T15:30:47.000ZBart Jacobs

Create A Custom Keyboard on Android

$
0
0
Final product image
What You'll Be Creating

Most Android devices don't have a physical keyboard. Instead, they rely on a virtual or soft keyboard to accept user input. If you're into Android personalization, knowing how to build a custom, soft keyboard can take your hobby to a whole new level.

Using the Android SDK, you can quickly create a soft keyboard with surprisingly few lines of code, because the SDK takes care of a lot of the low level tasks, such as recognizing key touches, drawing the keyboard, and establishing connections between the keyboard and input fields.

In this tutorial, you will learn how to create a fully functional soft keyboard that can serve as your Android device's default keyboard.

1. Prerequisites

You will need the Eclipse ADT Bundle installed. You can download it from the Android Developer website.

2. Create a New Project

Fire up Eclipse and create a new Android application. Call this application, SimpleKeyboard. Make sure you choose a unique package name. Set the minimum required SDK to Android 2.2 and set the target SDK to Android4.4.

This app will have no activities so deselect Create Activity and click Finish.

3. Edit the Manifest

A soft keyboard is considered as an Input Method Editor (IME) by the Android operating system. An IME is declared as a Service in AndroidManifest.xml that uses the BIND_INPUT_METHOD permission, and responds to the action android.view.InputMethod.

Add the following lines to the application tag of the manifest:

<service android:name=".SimpleIME"
    android:label="@string/simple_ime"
    android:permission="android.permission.BIND_INPUT_METHOD"><meta-data android:name="android.view.im" android:resource="@xml/method"/><intent-filter><action android:name="android.view.InputMethod" /></intent-filter>            </service>

4. Create method.xml

The service tag in the manifest file containes a meta-data tag that references an XML file named method.xml. Without this file, the Android operating system won't recognize our Service as a valid IME service. The file contains details about the input method and its subtypes. For our keyboard, we define a single subtype for the en_US locale. Create the directory res/xml if it doesn't exist, and add the file method.xml to it. The contents of the file should be:

<?xml version="1.0" encoding="utf-8"?><input-method xmlns:android="http://schemas.android.com/apk/res/android"> <subtype
        android:label="@string/subtype_en_US"		
		android:imeSubtypeLocale="en_US"
		android:imeSubtypeMode="keyboard" /></input-method>

5. Edit strings.xml

The strings that this app uses are defined in the res/values/strings.xml file. We're going to need three strings:

  • the name of the app
  • the label of the IME
  • the label of the IME's subtype

Update your strings.xml so that it has the following contents:

<resources><string name="app_name">SimpleKeyboard</string><string name="simple_ime">Simple IME</string><string name="subtype_en_US">English (US)</string></resources>

6. Define the Keyboard Layout

The layout of our keyboard contains only a KeyboardView. The layout_alignParentBottom attribute is set to true so that keyboard appears at the bottom of the screen.

Create a file named res/layout/keyboard.xml and replace its contents with the following:

<?xml version="1.0" encoding="UTF-8"?><android.inputmethodservice.KeyboardView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/keyboard"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:keyPreviewLayout ="@layout/preview"
/>

The keyPreviewLayout is the layout of the short-lived pop-up that shows up whenever a key on the keyboard is pressed. It contains a single TextView. Create a file named res/layout/preview.xml and add the following to it:

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="#ffff00"    
    android:textStyle="bold"
    android:textSize="30sp">    </TextView>

6. Define the Keyboard Keys

The details of the keyboard keys and their positions are specified in an XML file. Every key has the following attributes:

  • keyLabel: This attribute contains the text that is displayed on the key.
  • codes: This attribute contains the unicode values of the characters that the key represents.

For example, to define a key for the letter A, the codes attribute should have the value 97 and the keyLabel attribute should be set to A.

If more than one code is associated with a key, then the character that the key represents will depend on the number of taps the key receives. For example, if a key has the codes 63, 33, and 58:

  • a single tap on the key results in the character ?
  • two taps in quick succession results in the character !
  • three taps in quick succession results in the character :

A key can also have a few optional attributes:

  • keyEdgeFlags: This attribute can take the value left or right. This attribute is usually added to the leftmost and rightmost keys of a row.
  • keyWidth: This attribute defines the width of a key. It's usually defined as a percentage value.
  • isRepeatable: If this attribute is set to true, long-pressing the key will repeat the action of the key multiple times. It is usually set to true for the delete and spacebar keys.

The keys of a keyboard are grouped as rows. It's good practice to limit the number of keys on a row to a maximum of ten, with each key having a width equal to 10% of the keyboard. The height of the keys is set to 60dp in this tutorial. This value can be adjusted, but values less than 48dp are not recommended. Our keyboard will have five rows of keys.

We can now go ahead and design the keyboard. Create a new file named res/xml/qwerty.xml and replace its contents with the following:

<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="10%p"
	android:horizontalGap="0px"
	android:verticalGap="0px"	
	android:keyHeight="60dp"><Row><Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/><Key android:codes="50" android:keyLabel="2"/><Key android:codes="51" android:keyLabel="3"/><Key android:codes="52" android:keyLabel="4"/><Key android:codes="53" android:keyLabel="5"/><Key android:codes="54" android:keyLabel="6"/><Key android:codes="55" android:keyLabel="7"/><Key android:codes="56" android:keyLabel="8"/><Key android:codes="57" android:keyLabel="9"/><Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/></Row><Row><Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/><Key android:codes="119" android:keyLabel="w"/><Key android:codes="101" android:keyLabel="e"/><Key android:codes="114" android:keyLabel="r"/><Key android:codes="116" android:keyLabel="t"/><Key android:codes="121" android:keyLabel="y"/><Key android:codes="117" android:keyLabel="u"/><Key android:codes="105" android:keyLabel="i"/><Key android:codes="111" android:keyLabel="o"/><Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/></Row><Row><Key android:codes="97" android:keyLabel="a" android:keyEdgeFlags="left"/><Key android:codes="115" android:keyLabel="s"/><Key android:codes="100" android:keyLabel="d"/><Key android:codes="102" android:keyLabel="f"/><Key android:codes="103" android:keyLabel="g"/><Key android:codes="104" android:keyLabel="h"/><Key android:codes="106" android:keyLabel="j"/><Key android:codes="107" android:keyLabel="k"/>		<Key android:codes="108" android:keyLabel="l"/><Key android:codes="35,64" android:keyLabel="\# \@" android:keyEdgeFlags="right"/></Row><Row><Key android:codes="-1" android:keyLabel="CAPS" android:keyEdgeFlags="left"/><Key android:codes="122" android:keyLabel="z"/><Key android:codes="120" android:keyLabel="x"/><Key android:codes="99" android:keyLabel="c"/><Key android:codes="118" android:keyLabel="v"/><Key android:codes="98" android:keyLabel="b"/><Key android:codes="110" android:keyLabel="n"/><Key android:codes="109" android:keyLabel="m"/><Key android:codes="46" android:keyLabel="."/><Key android:codes="63,33,58" android:keyLabel="\? ! :" android:keyEdgeFlags="right"/>	</Row><Row android:rowEdgeFlags="bottom"><Key android:codes="44" android:keyLabel="," android:keyWidth="10%p"  android:keyEdgeFlags="left"/>	<Key android:codes="47" android:keyLabel="/" android:keyWidth="10%p" /><Key android:codes="32" android:keyLabel="SPACE" android:keyWidth="40%p" android:isRepeatable="true"/>		<Key android:codes="-5" android:keyLabel="DEL" android:keyWidth="20%p" android:isRepeatable="true"/><Key android:codes="-4" android:keyLabel="DONE" android:keyWidth="20%p" android:keyEdgeFlags="right"/></Row>	</Keyboard>

You may have noticed that some keys have negative values for the codes attribute. Negative values are equal to predefined constants in the Keyboard class. For example, the value -5 is equal to the value of Keyboard.KEYCODE_DELETE.

7. Create a Service Class

Create a new Java class and call it SimpleIME.java. The class should extend InputMethodService class and implement the OnKeyboardActionListener interface. The OnKeyboardActionListener interface contains the methods that are called when keys of the soft keyboard are tapped or pressed.

The SimpleIME class should have three member variables:

  • KeyboardView referencing the view defined in the layout
  • Keyboard instance that is assigned to the KeyboardView
  • boolean telling us if the caps lock is enabled

After declaring these variables and adding the methods of the OnKeyboardActionListener interface, the SimpleIME class should look like this:

public class SimpleIME extends InputMethodService
    implements OnKeyboardActionListener{
	private KeyboardView kv;
	private Keyboard keyboard;
	private boolean caps = false;

	@Override
	public void onKey(int primaryCode, int[] keyCodes) {		

	}

	@Override
	public void onPress(int primaryCode) {
	}

	@Override
	public void onRelease(int primaryCode) { 			
	}

	@Override
	public void onText(CharSequence text) {		
	}

	@Override
	public void swipeDown() {	
	}

	@Override
	public void swipeLeft() {
	}

	@Override
	public void swipeRight() {
	}

	@Override
	public void swipeUp() {
	}
}

When the keyboard is created, the onCreateInputView method is called. All the member variables of the Service can be initialized here. Update the implementation of the onCreateInputView method as shown below:

@Override
public View onCreateInputView() {
	kv = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard, null);
	keyboard = new Keyboard(this, R.xml.qwerty);
	kv.setKeyboard(keyboard);
	kv.setOnKeyboardActionListener(this);
	return kv;
}

Next, we create a method that plays a sound when a key is pressed. We use the AudioManager class to play the sounds. The Android SDK includes a few default sound effects for key presses and those are used in the playClick method.

private void playClick(int keyCode){
	AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
	switch(keyCode){
	case 32: 
		am.playSoundEffect(AudioManager.FX_KEYPRESS_SPACEBAR);
		break;
	case Keyboard.KEYCODE_DONE:
	case 10: 
		am.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN);
		break;
	case Keyboard.KEYCODE_DELETE:
		am.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE);
		break;				
	default: am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
	}		
}

Finally, update the onKey method so that our keyboard app can communicate with input fields (usually EditText views) of other applications.

The getCurrentInputConnection method is used to get a connection to the input field of another application. Once we have the connection, we can use the following methods:

  • commitText to add one or more characters to the input field
  • deleteSurroundingText to delete one or more characters of the input field
  • sendKeyEvent to send events, like KEYCODE_ENTER, to the external application

Whenever a user presses a key on the soft keyboard, the onKey method is called with the unicode value of the key as one of its parameters. Based on this value, the keyboard  performs one of the following actions:

  • If the code is KEYCODE_DELETE, one character to the left of the cursor is deleted using the deleteSurroundingText method.
  • If the code is KEYCODE_DONE, a KEYCODE_ENTER key event is fired.
  • If the code is KEYCODE_SHIFT, the value of the caps variable is changed and the shift state of the keyboard is updated using the setShifted method. The keyboard needs to be redrawn when the state changes so that the labels of the keys are updated. The invalidateAllKeys method is used to redraw all keys.
  • For all other codes, the code is simply converted into a character and sent to the input field. If the code represents a letter of the alphabet and the caps variable is set to true, then the character is converted to uppercase.

Update the onKey method so that it looks like this:

@Override
public void onKey(int primaryCode, int[] keyCodes) {        
    InputConnection ic = getCurrentInputConnection();
    playClick(primaryCode);
    switch(primaryCode){
    case Keyboard.KEYCODE_DELETE :
        ic.deleteSurroundingText(1, 0);
        break;
    case Keyboard.KEYCODE_SHIFT:
        caps = !caps;
        keyboard.setShifted(caps);
        kv.invalidateAllKeys();
        break;
    case Keyboard.KEYCODE_DONE:
        ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
        break;
    default:
        char code = (char)primaryCode;
        if(Character.isLetter(code) && caps){
            code = Character.toUpperCase(code);
        }
        ic.commitText(String.valueOf(code),1);                  
    }
}

8. Testing the Keyboard

The soft keyboard is now ready to be tested. Compile and run it on an Android device. This app doesn't have an Activity, which means that it won't show up in the launcher. To use it, it should first be activated in the device's Settings.

After activating Simple IME, open any app that allows text input (for example, any messaging app) and click on one of its input fields. You should see a keyboard icon appear in the notifications area. Depending on your device, you can either click on that icon or drag the notification bar down and select Simple IME as the input method. You should now be able to type using your new keyboard.

Conclusion

In this tutorial, you have learned how to create a custom keyboard app from scratch. To change the look and feel of your keyboard, all you have to do is add extra styling to the res/layout/keyboard.xml and res/layout/preview.xml files. To change the positions of the keys, update the res/xml/qwerty.xml file. To add more features to your keyboard, refer to the developer documentation.


2014-12-01T16:45:22.000Z2014-12-01T16:45:22.000ZAshraff Hathibelagal

Create A Custom Keyboard on Android

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22615
Final product image
What You'll Be Creating

Most Android devices don't have a physical keyboard. Instead, they rely on a virtual or soft keyboard to accept user input. If you're into Android personalization, knowing how to build a custom, soft keyboard can take your hobby to a whole new level.

Using the Android SDK, you can quickly create a soft keyboard with surprisingly few lines of code, because the SDK takes care of a lot of the low level tasks, such as recognizing key touches, drawing the keyboard, and establishing connections between the keyboard and input fields.

In this tutorial, you will learn how to create a fully functional soft keyboard that can serve as your Android device's default keyboard.

1. Prerequisites

You will need the Eclipse ADT Bundle installed. You can download it from the Android Developer website.

2. Create a New Project

Fire up Eclipse and create a new Android application. Call this application, SimpleKeyboard. Make sure you choose a unique package name. Set the minimum required SDK to Android 2.2 and set the target SDK to Android4.4.

This app will have no activities so deselect Create Activity and click Finish.

3. Edit the Manifest

A soft keyboard is considered as an Input Method Editor (IME) by the Android operating system. An IME is declared as a Service in AndroidManifest.xml that uses the BIND_INPUT_METHOD permission, and responds to the action android.view.InputMethod.

Add the following lines to the application tag of the manifest:

<service android:name=".SimpleIME"
    android:label="@string/simple_ime"
    android:permission="android.permission.BIND_INPUT_METHOD"><meta-data android:name="android.view.im" android:resource="@xml/method"/><intent-filter><action android:name="android.view.InputMethod" /></intent-filter>            </service>

4. Create method.xml

The service tag in the manifest file containes a meta-data tag that references an XML file named method.xml. Without this file, the Android operating system won't recognize our Service as a valid IME service. The file contains details about the input method and its subtypes. For our keyboard, we define a single subtype for the en_US locale. Create the directory res/xml if it doesn't exist, and add the file method.xml to it. The contents of the file should be:

<?xml version="1.0" encoding="utf-8"?><input-method xmlns:android="http://schemas.android.com/apk/res/android"> <subtype
        android:label="@string/subtype_en_US"		
		android:imeSubtypeLocale="en_US"
		android:imeSubtypeMode="keyboard" /></input-method>

5. Edit strings.xml

The strings that this app uses are defined in the res/values/strings.xml file. We're going to need three strings:

  • the name of the app
  • the label of the IME
  • the label of the IME's subtype

Update your strings.xml so that it has the following contents:

<resources><string name="app_name">SimpleKeyboard</string><string name="simple_ime">Simple IME</string><string name="subtype_en_US">English (US)</string></resources>

6. Define the Keyboard Layout

The layout of our keyboard contains only a KeyboardView. The layout_alignParentBottom attribute is set to true so that keyboard appears at the bottom of the screen.

Create a file named res/layout/keyboard.xml and replace its contents with the following:

<?xml version="1.0" encoding="UTF-8"?><android.inputmethodservice.KeyboardView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/keyboard"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:keyPreviewLayout ="@layout/preview"
/>

The keyPreviewLayout is the layout of the short-lived pop-up that shows up whenever a key on the keyboard is pressed. It contains a single TextView. Create a file named res/layout/preview.xml and add the following to it:

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="#ffff00"    
    android:textStyle="bold"
    android:textSize="30sp">    </TextView>

6. Define the Keyboard Keys

The details of the keyboard keys and their positions are specified in an XML file. Every key has the following attributes:

  • keyLabel: This attribute contains the text that is displayed on the key.
  • codes: This attribute contains the unicode values of the characters that the key represents.

For example, to define a key for the letter A, the codes attribute should have the value 97 and the keyLabel attribute should be set to A.

If more than one code is associated with a key, then the character that the key represents will depend on the number of taps the key receives. For example, if a key has the codes 63, 33, and 58:

  • a single tap on the key results in the character ?
  • two taps in quick succession results in the character !
  • three taps in quick succession results in the character :

A key can also have a few optional attributes:

  • keyEdgeFlags: This attribute can take the value left or right. This attribute is usually added to the leftmost and rightmost keys of a row.
  • keyWidth: This attribute defines the width of a key. It's usually defined as a percentage value.
  • isRepeatable: If this attribute is set to true, long-pressing the key will repeat the action of the key multiple times. It is usually set to true for the delete and spacebar keys.

The keys of a keyboard are grouped as rows. It's good practice to limit the number of keys on a row to a maximum of ten, with each key having a width equal to 10% of the keyboard. The height of the keys is set to 60dp in this tutorial. This value can be adjusted, but values less than 48dp are not recommended. Our keyboard will have five rows of keys.

We can now go ahead and design the keyboard. Create a new file named res/xml/qwerty.xml and replace its contents with the following:

<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="10%p"
	android:horizontalGap="0px"
	android:verticalGap="0px"	
	android:keyHeight="60dp"><Row><Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/><Key android:codes="50" android:keyLabel="2"/><Key android:codes="51" android:keyLabel="3"/><Key android:codes="52" android:keyLabel="4"/><Key android:codes="53" android:keyLabel="5"/><Key android:codes="54" android:keyLabel="6"/><Key android:codes="55" android:keyLabel="7"/><Key android:codes="56" android:keyLabel="8"/><Key android:codes="57" android:keyLabel="9"/><Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/></Row><Row><Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/><Key android:codes="119" android:keyLabel="w"/><Key android:codes="101" android:keyLabel="e"/><Key android:codes="114" android:keyLabel="r"/><Key android:codes="116" android:keyLabel="t"/><Key android:codes="121" android:keyLabel="y"/><Key android:codes="117" android:keyLabel="u"/><Key android:codes="105" android:keyLabel="i"/><Key android:codes="111" android:keyLabel="o"/><Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/></Row><Row><Key android:codes="97" android:keyLabel="a" android:keyEdgeFlags="left"/><Key android:codes="115" android:keyLabel="s"/><Key android:codes="100" android:keyLabel="d"/><Key android:codes="102" android:keyLabel="f"/><Key android:codes="103" android:keyLabel="g"/><Key android:codes="104" android:keyLabel="h"/><Key android:codes="106" android:keyLabel="j"/><Key android:codes="107" android:keyLabel="k"/>		<Key android:codes="108" android:keyLabel="l"/><Key android:codes="35,64" android:keyLabel="\# \@" android:keyEdgeFlags="right"/></Row><Row><Key android:codes="-1" android:keyLabel="CAPS" android:keyEdgeFlags="left"/><Key android:codes="122" android:keyLabel="z"/><Key android:codes="120" android:keyLabel="x"/><Key android:codes="99" android:keyLabel="c"/><Key android:codes="118" android:keyLabel="v"/><Key android:codes="98" android:keyLabel="b"/><Key android:codes="110" android:keyLabel="n"/><Key android:codes="109" android:keyLabel="m"/><Key android:codes="46" android:keyLabel="."/><Key android:codes="63,33,58" android:keyLabel="\? ! :" android:keyEdgeFlags="right"/>	</Row><Row android:rowEdgeFlags="bottom"><Key android:codes="44" android:keyLabel="," android:keyWidth="10%p"  android:keyEdgeFlags="left"/>	<Key android:codes="47" android:keyLabel="/" android:keyWidth="10%p" /><Key android:codes="32" android:keyLabel="SPACE" android:keyWidth="40%p" android:isRepeatable="true"/>		<Key android:codes="-5" android:keyLabel="DEL" android:keyWidth="20%p" android:isRepeatable="true"/><Key android:codes="-4" android:keyLabel="DONE" android:keyWidth="20%p" android:keyEdgeFlags="right"/></Row>	</Keyboard>

You may have noticed that some keys have negative values for the codes attribute. Negative values are equal to predefined constants in the Keyboard class. For example, the value -5 is equal to the value of Keyboard.KEYCODE_DELETE.

7. Create a Service Class

Create a new Java class and call it SimpleIME.java. The class should extend InputMethodService class and implement the OnKeyboardActionListener interface. The OnKeyboardActionListener interface contains the methods that are called when keys of the soft keyboard are tapped or pressed.

The SimpleIME class should have three member variables:

  • KeyboardView referencing the view defined in the layout
  • Keyboard instance that is assigned to the KeyboardView
  • boolean telling us if the caps lock is enabled

After declaring these variables and adding the methods of the OnKeyboardActionListener interface, the SimpleIME class should look like this:

public class SimpleIME extends InputMethodService
    implements OnKeyboardActionListener{
	private KeyboardView kv;
	private Keyboard keyboard;
	private boolean caps = false;

	@Override
	public void onKey(int primaryCode, int[] keyCodes) {		

	}

	@Override
	public void onPress(int primaryCode) {
	}

	@Override
	public void onRelease(int primaryCode) { 			
	}

	@Override
	public void onText(CharSequence text) {		
	}

	@Override
	public void swipeDown() {	
	}

	@Override
	public void swipeLeft() {
	}

	@Override
	public void swipeRight() {
	}

	@Override
	public void swipeUp() {
	}
}

When the keyboard is created, the onCreateInputView method is called. All the member variables of the Service can be initialized here. Update the implementation of the onCreateInputView method as shown below:

@Override
public View onCreateInputView() {
	kv = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard, null);
	keyboard = new Keyboard(this, R.xml.qwerty);
	kv.setKeyboard(keyboard);
	kv.setOnKeyboardActionListener(this);
	return kv;
}

Next, we create a method that plays a sound when a key is pressed. We use the AudioManager class to play the sounds. The Android SDK includes a few default sound effects for key presses and those are used in the playClick method.

private void playClick(int keyCode){
	AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
	switch(keyCode){
	case 32: 
		am.playSoundEffect(AudioManager.FX_KEYPRESS_SPACEBAR);
		break;
	case Keyboard.KEYCODE_DONE:
	case 10: 
		am.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN);
		break;
	case Keyboard.KEYCODE_DELETE:
		am.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE);
		break;				
	default: am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
	}		
}

Finally, update the onKey method so that our keyboard app can communicate with input fields (usually EditText views) of other applications.

The getCurrentInputConnection method is used to get a connection to the input field of another application. Once we have the connection, we can use the following methods:

  • commitText to add one or more characters to the input field
  • deleteSurroundingText to delete one or more characters of the input field
  • sendKeyEvent to send events, like KEYCODE_ENTER, to the external application

Whenever a user presses a key on the soft keyboard, the onKey method is called with the unicode value of the key as one of its parameters. Based on this value, the keyboard  performs one of the following actions:

  • If the code is KEYCODE_DELETE, one character to the left of the cursor is deleted using the deleteSurroundingText method.
  • If the code is KEYCODE_DONE, a KEYCODE_ENTER key event is fired.
  • If the code is KEYCODE_SHIFT, the value of the caps variable is changed and the shift state of the keyboard is updated using the setShifted method. The keyboard needs to be redrawn when the state changes so that the labels of the keys are updated. The invalidateAllKeys method is used to redraw all keys.
  • For all other codes, the code is simply converted into a character and sent to the input field. If the code represents a letter of the alphabet and the caps variable is set to true, then the character is converted to uppercase.

Update the onKey method so that it looks like this:

@Override
public void onKey(int primaryCode, int[] keyCodes) {        
    InputConnection ic = getCurrentInputConnection();
    playClick(primaryCode);
    switch(primaryCode){
    case Keyboard.KEYCODE_DELETE :
        ic.deleteSurroundingText(1, 0);
        break;
    case Keyboard.KEYCODE_SHIFT:
        caps = !caps;
        keyboard.setShifted(caps);
        kv.invalidateAllKeys();
        break;
    case Keyboard.KEYCODE_DONE:
        ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
        break;
    default:
        char code = (char)primaryCode;
        if(Character.isLetter(code) && caps){
            code = Character.toUpperCase(code);
        }
        ic.commitText(String.valueOf(code),1);                  
    }
}

8. Testing the Keyboard

The soft keyboard is now ready to be tested. Compile and run it on an Android device. This app doesn't have an Activity, which means that it won't show up in the launcher. To use it, it should first be activated in the device's Settings.

After activating Simple IME, open any app that allows text input (for example, any messaging app) and click on one of its input fields. You should see a keyboard icon appear in the notifications area. Depending on your device, you can either click on that icon or drag the notification bar down and select Simple IME as the input method. You should now be able to type using your new keyboard.

Conclusion

In this tutorial, you have learned how to create a custom keyboard app from scratch. To change the look and feel of your keyboard, all you have to do is add extra styling to the res/layout/keyboard.xml and res/layout/preview.xml files. To change the positions of the keys, update the res/xml/qwerty.xml file. To add more features to your keyboard, refer to the developer documentation.


2014-12-01T16:45:22.000Z2014-12-01T16:45:22.000ZAshraff Hathibelagal

Introduction to the Visual Format Language

$
0
0

Auto Layout has been available for a few years now, but with the iPhone 6 and 6 Plus it's become a necessity for projects. While it hasn't always been particularly easy to use, Xcode has steadily seen improvements in Interface Builder to make integrating Auto Layout simpler. In this tutorial, you'll learn how to use the Visual Format Language using Swift to create Auto Layout constraints in code.

1. Introduction

This tutorial assumes you have some knowledge of Auto Layout. If you are new Auto Layout, then I encourage you to read the introduction by Joyce Echessa first.

The Visual Format Language is a declarative language that is used to define Auto Layout constraints for views. Its syntax is expressive and easy to understand when you are skimming through code. The intended constraints should be immediately clear from reading a Visual Format Language statement and they flow much like a sentence.

Auto Layout constraints with different priorities, vertical layouts, spacing, and dimensions can be created using the Visual Format Language syntax. It's defined inside a string variable and then passed to the class level methods constraintsWithVisualFormat:options:metrics:views: and
 constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant: of the NSLayoutConstraint class.

The Visual Format Language can be especially useful when Interface Builder is not an option to add Auto Layout constraints, for example, when part of your application's user interface needs to be created programmatically.

2. Creating a New Project

Let's create a new project in Xcode to see how the Visual Format Language is used and how your projects can benefit from it.

Step 1: Project Template

Open Xcode and select New > Project... from the File menu. Choose Single View Application from the list of iOS Application templates and click Next.

Step 2: Project Configuration

Next, name your project and enter your organization's name and identifier. Choose Universal from the Devices list, click Next, and choose a location to save the project. Select Swift as the programming language.

3. Creating a Constraint for a Single View

Step 1: Define Variables

To begin, create three variables of type UIView. Open ViewController.swift and add the following code above the viewDidLoad method:

var vwBlue:UIView!
var vwRed:UIView!
var vwGreen:UIView!

Step 2: Initialize Views

Create a function called initViews at the bottom of the view controller void as its return type. This function will initialize the views and add them to the view hierarchy. Be sure to call this function in viewDidLoad after calling the superclass's viewDidLoad method.

func initViews() -> Void
{
    //Initialize
    vwRed = UIView()
    vwBlue = UIView()
    vwGreen = UIView()
    //Prep auto layout
    vwRed.setTranslatesAutoresizingMaskIntoConstraints(false)
    vwBlue.setTranslatesAutoresizingMaskIntoConstraints(false)
    vwGreen.setTranslatesAutoresizingMaskIntoConstraints(false)
    //Coloring
    vwRed.backgroundColor = UIColor.redColor()
    vwBlue.backgroundColor = UIColor.blueColor()
    vwGreen.backgroundColor = UIColor.greenColor()
    //Add them to the view
    self.view.addSubview(vwRed)
    self.view.addSubview(vwBlue)
    self.view.addSubview(vwGreen)
}

When using Auto Layout on views created in code, there are a few caveats to be aware of. The first is related to the value of the property translatesAutoresizingMaskIntoConstraints. This property is true by default, which means Auto Layout constraints will be created based on the view's autoresizing mask. We want the view to respect the Auto Layout constraints we will add so this property should be set to false.

The second thing to keep in mind is the view life cycle. Before Auto Layout constraints can be added to a view, it must be added to superview. Otherwise, a runtime exception is thrown. Recall that Auto Layout defines where views are positioned based on relationships. If a view has no superview, the operating system has no reference point to relate the Auto Layout constraints to.

Step 3: Create the Constraints for a Single View

Let's begin with a simple example of the Visual Format Language. For the red view, vwRed, we will add Auto Layout constraints that make it the same size as its superview. This is helpful in a scenario where you add a background image.

Before the Visual Format Language can be used, all of the views that we need must be referenced inside a dictionary. This is how the views will be identified by the Visual Format Language.

Create a function called createConstraints with a void return type at the bottom of the view controller class. Don't worry about the syntax. We'll revisit the implementation of the createConstraints function in a moment.

func createConstraints() -> Void
{
    //Views to add constraints to
    let views = Dictionary(dictionaryLiteral: ("red",vwRed),("blue",vwBlue),("green",vwGreen))
    //Horizontal constraints
    let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|[red]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(horizontalConstraints)
    //Vertical constraints
    let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:|[red]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(verticalConstraints)
}

Step 4: Build and Run

Call this function at the end of the initViews function we created earlier. Build and run the project by pressing Command + R or by clicking the play button at the top left. The iOS Simulator will run showing the red view taking up the entire screen as intended.

4. Analyzing the Visual Format Language Syntax

When using the Visual Format Language, Auto Layout constraints are defined either horizontally or vertically. You can also define the height or width of a view when you're declaring a vertical and horizontal constraint respectively. Let's take a closer look at the first string we used to create the horizontal constraint.

"H:|[red]|"

First, we identify that this will be a horizontal constraint by beginning the string with the letter H. Horizontal is the default, but it's good practice to include it to make it more obvious. The constraint's direction is followed by a colon.

The | or pipe symbol symbolizes the view's superview. To add space between two elements, the - or dash symbol is used and integer values can be placed between them to create a fixed or variable spacing. Views are referenced by the keys provided in the dictionary passed to constraintsWithVisualFormat. Each view is wrapped in square brackets.

Notice how the whole string visually matches the image from the simulator. It's written like a sentence that would read something like "Horizontally, the red view should extend the entire width of its superview with no padding."

5. Creating Constraints for Multiple Views

Now that you have a basic understanding of the syntax, we're going to edit the createConstraints function to add Auto Layout constraints to two views.

Step 1: Edit the Horizontal Constraint 

In the createConstraints function, edit the horizontalConstraints variable as shown below.

//Horizontal constraints
let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[red(>=100,<=200)]-0-[blue(==red)]-10-|", options: nil, metrics: nil, views: views)
self.view.addConstraints(horizontalConstraints)

This code snippet really shows the flexibility of the Visual Format Language. The above statement creates a number of Auto Layout constraints for us. Next to the name of the view, horizontal sizes are defined in parentheses. For the red view, the size should be greater than or equal to 100 points, but less than or equal to 200 points.

The blue view specifies that it should have the same horizontal size as the red view by using ==red in parentheses. This is a convenient way to specify multiple views should have the same size. Build and run the app in the iOS Simulator. The result should look like the screenshot shown below.

Step 2: Adding Priorities

With the application running in the iOS Simulator, press Command + Left Arrow to change the orientation of the iOS Simulator to landscape. While the application still runs fine, a warning has popped up in Xcode's console. The warning tells us that some Auto Layout constraints could not be satisfied. While this won't crash your application, it can lead to unexpected results inside your application's user interface.

This occurs because the two views we created cannot be 200 points wide and have no spacing between them when the device or iOS Simulator is in landscape. Auto Layout solves these types of scenarios using priorities. The Visual Format Language lets you define priorities using the @ symbol. Edit the horizontalConstraints variable to read like this:

//Horizontal constraints
let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[red(>=100,<=200@20)]-0-[blue(==red)]-10-|", options: nil, metrics: nil, views: views)

Because the red and blue views now have a low priority on their width constraint, indicated by @20, the Auto Layout system will break these constraints and supply them with the correct value at runtime. Run the application again and change the orientation to landscape. The views now fill the extra space and Xcode doesn't produce any warnings.

Step 3: Adding Constraints to the Bottom View

Next, we will create constraints for the green view. Update the implementation of the createConstraints function as shown below.

func createConstraints() -> Void
{
    //Views to add constraints to
    let views = Dictionary(dictionaryLiteral: ("red",vwRed),("blue",vwBlue),("green",vwGreen))
    //Horizontal constraints
    let horizontalConstraintsRedBlue = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[red(>=100,<=200@20)]-0-[blue(==red)]-10-|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(horizontalConstraintsRedBlue)
    let horizontalConstraintsGreen = NSLayoutConstraint.constraintsWithVisualFormat("H:|[green]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(horizontalConstraintsGreen)
    //Vertical constraints
    let verticalConstraintsRed = NSLayoutConstraint.constraintsWithVisualFormat("V:|[red]-10-[green(40)]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(verticalConstraintsRed)
    let verticalConstraintsBlue = NSLayoutConstraint.constraintsWithVisualFormat("V:|[blue]-10-[green(40)]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(verticalConstraintsBlue)
}

Because the horizontalConstraintsGreen constraint doesn't define a specific width or spacing to its superview, it will span the entire length. The vertical constraint ensure that it will be 40 points high with 10 points of spacing between the red and blue views.

If you run the application one more time, the green view spans the entire width of the screen and the red and blue views stay above it as they were before. When the iOS Simulator is rotated to landscape, the views keep their positions and resize appropriately.

Step 4: Adding Metrics

To make everything more readable, we'll use a dictionary of metrics in the constraints declarations. Create a dictionary as shown below, immediately after declaring the views dictionary.

let metrics = Dictionary(dictionaryLiteral: ("spacing", 10),("lowWidth",100),("highWidth",200),("priority",20),("redBlueSpacing",0),("greenHeight",40))

Now, instead of using hard-coded values, we can use the values of the metrics dictionary, which makes the declarations of the constraints much more readable. Edit the createConstraints function one last time using the new metrics dictionary.

func createConstraints() -> Void
{
    //Views to add constraints to
    let views = Dictionary(dictionaryLiteral: ("red",vwRed),("blue",vwBlue),("green",vwGreen))
    //Metrics for Visual Format string
    let metrics = Dictionary(dictionaryLiteral: ("spacing", 10),("lowWidth",100),("highWidth",200),("priority",20),("redBlueSpacing",0),("greenHeight",40))
    //Horizontal constraints
    let horizontalConstraintsRedBlue = NSLayoutConstraint.constraintsWithVisualFormat("H:|-spacing-[red(>=lowWidth,<=highWidth@priority)]-redBlueSpacing-[blue(==red)]-spacing-|", options: nil, metrics: metrics, views: views)
    self.view.addConstraints(horizontalConstraintsRedBlue)
    let horizontalConstraintsGreen = NSLayoutConstraint.constraintsWithVisualFormat("H:|[green]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(horizontalConstraintsGreen)
    //Vertical constraints
    let verticalConstraintsRed = NSLayoutConstraint.constraintsWithVisualFormat("V:|[red]-spacing-[green(greenHeight)]|", options: nil, metrics: metrics, views: views)
    self.view.addConstraints(verticalConstraintsRed)
    let verticalConstraintsBlue = NSLayoutConstraint.constraintsWithVisualFormat("V:|[blue]-spacing-[green(greenHeight)]|", options: nil, metrics: metrics, views: views)
    self.view.addConstraints(verticalConstraintsBlue)
}

6. Limitations of the Visual Format Language

You may be wondering why the height of the green view was defined twice. This is because the Visual Format Language works in rows and columns. When using the Visual Format Language, think of adding constraints left to right on one "row" of the view for horizontal constraints. For vertical constraints, you need to think in terms of columns.

Most of the Auto Layout constraints you'll use can be expressed with the Visual Format Language. There are a few that can't, however. For instance, a fixed aspect ratio constraint cannot be created using the Visual Format Language. This can't be accomplished with the Visual Format Language syntax, because the following string can't be parsed:

H:|imageView.width = 2 * imageView.height|

You can still use Auto Layout in your code to achieve these types of constraints using the traditional constraintWithItem method.

Conclusion

The Visual Format Language can be very helpful when you need to create Auto Layout constraints in code. Instead of creating constraints one by one, the Visual Format Language lets you create a number of constraints with one line of code.

Before Auto Layout was available to developers, keeping track of how to resize views for different device categories was a lot of work. With Auto Layout and the Visual Format Language, this is now more intuitive, making user interfaces easier to maintain across devices.

2014-12-03T16:45:32.000Z2014-12-03T16:45:32.000ZJordan Morgan

Introduction to the Visual Format Language

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22715

Auto Layout has been available for a few years now, but with the iPhone 6 and 6 Plus it's become a necessity for projects. While it hasn't always been particularly easy to use, Xcode has steadily seen improvements in Interface Builder to make integrating Auto Layout simpler. In this tutorial, you'll learn how to use the Visual Format Language using Swift to create Auto Layout constraints in code.

1. Introduction

This tutorial assumes you have some knowledge of Auto Layout. If you are new Auto Layout, then I encourage you to read the introduction by Joyce Echessa first.

The Visual Format Language is a declarative language that is used to define Auto Layout constraints for views. Its syntax is expressive and easy to understand when you are skimming through code. The intended constraints should be immediately clear from reading a Visual Format Language statement and they flow much like a sentence.

Auto Layout constraints with different priorities, vertical layouts, spacing, and dimensions can be created using the Visual Format Language syntax. It's defined inside a string variable and then passed to the class level methods constraintsWithVisualFormat:options:metrics:views: and
 constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant: of the NSLayoutConstraint class.

The Visual Format Language can be especially useful when Interface Builder is not an option to add Auto Layout constraints, for example, when part of your application's user interface needs to be created programmatically.

2. Creating a New Project

Let's create a new project in Xcode to see how the Visual Format Language is used and how your projects can benefit from it.

Step 1: Project Template

Open Xcode and select New > Project... from the File menu. Choose Single View Application from the list of iOS Application templates and click Next.

Step 2: Project Configuration

Next, name your project and enter your organization's name and identifier. Choose Universal from the Devices list, click Next, and choose a location to save the project. Select Swift as the programming language.

3. Creating a Constraint for a Single View

Step 1: Define Variables

To begin, create three variables of type UIView. Open ViewController.swift and add the following code above the viewDidLoad method:

var vwBlue:UIView!
var vwRed:UIView!
var vwGreen:UIView!

Step 2: Initialize Views

Create a function called initViews at the bottom of the view controller void as its return type. This function will initialize the views and add them to the view hierarchy. Be sure to call this function in viewDidLoad after calling the superclass's viewDidLoad method.

func initViews() -> Void
{
    //Initialize
    vwRed = UIView()
    vwBlue = UIView()
    vwGreen = UIView()
    //Prep auto layout
    vwRed.setTranslatesAutoresizingMaskIntoConstraints(false)
    vwBlue.setTranslatesAutoresizingMaskIntoConstraints(false)
    vwGreen.setTranslatesAutoresizingMaskIntoConstraints(false)
    //Coloring
    vwRed.backgroundColor = UIColor.redColor()
    vwBlue.backgroundColor = UIColor.blueColor()
    vwGreen.backgroundColor = UIColor.greenColor()
    //Add them to the view
    self.view.addSubview(vwRed)
    self.view.addSubview(vwBlue)
    self.view.addSubview(vwGreen)
}

When using Auto Layout on views created in code, there are a few caveats to be aware of. The first is related to the value of the property translatesAutoresizingMaskIntoConstraints. This property is true by default, which means Auto Layout constraints will be created based on the view's autoresizing mask. We want the view to respect the Auto Layout constraints we will add so this property should be set to false.

The second thing to keep in mind is the view life cycle. Before Auto Layout constraints can be added to a view, it must be added to superview. Otherwise, a runtime exception is thrown. Recall that Auto Layout defines where views are positioned based on relationships. If a view has no superview, the operating system has no reference point to relate the Auto Layout constraints to.

Step 3: Create the Constraints for a Single View

Let's begin with a simple example of the Visual Format Language. For the red view, vwRed, we will add Auto Layout constraints that make it the same size as its superview. This is helpful in a scenario where you add a background image.

Before the Visual Format Language can be used, all of the views that we need must be referenced inside a dictionary. This is how the views will be identified by the Visual Format Language.

Create a function called createConstraints with a void return type at the bottom of the view controller class. Don't worry about the syntax. We'll revisit the implementation of the createConstraints function in a moment.

func createConstraints() -> Void
{
    //Views to add constraints to
    let views = Dictionary(dictionaryLiteral: ("red",vwRed),("blue",vwBlue),("green",vwGreen))
    //Horizontal constraints
    let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|[red]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(horizontalConstraints)
    //Vertical constraints
    let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:|[red]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(verticalConstraints)
}

Step 4: Build and Run

Call this function at the end of the initViews function we created earlier. Build and run the project by pressing Command + R or by clicking the play button at the top left. The iOS Simulator will run showing the red view taking up the entire screen as intended.

4. Analyzing the Visual Format Language Syntax

When using the Visual Format Language, Auto Layout constraints are defined either horizontally or vertically. You can also define the height or width of a view when you're declaring a vertical and horizontal constraint respectively. Let's take a closer look at the first string we used to create the horizontal constraint.

"H:|[red]|"

First, we identify that this will be a horizontal constraint by beginning the string with the letter H. Horizontal is the default, but it's good practice to include it to make it more obvious. The constraint's direction is followed by a colon.

The | or pipe symbol symbolizes the view's superview. To add space between two elements, the - or dash symbol is used and integer values can be placed between them to create a fixed or variable spacing. Views are referenced by the keys provided in the dictionary passed to constraintsWithVisualFormat. Each view is wrapped in square brackets.

Notice how the whole string visually matches the image from the simulator. It's written like a sentence that would read something like "Horizontally, the red view should extend the entire width of its superview with no padding."

5. Creating Constraints for Multiple Views

Now that you have a basic understanding of the syntax, we're going to edit the createConstraints function to add Auto Layout constraints to two views.

Step 1: Edit the Horizontal Constraint 

In the createConstraints function, edit the horizontalConstraints variable as shown below.

//Horizontal constraints
let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[red(>=100,<=200)]-0-[blue(==red)]-10-|", options: nil, metrics: nil, views: views)
self.view.addConstraints(horizontalConstraints)

This code snippet really shows the flexibility of the Visual Format Language. The above statement creates a number of Auto Layout constraints for us. Next to the name of the view, horizontal sizes are defined in parentheses. For the red view, the size should be greater than or equal to 100 points, but less than or equal to 200 points.

The blue view specifies that it should have the same horizontal size as the red view by using ==red in parentheses. This is a convenient way to specify multiple views should have the same size. Build and run the app in the iOS Simulator. The result should look like the screenshot shown below.

Step 2: Adding Priorities

With the application running in the iOS Simulator, press Command + Left Arrow to change the orientation of the iOS Simulator to landscape. While the application still runs fine, a warning has popped up in Xcode's console. The warning tells us that some Auto Layout constraints could not be satisfied. While this won't crash your application, it can lead to unexpected results inside your application's user interface.

This occurs because the two views we created cannot be 200 points wide and have no spacing between them when the device or iOS Simulator is in landscape. Auto Layout solves these types of scenarios using priorities. The Visual Format Language lets you define priorities using the @ symbol. Edit the horizontalConstraints variable to read like this:

//Horizontal constraints
let horizontalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[red(>=100,<=200@20)]-0-[blue(==red)]-10-|", options: nil, metrics: nil, views: views)

Because the red and blue views now have a low priority on their width constraint, indicated by @20, the Auto Layout system will break these constraints and supply them with the correct value at runtime. Run the application again and change the orientation to landscape. The views now fill the extra space and Xcode doesn't produce any warnings.

Step 3: Adding Constraints to the Bottom View

Next, we will create constraints for the green view. Update the implementation of the createConstraints function as shown below.

func createConstraints() -> Void
{
    //Views to add constraints to
    let views = Dictionary(dictionaryLiteral: ("red",vwRed),("blue",vwBlue),("green",vwGreen))
    //Horizontal constraints
    let horizontalConstraintsRedBlue = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[red(>=100,<=200@20)]-0-[blue(==red)]-10-|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(horizontalConstraintsRedBlue)
    let horizontalConstraintsGreen = NSLayoutConstraint.constraintsWithVisualFormat("H:|[green]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(horizontalConstraintsGreen)
    //Vertical constraints
    let verticalConstraintsRed = NSLayoutConstraint.constraintsWithVisualFormat("V:|[red]-10-[green(40)]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(verticalConstraintsRed)
    let verticalConstraintsBlue = NSLayoutConstraint.constraintsWithVisualFormat("V:|[blue]-10-[green(40)]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(verticalConstraintsBlue)
}

Because the horizontalConstraintsGreen constraint doesn't define a specific width or spacing to its superview, it will span the entire length. The vertical constraint ensure that it will be 40 points high with 10 points of spacing between the red and blue views.

If you run the application one more time, the green view spans the entire width of the screen and the red and blue views stay above it as they were before. When the iOS Simulator is rotated to landscape, the views keep their positions and resize appropriately.

Step 4: Adding Metrics

To make everything more readable, we'll use a dictionary of metrics in the constraints declarations. Create a dictionary as shown below, immediately after declaring the views dictionary.

let metrics = Dictionary(dictionaryLiteral: ("spacing", 10),("lowWidth",100),("highWidth",200),("priority",20),("redBlueSpacing",0),("greenHeight",40))

Now, instead of using hard-coded values, we can use the values of the metrics dictionary, which makes the declarations of the constraints much more readable. Edit the createConstraints function one last time using the new metrics dictionary.

func createConstraints() -> Void
{
    //Views to add constraints to
    let views = Dictionary(dictionaryLiteral: ("red",vwRed),("blue",vwBlue),("green",vwGreen))
    //Metrics for Visual Format string
    let metrics = Dictionary(dictionaryLiteral: ("spacing", 10),("lowWidth",100),("highWidth",200),("priority",20),("redBlueSpacing",0),("greenHeight",40))
    //Horizontal constraints
    let horizontalConstraintsRedBlue = NSLayoutConstraint.constraintsWithVisualFormat("H:|-spacing-[red(>=lowWidth,<=highWidth@priority)]-redBlueSpacing-[blue(==red)]-spacing-|", options: nil, metrics: metrics, views: views)
    self.view.addConstraints(horizontalConstraintsRedBlue)
    let horizontalConstraintsGreen = NSLayoutConstraint.constraintsWithVisualFormat("H:|[green]|", options: nil, metrics: nil, views: views)
    self.view.addConstraints(horizontalConstraintsGreen)
    //Vertical constraints
    let verticalConstraintsRed = NSLayoutConstraint.constraintsWithVisualFormat("V:|[red]-spacing-[green(greenHeight)]|", options: nil, metrics: metrics, views: views)
    self.view.addConstraints(verticalConstraintsRed)
    let verticalConstraintsBlue = NSLayoutConstraint.constraintsWithVisualFormat("V:|[blue]-spacing-[green(greenHeight)]|", options: nil, metrics: metrics, views: views)
    self.view.addConstraints(verticalConstraintsBlue)
}

6. Limitations of the Visual Format Language

You may be wondering why the height of the green view was defined twice. This is because the Visual Format Language works in rows and columns. When using the Visual Format Language, think of adding constraints left to right on one "row" of the view for horizontal constraints. For vertical constraints, you need to think in terms of columns.

Most of the Auto Layout constraints you'll use can be expressed with the Visual Format Language. There are a few that can't, however. For instance, a fixed aspect ratio constraint cannot be created using the Visual Format Language. This can't be accomplished with the Visual Format Language syntax, because the following string can't be parsed:

H:|imageView.width = 2 * imageView.height|

You can still use Auto Layout in your code to achieve these types of constraints using the traditional constraintWithItem method.

Conclusion

The Visual Format Language can be very helpful when you need to create Auto Layout constraints in code. Instead of creating constraints one by one, the Visual Format Language lets you create a number of constraints with one line of code.

Before Auto Layout was available to developers, keeping track of how to resize views for different device categories was a lot of work. With Auto Layout and the Visual Format Language, this is now more intuitive, making user interfaces easier to maintain across devices.

2014-12-03T16:45:32.000Z2014-12-03T16:45:32.000ZJordan Morgan

Swift from Scratch: Introduction

$
0
0

If you're reading this article, then chances are that you have heard of a new programming language called Swift. Apple released Swift during this year's WWDC, the company's annual developer conference, and for most of us it came as a complete surprise. The last thing developers were expecting was a brand new programming language to power the next generation of iOS and OS X applications.

While Swift will feel familiar if you've been using Objective-C to develop iOS or OS X applications, there are a number of important differences. You'll also have to become familiar with Swift's elegant and modern syntax. I'm going to kick this series off by showing you in what ways Swift differs from Objective-C and why those differences are a good thing. Let's get started.

1. Prerequisites

Programming

Throughout this series, I will make references to Objective-C and compare the Swift programming language with Objective-C. However, to follow along there is no need to be familiar with Objective-C.

That said, it is important that you have experience with a programming language. While this series focuses on Swift, it doesn't cover the basics of programming. I expect you to be familiar with variables, constants, functions, control flow, and object-oriented programming.

If you're familiar with Objective-C, Java, Ruby, PHP, or JavaScript, then you won't have problems understanding the concepts explained in this series. As a matter of fact, you'll quickly learn that Swift shares similarities with a number of popular programming languages, including Objective-C.

Xcode

Swift is only supported by Xcode 6 and you need to install the latest version of Apple's IDE (Integrated Development Environment) to follow along. You can either download Xcode from the App Store or Apple's Developer Center.

2. Swift

Compared to Objective-C or Java, Swift is an expressive, succinct language that often reminds me of Ruby and JavaScript. Even though the creator of Swift, Chris Lattner, took inspiration from other languages, Swift very much is a language that stands on its own feet.

As you may know, Objective-C is a strict superset of C. Swift, however, is not. While Swift uses curly braces and shares a number of keywords with the C programming language, Swift is not compatible with C.

Swift is a modern programming language that feels intuitive, especially if you're used to Java or C-based programming languages like Objective-C. During the development and design of Swift, Chris Lattner focused on a number of key characteristics that ended up defining the language.

Safety

Safety is one of Swift's foundations. You will quickly learn that Swift is very different from Objective-C in terms of safety and this will directly impact the code you write. If you've worked with Objective-C, then this will take some getting used to.

LLVM

Chris Lattner also designed the LLVM (Low Level Virtual Machine) compiler and it shouldn't be a surprise that Swift is built with the LLVM compiler. The result is speed, power, and reliability. Swift is significantly faster than Objective-C in most scenarios. Read this article from Jesse Squires if you're interested in the nitty-gritty details.

Type Inference

Type safety is one of Swift's key features. Swift inspects your code at compile time and warns you about type mismatches. This means that you can catch errors earlier, avoiding a range of common bugs.

Luckily, Swift helps you with this. Swift is often smart enough to infer the type of variables and constants, which means you don't have to explicitly declare the type of each variable and constant. In the following code snippet, we declare a variable a and assign the value "this is a string" to it. Swift is smart enough to infer that a is of type String.

var a = "this is a string"

This is a trivial example, but you'll quickly learn that Swift can also handle more complex scenarios.

Variables and Constants

Constants are useful in C and Objective-C, but most developers use them sparingly. In Swift, constants are just as important and common as variables. If the value of a variable doesn't change, then that variable should be a constant. Variables are declared using the var keyword while constants are declared using the let keyword.

var a = 1 // variable
let b = 1 // constant

Not only does this improve the intent, it also improves safety by avoiding that the variable's value is changed by accident. We'll take a closer look at variables and constants a bit later in this article.

Semicolons

In Swift, semicolons are not required. You can use semicolons, for example, to write multiple statements on the same line, but they are not required. Take a look at the following example to better understand the concept.

var a = 1
var b = 2

var c = 1; var d = 2;

Know that we've barely scratched the surface. You'll learn about a lot more features and concepts throughout this series. Instead of overloading you with more theory, I suggest you get your feet wet by writing some code. This brings us to one of the best features of Swift and Xcode 6, playgrounds.

3. Playgrounds

In Xcode 6, Apple introduced playgrounds. Playgrounds are the perfect tool for learning Swift. A playground is an interactive environment in which you can write Swift and immediately see the result. Not only does it make learning Swift more fun, it is much faster and more intuitive than setting up a project in Xcode.

As a matter of fact, it's so easy that we might as well jump in and create our first playground. Open Xcode 6 and select New > Playground... from the File menu. Name the playground and set Platform to iOS.

Tell Xcode where you'd like to save the playground and click Create. Instead of creating a folder with a bunch of files and folders, a playground is nothing more than a file with a .playground extension.

The user interface you're presented with couldn't be simpler. On the left, you see a code editor with a comment at the top, an import statement for importing the UIKit framework, and one line of code that shouldn't be too difficult to understand. On the right, you see the output or results generated by the code on the left.

Let's take a moment to understand the code in our new playground. The first line should look familiar if you've worked with Objective-C, PHP, or JavaScript. Comments in Swift start with two forward slashes or, in the case of multiline comments, start with /* and end with */.

Because we selected iOS as the platform when we created the playground, Xcode added an import statement for the UIKit framework. This gives us access to every class and constant declared in this framework.

The third line looks familiar, but there are a few things that need clarifying. We declare a variable str and assign a string to it. This line of code is easy to understand, but note that the variable's name is preceded by the var keyword instead of the variable's type as you'd expect in Objective-C. The same statement in Objective-C would look something like this:

NSString *str = @"Hello, playground";

In Objective-C, we would replace the var keyword with the variable's type, prefix the string with an @ symbol, and end the statement with a semicolon. It's important to understand that the var keyword doesn't replace the type specifier in Objective-C. It's nothing more than a keyword to indicate that str is a variable as opposed to a constant. Let me explain this in more detail. Add the following line of code to the playground.

let hello = "Hello, playground"

The let keyword tells the compiler hello is a constant, not a variable. Both str and hello are of type String, but str is a variable while hello is a constant. The difference is simple to understand by adding two more lines of code.

str = "This is a variable."
hello = "This is a constant."

Assigning a new value to str isn't a problem. Assigning a new value to hello, however, results in an error. Xcode tells us that it cannot assign a new value to hello, because hello is a constant, not a variable. This is another key feature of Swift, which will take some getting used to.

The idea is simple. If the value of a variable is not going to change, then it should be a constant instead of a variable. While this may seem like a semantic detail, I guarantee you that it will make your code safer and less prone to errors. Be prepared, because you're going to see the let keyword a lot in this series.

We'll use playgrounds extensively throughout this series, because it's a great way to learn the language. There are a few other powerful playground features that we haven't covered yet, but we first need to understand the basics of the Swift language before we can benefit from those.

Conclusion

I still have to meet a developer that doesn't like Swift and that's saying something. Swift has a number of concepts that require some getting used to, but I'm pretty confident that you too will end up enjoying Swift, appreciating its power, elegance, and conciseness. In the next installment of this series, we start exploring the basics of Swift.

2014-12-05T19:15:06.000Z2014-12-05T19:15:06.000ZBart Jacobs

Swift from Scratch: Introduction

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22598

If you're reading this article, then chances are that you have heard of a new programming language called Swift. Apple released Swift during this year's WWDC, the company's annual developer conference, and for most of us it came as a complete surprise. The last thing developers were expecting was a brand new programming language to power the next generation of iOS and OS X applications.

While Swift will feel familiar if you've been using Objective-C to develop iOS or OS X applications, there are a number of important differences. You'll also have to become familiar with Swift's elegant and modern syntax. I'm going to kick this series off by showing you in what ways Swift differs from Objective-C and why those differences are a good thing. Let's get started.

1. Prerequisites

Programming

Throughout this series, I will make references to Objective-C and compare the Swift programming language with Objective-C. However, to follow along there is no need to be familiar with Objective-C.

That said, it is important that you have experience with a programming language. While this series focuses on Swift, it doesn't cover the basics of programming. I expect you to be familiar with variables, constants, functions, control flow, and object-oriented programming.

If you're familiar with Objective-C, Java, Ruby, PHP, or JavaScript, then you won't have problems understanding the concepts explained in this series. As a matter of fact, you'll quickly learn that Swift shares similarities with a number of popular programming languages, including Objective-C.

Xcode

Swift is only supported by Xcode 6 and you need to install the latest version of Apple's IDE (Integrated Development Environment) to follow along. You can either download Xcode from the App Store or Apple's Developer Center.

2. Swift

Compared to Objective-C or Java, Swift is an expressive, succinct language that often reminds me of Ruby and JavaScript. Even though the creator of Swift, Chris Lattner, took inspiration from other languages, Swift very much is a language that stands on its own feet.

As you may know, Objective-C is a strict superset of C. Swift, however, is not. While Swift uses curly braces and shares a number of keywords with the C programming language, Swift is not compatible with C.

Swift is a modern programming language that feels intuitive, especially if you're used to Java or C-based programming languages like Objective-C. During the development and design of Swift, Chris Lattner focused on a number of key characteristics that ended up defining the language.

Safety

Safety is one of Swift's foundations. You will quickly learn that Swift is very different from Objective-C in terms of safety and this will directly impact the code you write. If you've worked with Objective-C, then this will take some getting used to.

LLVM

Chris Lattner also designed the LLVM (Low Level Virtual Machine) compiler and it shouldn't be a surprise that Swift is built with the LLVM compiler. The result is speed, power, and reliability. Swift is significantly faster than Objective-C in most scenarios. Read this article from Jesse Squires if you're interested in the nitty-gritty details.

Type Inference

Type safety is one of Swift's key features. Swift inspects your code at compile time and warns you about type mismatches. This means that you can catch errors earlier, avoiding a range of common bugs.

Luckily, Swift helps you with this. Swift is often smart enough to infer the type of variables and constants, which means you don't have to explicitly declare the type of each variable and constant. In the following code snippet, we declare a variable a and assign the value "this is a string" to it. Swift is smart enough to infer that a is of type String.

var a = "this is a string"

This is a trivial example, but you'll quickly learn that Swift can also handle more complex scenarios.

Variables and Constants

Constants are useful in C and Objective-C, but most developers use them sparingly. In Swift, constants are just as important and common as variables. If the value of a variable doesn't change, then that variable should be a constant. Variables are declared using the var keyword while constants are declared using the let keyword.

var a = 1 // variable
let b = 1 // constant

Not only does this improve the intent, it also improves safety by avoiding that the variable's value is changed by accident. We'll take a closer look at variables and constants a bit later in this article.

Semicolons

In Swift, semicolons are not required. You can use semicolons, for example, to write multiple statements on the same line, but they are not required. Take a look at the following example to better understand the concept.

var a = 1
var b = 2

var c = 1; var d = 2;

Know that we've barely scratched the surface. You'll learn about a lot more features and concepts throughout this series. Instead of overloading you with more theory, I suggest you get your feet wet by writing some code. This brings us to one of the best features of Swift and Xcode 6, playgrounds.

3. Playgrounds

In Xcode 6, Apple introduced playgrounds. Playgrounds are the perfect tool for learning Swift. A playground is an interactive environment in which you can write Swift and immediately see the result. Not only does it make learning Swift more fun, it is much faster and more intuitive than setting up a project in Xcode.

As a matter of fact, it's so easy that we might as well jump in and create our first playground. Open Xcode 6 and select New > Playground... from the File menu. Name the playground and set Platform to iOS.

Tell Xcode where you'd like to save the playground and click Create. Instead of creating a folder with a bunch of files and folders, a playground is nothing more than a file with a .playground extension.

The user interface you're presented with couldn't be simpler. On the left, you see a code editor with a comment at the top, an import statement for importing the UIKit framework, and one line of code that shouldn't be too difficult to understand. On the right, you see the output or results generated by the code on the left.

Let's take a moment to understand the code in our new playground. The first line should look familiar if you've worked with Objective-C, PHP, or JavaScript. Comments in Swift start with two forward slashes or, in the case of multiline comments, start with /* and end with */.

Because we selected iOS as the platform when we created the playground, Xcode added an import statement for the UIKit framework. This gives us access to every class and constant declared in this framework.

The third line looks familiar, but there are a few things that need clarifying. We declare a variable str and assign a string to it. This line of code is easy to understand, but note that the variable's name is preceded by the var keyword instead of the variable's type as you'd expect in Objective-C. The same statement in Objective-C would look something like this:

NSString *str = @"Hello, playground";

In Objective-C, we would replace the var keyword with the variable's type, prefix the string with an @ symbol, and end the statement with a semicolon. It's important to understand that the var keyword doesn't replace the type specifier in Objective-C. It's nothing more than a keyword to indicate that str is a variable as opposed to a constant. Let me explain this in more detail. Add the following line of code to the playground.

let hello = "Hello, playground"

The let keyword tells the compiler hello is a constant, not a variable. Both str and hello are of type String, but str is a variable while hello is a constant. The difference is simple to understand by adding two more lines of code.

str = "This is a variable."
hello = "This is a constant."

Assigning a new value to str isn't a problem. Assigning a new value to hello, however, results in an error. Xcode tells us that it cannot assign a new value to hello, because hello is a constant, not a variable. This is another key feature of Swift, which will take some getting used to.

The idea is simple. If the value of a variable is not going to change, then it should be a constant instead of a variable. While this may seem like a semantic detail, I guarantee you that it will make your code safer and less prone to errors. Be prepared, because you're going to see the let keyword a lot in this series.

We'll use playgrounds extensively throughout this series, because it's a great way to learn the language. There are a few other powerful playground features that we haven't covered yet, but we first need to understand the basics of the Swift language before we can benefit from those.

Conclusion

I still have to meet a developer that doesn't like Swift and that's saying something. Swift has a number of concepts that require some getting used to, but I'm pretty confident that you too will end up enjoying Swift, appreciating its power, elegance, and conciseness. In the next installment of this series, we start exploring the basics of Swift.

2014-12-05T19:15:06.000Z2014-12-05T19:15:06.000ZBart Jacobs

Quick Tip: How to Use the EventBus Library

$
0
0
Final product image
What You'll Be Creating

Building an Android app that has multiple active components communicating with each other can get tedious. To save time, developers often end up with tightly coupled components in their apps. EventBus is a popular open-source library that was created to solve this problem using the publisher/subscriber pattern.

Using the EventBus library, you can pass messages from one class to one or more classes in just a few lines of code. Moreover, all classes involved are completely decoupled from each other, leading to code that is less complex, and easier to maintain and debug.

In this quick tip, you are going to learn how to use the EventBus library by building a simple Android application that displays the charging status of the device. Because changes in the charging status are system events, the app is going to have an Activity that needs to receive information from a BroadcastReceiver—the perfect scenario for using an event bus.

Prerequisites

Ensure that you have the Eclipse ADT Bundle set up. You can download it from the Android Developer website.

1. Create New Project

Start Eclipse and create a new Android application. Name the application EventBusSample. Choose a unique package name and set the Minimum Required SDK to Android2.2 and the Target SDK to Android4.4.

Create a new Android application

We're going to create the Activity ourselves, so deselect Create Activity and click Finish.

Deselect Create Activity and click Finish

2. Edit Manifest

This app has one BroadcastReceiver that responds to the following actions:

  • android.intent.action.ACTION_POWER_CONNECTED
  • android.intent.action.ACTION_POWER_DISCONNECTED

Name the BroadcastReceiver ChargingReceiver and declare it in the AndroidManifest.xml file.

<receiver android:name=".ChargingReceiver"><intent-filter><action android:name="android.intent.action.ACTION_POWER_CONNECTED" /><action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" /></intent-filter></receiver>

The app has one Activity to display the charging status. Name it DisplayActivity and declare it as shown below.

<activity android:name=".DisplayActivity"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER" /></intent-filter>            </activity>

3. Add EventBus Library

Download the latest version of the EventBus library as a JAR from Maven Central and add the JAR in your project's libs directory.

4. Create ChargingEvent Class

Events on the event bus are nothing but objects containing the information that needs to be communicated. Let ChargingEvent be the name of the class that contains the information passed from the BroadcastReceiver to the Activity. This is a simple class that has only a String to represent the information. It also has a constructor to set the value of the string and an accessor to get its value.

Create a new file named ChargingEvent.java and add the following code to it:

package com.hathy.eventbussample;

public class ChargingEvent {
    private String data;
	
	public ChargingEvent(String data){			
		this.data = data;
	}
	
	public String getData(){
		return data;
	}
}

5. Create BroadcastReceiver Class

Create a new class named ChargingReceiver that extends BroadcastReceiver. This class uses the event bus to publish messages. It has a variable named bus, which references the bus created by the EventBus library. The bus is a singleton and you have to use the getDefault method to refer to it.

In the onReceive method, we create a new instance of the ChargingEvent class and add our message to it. Here is a sample message:

@14:23:20 this device started charging.

To generate this message, we need to do the following:

  • Use the methods available in the Time class to set the time at which the event occurred.
  • Append the string "this device started charging | discharging" based on the action received. If the action received is Intent.ACTION_POWER_CONNECTED, the device is charging. If it is Intent.ACTION_POWER_DISCONNECTED, the device is discharging.

Once the ChargingEvent object has the right information, it is published on the event bus using the post method. The implementation of the ChargingReceiver class should now look like this:

package com.hathy.eventbussample;

import de.greenrobot.event.EventBus;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.text.format.Time;

public class ChargingReceiver extends BroadcastReceiver {

    private EventBus bus = EventBus.getDefault(); 
	
	@Override
	public void onReceive(Context context, Intent intent) {
		ChargingEvent event = null;
		
		// Get current time
		Time now = new Time();
		now.setToNow();		
		String timeOfEvent = now.format("%H:%M:%S");
		
		String eventData = "@" + timeOfEvent + " this device started ";
		if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)){
			event=new ChargingEvent(eventData+"charging.");
		} else if(intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)){
			event=new ChargingEvent(eventData+"discharging.");
		}
		
		// Post the event
		bus.post(event);
	}

}

As you can see, publishing messages to the event bus takes just a single line of code. Moreover, the publisher doesn't need to know anything about the subscriber(s).

6. Create DisplayActivity Class

Create a new class named DisplayActivity. This class is responsible for displaying the messages of the events published on the event bus.

This class too has a variable that references the event bus. As the EventBus library follows the singleton pattern, the instance of the event bus available to this Activity is the same as the instance available to the BroadcastReceiver.

To allow a class to subscribe to events on the bus, the register method is invoked. In our Activity, we call it in the onCreate method.

Similarly, to stop receiving events, the unregister method is invoked. We call this method in the onDestroy method to make sure all resources are freed.

The Activity has a very basic layout, containing only a TextView that displays the messages. There's therefore no need to create a layout for it. We simply use the TextView as the content view of the Activity.

At this point, the implementation of the DisplayActivity class should look like this:

package com.hathy.eventbussample;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import de.greenrobot.event.EventBus;

public class DisplayActivity extends Activity {
    
	private EventBus bus = EventBus.getDefault();
	
	private TextView view;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {	
		super.onCreate(savedInstanceState);
		
		view = new TextView(this);

		view.setTextSize(20f);
		view.setPadding(20, 20, 20, 20);
		view.setText("Waiting for events...");
		
		setContentView(view);

		// Register as a subscriber
		bus.register(this);
	}	
	
	@Override
	protected void onDestroy() {
		// Unregister
		bus.unregister(this);
		super.onDestroy();
	}

}

Every class that intends to receive events from the event bus should contain an onEvent method. The name of this method is important, because the EventBus library uses the Java Reflection API to access this method. It has a single parameter that refers to the event. In our case, the parameter is going to be of type ChargingEvent.

All we do in this method is append the last received message to the contents of the TextView. The implementation of the onEvent method looks like this:

public void onEvent(ChargingEvent event){
    view.setText(view.getText() + "\n" + event.getData());
}

7. Run and Test

The app is now ready to be tested. Compile and run it on a physical Android device. Once the app has finished launching, plug and unplug the power cord a couple of times to see the charging status changing.

Conclusion

In this tutorial, you've learnt how to use the EventBus library and how much it simplifies the communication between classes. The library is optimized for the Android platform and is very lightweight. This means that you can use it in your projects without having to worry about the size of your app. To learn more about the EventBus library, visit the project on GitHub.

2014-12-08T15:45:48.000Z2014-12-08T15:45:48.000ZAshraff Hathibelagal

Quick Tip: How to Use the EventBus Library

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22694
Final product image
What You'll Be Creating

Building an Android app that has multiple active components communicating with each other can get tedious. To save time, developers often end up with tightly coupled components in their apps. EventBus is a popular open-source library that was created to solve this problem using the publisher/subscriber pattern.

Using the EventBus library, you can pass messages from one class to one or more classes in just a few lines of code. Moreover, all classes involved are completely decoupled from each other, leading to code that is less complex, and easier to maintain and debug.

In this quick tip, you are going to learn how to use the EventBus library by building a simple Android application that displays the charging status of the device. Because changes in the charging status are system events, the app is going to have an Activity that needs to receive information from a BroadcastReceiver—the perfect scenario for using an event bus.

Prerequisites

Ensure that you have the Eclipse ADT Bundle set up. You can download it from the Android Developer website.

1. Create New Project

Start Eclipse and create a new Android application. Name the application EventBusSample. Choose a unique package name and set the Minimum Required SDK to Android2.2 and the Target SDK to Android4.4.

Create a new Android application

We're going to create the Activity ourselves, so deselect Create Activity and click Finish.

Deselect Create Activity and click Finish

2. Edit Manifest

This app has one BroadcastReceiver that responds to the following actions:

  • android.intent.action.ACTION_POWER_CONNECTED
  • android.intent.action.ACTION_POWER_DISCONNECTED

Name the BroadcastReceiver ChargingReceiver and declare it in the AndroidManifest.xml file.

<receiver android:name=".ChargingReceiver"><intent-filter><action android:name="android.intent.action.ACTION_POWER_CONNECTED" /><action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" /></intent-filter></receiver>

The app has one Activity to display the charging status. Name it DisplayActivity and declare it as shown below.

<activity android:name=".DisplayActivity"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER" /></intent-filter>            </activity>

3. Add EventBus Library

Download the latest version of the EventBus library as a JAR from Maven Central and add the JAR in your project's libs directory.

4. Create ChargingEvent Class

Events on the event bus are nothing but objects containing the information that needs to be communicated. Let ChargingEvent be the name of the class that contains the information passed from the BroadcastReceiver to the Activity. This is a simple class that has only a String to represent the information. It also has a constructor to set the value of the string and an accessor to get its value.

Create a new file named ChargingEvent.java and add the following code to it:

package com.hathy.eventbussample;

public class ChargingEvent {
    private String data;
	
	public ChargingEvent(String data){			
		this.data = data;
	}
	
	public String getData(){
		return data;
	}
}

5. Create BroadcastReceiver Class

Create a new class named ChargingReceiver that extends BroadcastReceiver. This class uses the event bus to publish messages. It has a variable named bus, which references the bus created by the EventBus library. The bus is a singleton and you have to use the getDefault method to refer to it.

In the onReceive method, we create a new instance of the ChargingEvent class and add our message to it. Here is a sample message:

@14:23:20 this device started charging.

To generate this message, we need to do the following:

  • Use the methods available in the Time class to set the time at which the event occurred.
  • Append the string "this device started charging | discharging" based on the action received. If the action received is Intent.ACTION_POWER_CONNECTED, the device is charging. If it is Intent.ACTION_POWER_DISCONNECTED, the device is discharging.

Once the ChargingEvent object has the right information, it is published on the event bus using the post method. The implementation of the ChargingReceiver class should now look like this:

package com.hathy.eventbussample;

import de.greenrobot.event.EventBus;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.text.format.Time;

public class ChargingReceiver extends BroadcastReceiver {

    private EventBus bus = EventBus.getDefault(); 
	
	@Override
	public void onReceive(Context context, Intent intent) {
		ChargingEvent event = null;
		
		// Get current time
		Time now = new Time();
		now.setToNow();		
		String timeOfEvent = now.format("%H:%M:%S");
		
		String eventData = "@" + timeOfEvent + " this device started ";
		if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)){
			event=new ChargingEvent(eventData+"charging.");
		} else if(intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)){
			event=new ChargingEvent(eventData+"discharging.");
		}
		
		// Post the event
		bus.post(event);
	}

}

As you can see, publishing messages to the event bus takes just a single line of code. Moreover, the publisher doesn't need to know anything about the subscriber(s).

6. Create DisplayActivity Class

Create a new class named DisplayActivity. This class is responsible for displaying the messages of the events published on the event bus.

This class too has a variable that references the event bus. As the EventBus library follows the singleton pattern, the instance of the event bus available to this Activity is the same as the instance available to the BroadcastReceiver.

To allow a class to subscribe to events on the bus, the register method is invoked. In our Activity, we call it in the onCreate method.

Similarly, to stop receiving events, the unregister method is invoked. We call this method in the onDestroy method to make sure all resources are freed.

The Activity has a very basic layout, containing only a TextView that displays the messages. There's therefore no need to create a layout for it. We simply use the TextView as the content view of the Activity.

At this point, the implementation of the DisplayActivity class should look like this:

package com.hathy.eventbussample;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import de.greenrobot.event.EventBus;

public class DisplayActivity extends Activity {
    
	private EventBus bus = EventBus.getDefault();
	
	private TextView view;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {	
		super.onCreate(savedInstanceState);
		
		view = new TextView(this);

		view.setTextSize(20f);
		view.setPadding(20, 20, 20, 20);
		view.setText("Waiting for events...");
		
		setContentView(view);

		// Register as a subscriber
		bus.register(this);
	}	
	
	@Override
	protected void onDestroy() {
		// Unregister
		bus.unregister(this);
		super.onDestroy();
	}

}

Every class that intends to receive events from the event bus should contain an onEvent method. The name of this method is important, because the EventBus library uses the Java Reflection API to access this method. It has a single parameter that refers to the event. In our case, the parameter is going to be of type ChargingEvent.

All we do in this method is append the last received message to the contents of the TextView. The implementation of the onEvent method looks like this:

public void onEvent(ChargingEvent event){
    view.setText(view.getText() + "\n" + event.getData());
}

7. Run and Test

The app is now ready to be tested. Compile and run it on a physical Android device. Once the app has finished launching, plug and unplug the power cord a couple of times to see the charging status changing.

Conclusion

In this tutorial, you've learnt how to use the EventBus library and how much it simplifies the communication between classes. The library is optimized for the Android platform and is very lightweight. This means that you can use it in your projects without having to worry about the size of your app. To learn more about the EventBus library, visit the project on GitHub.

2014-12-08T15:45:48.000Z2014-12-08T15:45:48.000ZAshraff Hathibelagal

Working With Isolated Storage on Windows Phone 8

$
0
0

Isolated storage is used to store local data on a Windows Phone. It is "isolated" because other applications can't access this data. In this tutorial you'll get an overview of isolated storage and learn how you can use it to store data more securely on Windows Phone 8.

The mobile architecture of isolated storage is similar to the Silverlight-based applications on Windows. All I/O operations are restricted to isolated storage and do not have direct access to the underlying OS file system, which helps to provide security and prevents unauthorized access and data corruption from other apps. If you want to share data between two applications, you will need some kind of cloud-based service that can share that data for you. 

The local folder is the root folder of your app's data store. There are two ways to store data locally. The first way is through a collection of name/value pairs called IsolatedStorageSettings. The other way is through the creation of actual files and folders called IsolatedStorageFile. Here are a few things worth mentioning about isolated storage:

1. URI Schemes

Use the isostore or ms-appdata URI scheme names when addressing the local folder in a path. Both of these URI schemes let you access the local folder, but they cannot be used interchangeably. ms-appdata is used to address the root of the local folder with APIs, while isostore is used to address the root of the local folder. The following example demonstrates this. 

// Create a local database in the local folder with the isostore URI scheme.
MyDataContext db = new MyDataContext("isostore:/mydb.sdf");

// Get a file from the local folder with the ms-appdata URI scheme.
var file = await Windows.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appdata:///local/AppConfigSettings.xml"));

ms-appdata requires three slashes (///) and isostore requires only one slash (/). The total length of the path for any of the two URI schemes can't exceed 185 characters.

2. IsolatedStorageSettings

The simplest way to put data into isolated storage is to use the IsolatedStorageSettings class, which is a Dictionary<TKey, TValue> that stores key-value pairs in isolated storage. IsolatedStorageSettings is typically used to save settings, such as the number of images to display per page, page layout options, and so on. The data saved in IsolatedStorageSettings persists across application launches.

If you just want to store settings information like Username = "Fred", then you can use the ApplicationSettings object in isolated storage. It is used in the same way as you would use a dictionary. The saveString method can be used to save a string value message for the key name.

void saveString(string message, string name)
{
    IsolatedStorageSettings.ApplicationSettings[name] = message;
    IsolatedStorageSettings.ApplicationSettings.Save();
}

The storage works as a dictionary but remember to call Save when you have finished adding keys.

To retrieve values from settings, you can use the loadString method that takes the key of the saved settings as a parameter and returns the value if the key exists.

string loadString(string name)
{
    if (IsolatedStorageSettings.ApplicationSettings.Contains(name))
    {
    return (string)IsolatedStorageSettings.ApplicationSettings[name];
    }
    else
    {
    return null;
}
} 

Test if the key exists before you try to find it. If you're trying to get the value for a key that doesn't exist, an exception will be thrown.

A good practice is to create a special static class, that contains your application's settings. This makes it easy to access any property in your application at any time.

If you're working on a Universal Windows app project, then using IsolatedStorageSettings.ApplicationSettings will give a syntax error. You need to replace it with Windows.Storage.ApplicationData.Current.LocalSettings.

3.IsolatedStorageFile

IsolatedStorageFile is the mechanism that you can use to store files on a user's device. You can perform various operations on the isolated storage, such as creating folders and files, writing to a file, reading data, removing files, etc.

These files and folders are not accessible to other applications installed on the user's device. The IsolatedStorageFileStream class is used to read, write, and create files in isolated storage. This class extends FileStream, which means you can use an instance of IsolatedStorageFileStream in most situations where a FileStream instance might otherwise be used, such as to construct a StreamReader or StreamWriter.

Writing To a File 

The following code snippet shows you how to write to a file in isolated storage. The saveGameToIsolatedStorage function creates a new file in isolated storage and saves the string message in it.

private void saveGameToIsolatedStorage(string message)
{
    using (IsolatedStorageFile isf =
    IsolatedStorageFile.GetUserStoreForApplication())
    {
       using (IsolatedStorageFileStream rawStream = isf.CreateFile("MyFile.store"))
    {
        StreamWriter writer = new StreamWriter(rawStream);
        writer.WriteLine(message); // save the message
        writer.Close();
    }
    }
} 

Reading From a File

The loadString function reads and returns the text contained in the file. The function uses FileExists to first check if the file exists in isolated storage. It then uses an instance of StreamReader to read the file.

private string loadString()
{
    string result = null;
    using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
    {
        if (isf.FileExists("Myfile.store")
        {
            using (IsolatedStorageFileStream rawStream = isf.OpenFile(filename, 
            System.IO.FileMode.Open)) 
            {
                StreamReader reader = new StreamReader(rawStream);
                result = reader.ReadLine();
                reader.Close();
            }
        }
    }
    return result;
} 

Isolated storage is not available for Windows Store apps. Instead, use the application data classes in the Windows.Storage namespaces included in the Windows Runtime API to store local data and files.

It is recommended to dispose off the instances of IsolatedStorageFile and IsolatedStorageFileStream when they're no longer needed. The using statement does this automatically for you and its use is considered a good practice.

Writing to an Existing File

To overwrite the contents of an existing file, use an instance of the StreamWriter class to open the file. The parameters FileMode.Open and FileAccess.Write are passed to open the file with write access. This will overwrite the existing content with new data.

IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
if (myIsolatedStorage.FileExists(filename))
{
    using (StreamWriter writeFile = new StreamWriter(new IsolatedStorageFileStream(filename, FileMode.Open, FileAccess.Write, myIsolatedStorage)))
    {
        string someTextData = "Learn to code using Tuts+";
        writeFile.WriteLine(someTextData);
        writeFile.Close();
}
}

Appending to an Existing File

Adding data to an existing file is very similar to writing data to an existing file. The only change needed is setting the file mode to FileMode.Append.

IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
if (myIsolatedStorage.FileExists(filename))
{
    if (myIsolatedStorage.FileExists(filename))
    {
        using (StreamWriter writeFile = new StreamWriter(new IsolatedStorageFileStream(filename, FileMode.Append, FileAccess.Write, myIsolatedStorage)))
        {
            string someTextData = "Use Tuts+ to Learn Creative Skills, Shape Your Future";
            writeFile.WriteLine(someTextData);
            writeFile.Close();
        }
    }
}

Deleting a Text File

To delete a text file, we first check if the text file exists in isolated storage and then use DeleteFile to delete the file.

IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
if (myIsolatedStorage.FileExists(filename))
{
    myIsolatedStorage.DeleteFile(filename);
}

I encourage you to explore the sample application of this tutorial to see how to read, write, and append data to a file.

4. Isolated Storage Explorer

While debugging an application, you may need to check the files and folders saved in your app's isolated storage to verify that the correct files are being saved in the correct location. Emulators and devices running Windows Phone 8 or lower can use Windows Phone Power Tools, which is a GUI-based tool for accessing the isolated storage of apps.

Another option is using the Isolated Storage Explorer or ISETool, a command line tool installed together with the Windows Phone SDK. You can use ISETool to list, copy, and replace files and directories in your app's local folder.

ISETool can be be used with any device or emulator, and is usually installed at the following location:

Program Files(x86)\MicrosoftSDKs\WindowsPhone\v8.0\Tools\IsolatedStorageExplorerTool

Here are a few things worth noting while using ISETool:

  • The app must be installed on the emulator or device.
  • The emulator or device must be unlocked, but the app doesn't have to be running.
  • You can't access the isolated storage of apps installed from the Windows Phone Store.
  • You can't view settings stored using the IsolatedStorageSettings class using ISETool.

To use ISETool, you need to use the following syntax:

ISETool.exe <cmd[:param]> <target-device[:param]> <product-id> [<desktop-path>]

Here are a couple of things that can be done using ISETool.

Copying Files From Isolated Storage to Computer

  1. Deploy the app you want to test to the emulator or a device, and create local files and directories. 
  2. Get the app ID from the ProductID attribute of the app element in the WMAppManifest.xml file.
  3. Navigate to ISETool.exe using the command prompt, and run the following command to copy all of the files from the app's isolated storage to your computer.
ISETool.exe ts xd f8ce6878-0aeb-497f-bcf4-65be961d4bba c:\data\myfiles

Replacing Files in Isolated Storage

Repeat the previous three steps and use the following command to replace files in the app's isolated storage.

ISETool.exe rs xd f8ce6878-0aeb-497f-bcf4-65be961d4bba “C:\Data\My Files”

If you want to learn more about the ISETool, I suggest you read another article I wrote about the use of the ISETool.

Conclusion

We have two simple mechanisms available on Windows Phone, IsolatedStorageSettings and IsolatedStorageFile. Isolated storage represents a storage area containing files and directories which can't be accessed by other applications. Isolated storage is useful in many situations. Feel free to download the tutorial's source files to use as a reference.

2014-12-10T15:20:09.000Z2014-12-10T15:20:09.000ZVivek Maskara

Working With Isolated Storage on Windows Phone 8

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22778

Isolated storage is used to store local data on a Windows Phone. It is "isolated" because other applications can't access this data. In this tutorial you'll get an overview of isolated storage and learn how you can use it to store data more securely on Windows Phone 8.

The mobile architecture of isolated storage is similar to the Silverlight-based applications on Windows. All I/O operations are restricted to isolated storage and do not have direct access to the underlying OS file system, which helps to provide security and prevents unauthorized access and data corruption from other apps. If you want to share data between two applications, you will need some kind of cloud-based service that can share that data for you. 

The local folder is the root folder of your app's data store. There are two ways to store data locally. The first way is through a collection of name/value pairs called IsolatedStorageSettings. The other way is through the creation of actual files and folders called IsolatedStorageFile. Here are a few things worth mentioning about isolated storage:

1. URI Schemes

Use the isostore or ms-appdata URI scheme names when addressing the local folder in a path. Both of these URI schemes let you access the local folder, but they cannot be used interchangeably. ms-appdata is used to address the root of the local folder with APIs, while isostore is used to address the root of the local folder. The following example demonstrates this. 

// Create a local database in the local folder with the isostore URI scheme.
MyDataContext db = new MyDataContext("isostore:/mydb.sdf");

// Get a file from the local folder with the ms-appdata URI scheme.
var file = await Windows.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appdata:///local/AppConfigSettings.xml"));

ms-appdata requires three slashes (///) and isostore requires only one slash (/). The total length of the path for any of the two URI schemes can't exceed 185 characters.

2. IsolatedStorageSettings

The simplest way to put data into isolated storage is to use the IsolatedStorageSettings class, which is a Dictionary<TKey, TValue> that stores key-value pairs in isolated storage. IsolatedStorageSettings is typically used to save settings, such as the number of images to display per page, page layout options, and so on. The data saved in IsolatedStorageSettings persists across application launches.

If you just want to store settings information like Username = "Fred", then you can use the ApplicationSettings object in isolated storage. It is used in the same way as you would use a dictionary. The saveString method can be used to save a string value message for the key name.

void saveString(string message, string name)
{
    IsolatedStorageSettings.ApplicationSettings[name] = message;
    IsolatedStorageSettings.ApplicationSettings.Save();
}

The storage works as a dictionary but remember to call Save when you have finished adding keys.

To retrieve values from settings, you can use the loadString method that takes the key of the saved settings as a parameter and returns the value if the key exists.

string loadString(string name)
{
    if (IsolatedStorageSettings.ApplicationSettings.Contains(name))
    {
    return (string)IsolatedStorageSettings.ApplicationSettings[name];
    }
    else
    {
    return null;
}
} 

Test if the key exists before you try to find it. If you're trying to get the value for a key that doesn't exist, an exception will be thrown.

A good practice is to create a special static class, that contains your application's settings. This makes it easy to access any property in your application at any time.

If you're working on a Universal Windows app project, then using IsolatedStorageSettings.ApplicationSettings will give a syntax error. You need to replace it with Windows.Storage.ApplicationData.Current.LocalSettings.

3.IsolatedStorageFile

IsolatedStorageFile is the mechanism that you can use to store files on a user's device. You can perform various operations on the isolated storage, such as creating folders and files, writing to a file, reading data, removing files, etc.

These files and folders are not accessible to other applications installed on the user's device. The IsolatedStorageFileStream class is used to read, write, and create files in isolated storage. This class extends FileStream, which means you can use an instance of IsolatedStorageFileStream in most situations where a FileStream instance might otherwise be used, such as to construct a StreamReader or StreamWriter.

Writing To a File 

The following code snippet shows you how to write to a file in isolated storage. The saveGameToIsolatedStorage function creates a new file in isolated storage and saves the string message in it.

private void saveGameToIsolatedStorage(string message)
{
    using (IsolatedStorageFile isf =
    IsolatedStorageFile.GetUserStoreForApplication())
    {
       using (IsolatedStorageFileStream rawStream = isf.CreateFile("MyFile.store"))
    {
        StreamWriter writer = new StreamWriter(rawStream);
        writer.WriteLine(message); // save the message
        writer.Close();
    }
    }
} 

Reading From a File

The loadString function reads and returns the text contained in the file. The function uses FileExists to first check if the file exists in isolated storage. It then uses an instance of StreamReader to read the file.

private string loadString()
{
    string result = null;
    using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
    {
        if (isf.FileExists("Myfile.store")
        {
            using (IsolatedStorageFileStream rawStream = isf.OpenFile(filename, 
            System.IO.FileMode.Open)) 
            {
                StreamReader reader = new StreamReader(rawStream);
                result = reader.ReadLine();
                reader.Close();
            }
        }
    }
    return result;
} 

Isolated storage is not available for Windows Store apps. Instead, use the application data classes in the Windows.Storage namespaces included in the Windows Runtime API to store local data and files.

It is recommended to dispose off the instances of IsolatedStorageFile and IsolatedStorageFileStream when they're no longer needed. The using statement does this automatically for you and its use is considered a good practice.

Writing to an Existing File

To overwrite the contents of an existing file, use an instance of the StreamWriter class to open the file. The parameters FileMode.Open and FileAccess.Write are passed to open the file with write access. This will overwrite the existing content with new data.

IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
if (myIsolatedStorage.FileExists(filename))
{
    using (StreamWriter writeFile = new StreamWriter(new IsolatedStorageFileStream(filename, FileMode.Open, FileAccess.Write, myIsolatedStorage)))
    {
        string someTextData = "Learn to code using Tuts+";
        writeFile.WriteLine(someTextData);
        writeFile.Close();
}
}

Appending to an Existing File

Adding data to an existing file is very similar to writing data to an existing file. The only change needed is setting the file mode to FileMode.Append.

IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
if (myIsolatedStorage.FileExists(filename))
{
    if (myIsolatedStorage.FileExists(filename))
    {
        using (StreamWriter writeFile = new StreamWriter(new IsolatedStorageFileStream(filename, FileMode.Append, FileAccess.Write, myIsolatedStorage)))
        {
            string someTextData = "Use Tuts+ to Learn Creative Skills, Shape Your Future";
            writeFile.WriteLine(someTextData);
            writeFile.Close();
        }
    }
}

Deleting a Text File

To delete a text file, we first check if the text file exists in isolated storage and then use DeleteFile to delete the file.

IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
if (myIsolatedStorage.FileExists(filename))
{
    myIsolatedStorage.DeleteFile(filename);
}

I encourage you to explore the sample application of this tutorial to see how to read, write, and append data to a file.

4. Isolated Storage Explorer

While debugging an application, you may need to check the files and folders saved in your app's isolated storage to verify that the correct files are being saved in the correct location. Emulators and devices running Windows Phone 8 or lower can use Windows Phone Power Tools, which is a GUI-based tool for accessing the isolated storage of apps.

Another option is using the Isolated Storage Explorer or ISETool, a command line tool installed together with the Windows Phone SDK. You can use ISETool to list, copy, and replace files and directories in your app's local folder.

ISETool can be be used with any device or emulator, and is usually installed at the following location:

Program Files(x86)\MicrosoftSDKs\WindowsPhone\v8.0\Tools\IsolatedStorageExplorerTool

Here are a few things worth noting while using ISETool:

  • The app must be installed on the emulator or device.
  • The emulator or device must be unlocked, but the app doesn't have to be running.
  • You can't access the isolated storage of apps installed from the Windows Phone Store.
  • You can't view settings stored using the IsolatedStorageSettings class using ISETool.

To use ISETool, you need to use the following syntax:

ISETool.exe <cmd[:param]> <target-device[:param]> <product-id> [<desktop-path>]

Here are a couple of things that can be done using ISETool.

Copying Files From Isolated Storage to Computer

  1. Deploy the app you want to test to the emulator or a device, and create local files and directories. 
  2. Get the app ID from the ProductID attribute of the app element in the WMAppManifest.xml file.
  3. Navigate to ISETool.exe using the command prompt, and run the following command to copy all of the files from the app's isolated storage to your computer.
ISETool.exe ts xd f8ce6878-0aeb-497f-bcf4-65be961d4bba c:\data\myfiles

Replacing Files in Isolated Storage

Repeat the previous three steps and use the following command to replace files in the app's isolated storage.

ISETool.exe rs xd f8ce6878-0aeb-497f-bcf4-65be961d4bba “C:\Data\My Files”

If you want to learn more about the ISETool, I suggest you read another article I wrote about the use of the ISETool.

Conclusion

We have two simple mechanisms available on Windows Phone, IsolatedStorageSettings and IsolatedStorageFile. Isolated storage represents a storage area containing files and directories which can't be accessed by other applications. Isolated storage is useful in many situations. Feel free to download the tutorial's source files to use as a reference.

2014-12-10T15:20:09.000Z2014-12-10T15:20:09.000ZVivek Maskara

Swift from Scratch: Variables and Constants

$
0
0

In the first article of Swift from Scratch, you learned about Xcode playgrounds and wrote your first lines of Swift. In this article, we start learning the fundamentals of the Swift programming language by exploring variables and typing. We will also take a close look at constants and why you're encouraged to use them as much as possible.

In the next installments of this series, we're going to make use of Xcode playgrounds to learn the fundamentals of the Swift programming language. As we've seen in the previous article, playgrounds are ideal for teaching and learning Swift.

Let's start by creating a new playground for this tutorial and I encourage you to follow along. Using a language is a great way to learn its syntax and understand its concepts.

Launch Xcode 6+ and create a new playground by selecting New > Playground... from Xcode's File menu. Enter a name for the playground, set Platform to iOS, and click Next. Tell Xcode where you'd like to save the playground and hit Create.

Clear the contents of the playground so we can start with a clean slate. We've already made use of variables in the previous tutorial, but let's now take a closer look at the nitty-gritty details to better understand what Swift is doing behind the scenes.

1. Variables

Declaring Variables

In Swift, we use the var keyword to declare a variable. While this is similar to how variables are declared in other programming languages, I strongly advise you to not think about other programming languages when using the var keyword in Swift. There are a few important differences.

The var keyword is the only way to declare a variable in Swift. The most common and concise use of the var keyword is to declare a variable and assign a value to it.

var street = "5th Avenue"

Remember that we don't end this line of code with a semicolon. While a semicolon is optional in Swift, the best practice is to not use a semicolon if it isn't required.

You also may have noticed that we didn't specify a type when declaring the variable street. This brings us to one of Swift's key features, type inference.

Type Inference

The above statement declares a variable street and assigns the value 5th Avenue to it. If you're new to Swift or you're used to JavaScript or PHP, then you may be thinking that Swift is a typeless or loosely typed language, but nothing could be further from the truth. Let me reiterate that Swift is a strongly typed language. Type safety is one of the cornerstones of the language.

We're just getting started and Swift already shows us a bit of its magic. Even though the above statement doesn't explicitly specify a type, the variable street is of type String. This is Swift's type inference in action. The value we assign to street is a string. Swift is smart enough to see that and implicitly sets the type of street to String.

The following statement gives us the same result. The difference is that we explicitly set the type of the variable. The above statement literally says that street is of typeString.

var street: String = "5th Avenue"

Swift requires you to explicitly or implicitly set the type of variables and constants. If you don't, Swift will complain by throwing an error. Add the following line to your playground to see what I mean.

var number

This statement would be perfectly valid in PHP or JavaScript. In Swift, however, it is invalid. The reason is simple. Even though we declare a variable using the var keyword, we don't specify the variable's type. Swift is unable to infer the type since we don't assign a value to the variable. If you click the error, Xcode will tell you what is wrong with this statement.

We can easily fix this issue by doing one of two things. We can assign a value to the variable as we did earlier or we can explicitly specify a type for the variable number. By explicitly setting the type of number, the error disappears. The below line of code reads that number is of type String.

var number: String

Changing Type

As you can see below, assigning new values to street and number is simple and comes with no surprises.

var street: String = "5th Avenue"
var number: String

street = "Main Street"
number = "10"

Wouldn't it be easier to assign the number 10 to the number variable. There's no need to store the street number as a string. Let's see what happens if we do.

var street: String = "5th Avenue"
var number: String

street = "Main Street"
number = 10

If we assign an integer to number, Xcode will throw another error at us. Unfortunately, this time the error message isn't particularly helpful. The reason Xcode throws an error is that we try to assign an integer to a variable of type String. This isn't a problem in loosely typed languages, but it is in Swift.

Swift is a strongly typed language in which every variable has a specific type and that type cannot be changed. Read this sentence again to let it sink in, because this is an important concept in Swift.

To get rid of the error, we need to declare the number variable as an Int. Take a look at the updated example below.

var street: String = "5th Avenue"
var number: Int

street = "Main Street"
number = 10

Summary

It's important that you keep the following in mind. You can declare a variable with the var keyword and you don't need to explicitly declare the variable's type. However, remember that every variable—and constant—has a type in Swift. If Swift can't infer the type, then it will complain. Every variable has a type and that type cannot be changed.

2. Constants

Constants are similar to variables in terms of typing. The only difference is that the value of a constant cannot be changed once it has a value. The value of a constant is ... well ... constant.

Declaring Constants

To declare a constant, you use the let keyword. Take a look at the following example in which we declare street as a constant instead of a variable.

let street: String = "5th Avenue"
var number: Int

street = "Main Street"
number = 10

If we only update the first line, by replacing var with let, Xcode throws an error for obvious reasons. Trying to change the value of a constant is not allowed in Swift. Remove or comment out the line in which we try to assign a new value to street to get rid of the error.

Using Constants

I hope you agree that declaring constants is very easy in Swift. There's no need for exotic keywords or a complex syntax. Declaring constants is just as easy as declaring variables and that isn't a coincidence.

The use of constants is encouraged in Swift. If a value isn't going to change or you don't expect it to change, then it should be a constant. This has a number of benefits. One of the benefits is performance, but a more important benefit is safety. By using constants whenever possible, you add constraints to your code and that will result in safer code.

3. Data Types

Most programming languages have a wide range of types for storing strings, integers, floats, etc. The available types in Swift are limited. Take a look at the following list.

  • Int
  • Float
  • Double
  • String
  • Character
  • Bool

It's important to understand that the above types are not basic or primitive types. They are named types, which are implemented by Swift using structures. We will explore structures in more detail later in this series, but it's good to know that the types we've seen so far aren't the same as the primitive types you may have used in, for example, Objective-C.

There are many other data types in Swift, such as tuples, arrays, and, dictionaries, and we'll explore those a bit later in this series.

4. Type Conversion

There is one more topic that we need to discuss, type conversion. Take a look at the following Objective-C snippet. This code block will output the value 314.000000 to the console.

int a = 100;
float b = 3.14;

NSLog(@"%f", (a * b));

The Objective-C runtime will implicitly convert a to a floating point value and multiply it with b. Let's rewrite the above code snippet using Swift.

var a = 100
var b = 3.14

println(a * b)

Ignore the println function for now. I first want to focus on the multiplication of a and b. Swift infers the type of a, an Int, and b, a Double. However, when it tries to multiply a and b, it notices that they aren't of the same type. This may not seem like a problem to you, but it is for Swift. Swift doesn't know what type the result of this multiplication should be. Should it be an integer or a double.

To fix this issue, we need to make sure that both operands of the multiplication are of the same type, because Swift doesn't implicitly convert the operands for us. There are a number of globally available helper functions that make this task trivial. In the updated example below, we call Double on the variable a to convert its value a double. This resolves the error.

var a = 100
var b = 3.14

println(Double(a) * b)

Note that the type of a hasn't changed. Even though the above code snippet may look like type casting, it is not the same thing. The Double function takes the value of a, returns its value as a Double and that result is used in the multiplication with b. The result is of type Double.

What you need to remember is that Swift is different from C and Objective-C. It doesn't implicitly convert values of variables and constants. This is another important concept to grasp.

5. Print Line

In the previous code snippet, we invoked our first function, println. The println function is very similar to Objective-C's NSLog, it prints something and appends a new line. The Swift standard library also includes a print function, which does the exact same thing as the println function, but doesn't append a new line.

To print something, you simply invoke println and pass it a parameter. This can be a variable, a constant, an expression, or a literal. Take a look at the following examples.

var string = "this is a string"
let constant = 3.14

println(string)
println(constant)
println(10 * 50)
println("string literal")

It's also possible to use string interpolation to combine variables, constants, expressions, and literals. String interpolation is very easy in Swift, simply wrap the variables, constants, expressions, or literals in \(). Easy as pie.

let street = "5th Avenue"
let number = 10

println("She lives at \(street) \(number).")

Conclusion

It is key that you understand how Swift handles variables and constants. While it may take some time getting used to the concept of constants, once you've embraced this best practice, your code will be much safer and easier to understand. In the next tutorial of this series, we continue our exploration of Swift by looking at collections.

2014-12-12T18:15:02.000Z2014-12-12T18:15:02.000ZBart Jacobs

Swift from Scratch: Variables and Constants

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/cms-22828

In the first article of Swift from Scratch, you learned about Xcode playgrounds and wrote your first lines of Swift. In this article, we start learning the fundamentals of the Swift programming language by exploring variables and typing. We will also take a close look at constants and why you're encouraged to use them as much as possible.

In the next installments of this series, we're going to make use of Xcode playgrounds to learn the fundamentals of the Swift programming language. As we've seen in the previous article, playgrounds are ideal for teaching and learning Swift.

Let's start by creating a new playground for this tutorial and I encourage you to follow along. Using a language is a great way to learn its syntax and understand its concepts.

Launch Xcode 6+ and create a new playground by selecting New > Playground... from Xcode's File menu. Enter a name for the playground, set Platform to iOS, and click Next. Tell Xcode where you'd like to save the playground and hit Create.

Clear the contents of the playground so we can start with a clean slate. We've already made use of variables in the previous tutorial, but let's now take a closer look at the nitty-gritty details to better understand what Swift is doing behind the scenes.

1. Variables

Declaring Variables

In Swift, we use the var keyword to declare a variable. While this is similar to how variables are declared in other programming languages, I strongly advise you to not think about other programming languages when using the var keyword in Swift. There are a few important differences.

The var keyword is the only way to declare a variable in Swift. The most common and concise use of the var keyword is to declare a variable and assign a value to it.

var street = "5th Avenue"

Remember that we don't end this line of code with a semicolon. While a semicolon is optional in Swift, the best practice is to not use a semicolon if it isn't required.

You also may have noticed that we didn't specify a type when declaring the variable street. This brings us to one of Swift's key features, type inference.

Type Inference

The above statement declares a variable street and assigns the value 5th Avenue to it. If you're new to Swift or you're used to JavaScript or PHP, then you may be thinking that Swift is a typeless or loosely typed language, but nothing could be further from the truth. Let me reiterate that Swift is a strongly typed language. Type safety is one of the cornerstones of the language.

We're just getting started and Swift already shows us a bit of its magic. Even though the above statement doesn't explicitly specify a type, the variable street is of type String. This is Swift's type inference in action. The value we assign to street is a string. Swift is smart enough to see that and implicitly sets the type of street to String.

The following statement gives us the same result. The difference is that we explicitly set the type of the variable. The above statement literally says that street is of typeString.

var street: String = "5th Avenue"

Swift requires you to explicitly or implicitly set the type of variables and constants. If you don't, Swift will complain by throwing an error. Add the following line to your playground to see what I mean.

var number

This statement would be perfectly valid in PHP or JavaScript. In Swift, however, it is invalid. The reason is simple. Even though we declare a variable using the var keyword, we don't specify the variable's type. Swift is unable to infer the type since we don't assign a value to the variable. If you click the error, Xcode will tell you what is wrong with this statement.

We can easily fix this issue by doing one of two things. We can assign a value to the variable as we did earlier or we can explicitly specify a type for the variable number. By explicitly setting the type of number, the error disappears. The below line of code reads that number is of type String.

var number: String

Changing Type

As you can see below, assigning new values to street and number is simple and comes with no surprises.

var street: String = "5th Avenue"
var number: String

street = "Main Street"
number = "10"

Wouldn't it be easier to assign the number 10 to the number variable. There's no need to store the street number as a string. Let's see what happens if we do.

var street: String = "5th Avenue"
var number: String

street = "Main Street"
number = 10

If we assign an integer to number, Xcode will throw another error at us. Unfortunately, this time the error message isn't particularly helpful. The reason Xcode throws an error is that we try to assign an integer to a variable of type String. This isn't a problem in loosely typed languages, but it is in Swift.

Swift is a strongly typed language in which every variable has a specific type and that type cannot be changed. Read this sentence again to let it sink in, because this is an important concept in Swift.

To get rid of the error, we need to declare the number variable as an Int. Take a look at the updated example below.

var street: String = "5th Avenue"
var number: Int

street = "Main Street"
number = 10

Summary

It's important that you keep the following in mind. You can declare a variable with the var keyword and you don't need to explicitly declare the variable's type. However, remember that every variable—and constant—has a type in Swift. If Swift can't infer the type, then it will complain. Every variable has a type and that type cannot be changed.

2. Constants

Constants are similar to variables in terms of typing. The only difference is that the value of a constant cannot be changed once it has a value. The value of a constant is ... well ... constant.

Declaring Constants

To declare a constant, you use the let keyword. Take a look at the following example in which we declare street as a constant instead of a variable.

let street: String = "5th Avenue"
var number: Int

street = "Main Street"
number = 10

If we only update the first line, by replacing var with let, Xcode throws an error for obvious reasons. Trying to change the value of a constant is not allowed in Swift. Remove or comment out the line in which we try to assign a new value to street to get rid of the error.

Using Constants

I hope you agree that declaring constants is very easy in Swift. There's no need for exotic keywords or a complex syntax. Declaring constants is just as easy as declaring variables and that isn't a coincidence.

The use of constants is encouraged in Swift. If a value isn't going to change or you don't expect it to change, then it should be a constant. This has a number of benefits. One of the benefits is performance, but a more important benefit is safety. By using constants whenever possible, you add constraints to your code and that will result in safer code.

3. Data Types

Most programming languages have a wide range of types for storing strings, integers, floats, etc. The available types in Swift are limited. Take a look at the following list.

  • Int
  • Float
  • Double
  • String
  • Character
  • Bool

It's important to understand that the above types are not basic or primitive types. They are named types, which are implemented by Swift using structures. We will explore structures in more detail later in this series, but it's good to know that the types we've seen so far aren't the same as the primitive types you may have used in, for example, Objective-C.

There are many other data types in Swift, such as tuples, arrays, and, dictionaries, and we'll explore those a bit later in this series.

4. Type Conversion

There is one more topic that we need to discuss, type conversion. Take a look at the following Objective-C snippet. This code block will output the value 314.000000 to the console.

int a = 100;
float b = 3.14;

NSLog(@"%f", (a * b));

The Objective-C runtime will implicitly convert a to a floating point value and multiply it with b. Let's rewrite the above code snippet using Swift.

var a = 100
var b = 3.14

println(a * b)

Ignore the println function for now. I first want to focus on the multiplication of a and b. Swift infers the type of a, an Int, and b, a Double. However, when it tries to multiply a and b, it notices that they aren't of the same type. This may not seem like a problem to you, but it is for Swift. Swift doesn't know what type the result of this multiplication should be. Should it be an integer or a double.

To fix this issue, we need to make sure that both operands of the multiplication are of the same type, because Swift doesn't implicitly convert the operands for us. There are a number of globally available helper functions that make this task trivial. In the updated example below, we call Double on the variable a to convert its value a double. This resolves the error.

var a = 100
var b = 3.14

println(Double(a) * b)

Note that the type of a hasn't changed. Even though the above code snippet may look like type casting, it is not the same thing. The Double function takes the value of a, returns its value as a Double and that result is used in the multiplication with b. The result is of type Double.

What you need to remember is that Swift is different from C and Objective-C. It doesn't implicitly convert values of variables and constants. This is another important concept to grasp.

5. Print Line

In the previous code snippet, we invoked our first function, println. The println function is very similar to Objective-C's NSLog, it prints something and appends a new line. The Swift standard library also includes a print function, which does the exact same thing as the println function, but doesn't append a new line.

To print something, you simply invoke println and pass it a parameter. This can be a variable, a constant, an expression, or a literal. Take a look at the following examples.

var string = "this is a string"
let constant = 3.14

println(string)
println(constant)
println(10 * 50)
println("string literal")

It's also possible to use string interpolation to combine variables, constants, expressions, and literals. String interpolation is very easy in Swift, simply wrap the variables, constants, expressions, or literals in \(). Easy as pie.

let street = "5th Avenue"
let number = 10

println("She lives at \(street) \(number).")

Conclusion

It is key that you understand how Swift handles variables and constants. While it may take some time getting used to the concept of constants, once you've embraced this best practice, your code will be much safer and easier to understand. In the next tutorial of this series, we continue our exploration of Swift by looking at collections.

2014-12-12T18:15:02.000Z2014-12-12T18:15:02.000ZBart Jacobs
Viewing all 1836 articles
Browse latest View live