<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:series="http://unfoldingneurons.com/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"> <channel><title>Mobiletuts+</title> <link>http://mobile.tutsplus.com</link> <description>iPhone, Android, Windows and BlackBerry mobile development tutorials.</description> <lastBuildDate>Fri, 17 May 2013 11:55:09 +0000</lastBuildDate> <language /> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.5</generator> <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/MobileTuts" /><feedburner:info uri="mobiletuts" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>MobileTuts</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item><title>Reading NFC Tags with Android</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/fAK8stCZ8eI/</link> <comments>http://mobile.tutsplus.com/tutorials/android/reading-nfc-tags-with-android/#comments</comments> <pubDate>Thu, 16 May 2013 17:32:40 +0000</pubDate> <dc:creator>Ralf Wondratschek</dc:creator> <category><![CDATA[Android SDK]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=17278</guid> <description>&lt;p&gt;Are you curious about what NFC is and how it can be integrated into your own Android applications? This tutorial will quickly introduce you to the topic before diving in and teaching you how to build a simple NFC reader app!&lt;br
/&gt;&lt;/p&gt;&lt;hr/&gt;&lt;h2&gt;What is NFC?&lt;/h2&gt;&lt;p&gt;NFC is the abbreviation for &lt;em&gt;Near Field Communication&lt;/em&gt;. It is the international standard for contactless exchange of data. In contrast to a large range of other technologies, such as wireless LAN and Bluetooth, the maximum distance of two devices is 10cm. The development of the standard started in 2002 by NXP Semiconductors and Sony. The &lt;a
href="http://www.nfc-forum.org/home/"&gt;NFC Forum&lt;/a&gt;, a consortium of over 170 companies and members, which included Mastercard, NXP, Nokia, Samsung, Intel, and Google, has been designing new specifications since 2004.&lt;/p&gt;&lt;p&gt;There are various possibilities for NFC use with mobile devices; for example, paperless tickets, access controls, cashless payments, and car keys. With the help of NFC tags you can control your phone and change settings. Data can be exchanged simply by holding two devices next to each other.&lt;/p&gt;&lt;p&gt;In this tutorial I want to explain how to implement NFC with the Android SDK, which pitfalls exist, and what to keep in mind. We will create an app step by step, which can read the content of NFC tags supporting NDEF.&lt;/p&gt;&lt;hr/&gt;&lt;h2&gt;NFC Technologies&lt;/h2&gt;&lt;p&gt;There are a variety of NFC tags that can be read with a smartphone. The spectrum ranges from simple stickers and key rings to complex cards with integrated cryptographic hardware. Tags also differ in their chip technology. The most important is NDEF, which is supported by most tags. In addidition, Mifare should be mentioned as it is the most used contactless chip technology worldwide. Some tags can be read and written, while others are read-only or encrypted.&lt;/p&gt;&lt;p&gt;Only the NFC Data Exchange Format (NDEF) is discussed in this tutorial.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/Reading-NFC-Tags-with-Android_2.jpg" alt="girogo-card" /&gt;&lt;br
/&gt; &lt;fig
caption="X-ray view of a girogo-card" /&gt;&lt;br
/&gt; &lt;/figure&gt;&lt;/p&gt;&lt;/hr&gt;&lt;h2&gt;Adding NFC Support in an App&lt;/h2&gt;&lt;p&gt;We start with a new project and a blank activity. It is important to select a minimum SDK version of level 10, because NFC is only supported after Android 2.3.3. Remember to choose your own package name. I&amp;#8217;ve chosen &lt;em&gt;net.vrallev.android.nfc.demo&lt;/em&gt;, because vrallev.net is the domain of my website and the other part refers to the topic of this application.&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
	&amp;lt;uses-sdk
		android:minSdkVersion=&amp;quot;10&amp;quot;
        android:targetSdkVersion=&amp;quot;17&amp;quot; /&amp;gt;
	&lt;/pre&gt;&lt;p&gt;The default layout generated by Eclipse is almost sufficient for us. I&amp;#8217;ve only added an ID to the TextView and changed the text.&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
	&amp;lt;TextView
        android:id=&amp;quot;@+id/textView_explanation&amp;quot;
        android:layout_width=&amp;quot;wrap_content&amp;quot;
        android:layout_height=&amp;quot;wrap_content&amp;quot;
        android:text=&amp;quot;@string/explanation&amp;quot; /&amp;gt;
	&lt;/pre&gt;&lt;p&gt;To get access to the NFC hardware, you have to apply for permission in the manifest. If the app won&amp;#8217;t work without NFC, you can specify the condition with the uses-feature tag. If NFC is required, the app can&amp;#8217;t be installed on devices without it and Google Play will only display your app to users who own a NFC device.&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
	&amp;lt;uses-permission android:name=&amp;quot;android.permission.NFC&amp;quot; /&amp;gt;
    &amp;lt;uses-feature
        android:name=&amp;quot;android.hardware.nfc&amp;quot;
        android:required=&amp;quot;true&amp;quot; /&amp;gt;
	&lt;/pre&gt;&lt;p&gt;The MainActivity should only consist of the onCreate() method. You can interact with the hardware via the NfcAdapter class. It is important to find out whether the NfcAdapter is null. In this case, the Android device does not support NFC.&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
	package net.vrallev.android.nfc.demo;
	import android.app.Activity;
	import android.nfc.NfcAdapter;
	import android.os.Bundle;
	import android.widget.TextView;
	import android.widget.Toast;
	/**
	 * Activity for reading data from an NDEF Tag.
	 *
	 * @author Ralf Wondratschek
	 *
	 */
	public class MainActivity extends Activity {
		public static final String TAG = &amp;quot;NfcDemo&amp;quot;;
		private TextView mTextView;
		private NfcAdapter mNfcAdapter;
		@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
			setContentView(R.layout.activity_main);
			mTextView = (TextView) findViewById(R.id.textView_explanation);
			mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
			if (mNfcAdapter == null) {
				// Stop here, we definitely need NFC
				Toast.makeText(this, &amp;quot;This device doesn't support NFC.&amp;quot;, Toast.LENGTH_LONG).show();
				finish();
				return;
			}
			if (!mNfcAdapter.isEnabled()) {
				mTextView.setText(&amp;quot;NFC is disabled.&amp;quot;);
			} else {
				mTextView.setText(R.string.explanation);
			}
			handleIntent(getIntent());
		}
		private void handleIntent(Intent intent) {
			// TODO: handle Intent
		}
	}
	&lt;/pre&gt;&lt;p&gt;If we start our app now, we can see the text whether NFC is enabled or disabled.&lt;/p&gt;&lt;/hr&gt;&lt;h2&gt;How to Filter for NFC Tags&lt;/h2&gt;&lt;p&gt;We have our sample app and want to receive a notification from the system when we attach an NFC tag to the device. As usual, Android uses its Intent system to deliver tags to the apps. If multiple apps can handle the Intent, the activity chooser gets displayed and the user can decide which app will be opened. Opening URLs or sharing information is handled the same way.&lt;/p&gt;&lt;hr/&gt;&lt;h2&gt;NFC Intent Filter&lt;/h2&gt;&lt;p&gt;There are three different filters for tags:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;ACTION_NDEF_DISCOVERED&lt;/li&gt;&lt;li&gt;ACTION_TECH_DISCOVERED&lt;/li&gt;&lt;li&gt;ACTION_TAG_DISCOVERED&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The list is sorted from the highest to the lowest priority.&lt;/p&gt;&lt;p&gt;Now what happens when a tag is attached to the smartphone? If the system detects a tag with NDEF support, an Intent is triggered. An &lt;em&gt;ACTION_TECH_DISCOVERED&lt;/em&gt; Intent is triggered if no Activity from any app is registered for the NDEF Intent or if the tag does not support NDEF. If again no app is found for the Intent or the chip technology could not be detected, then a &lt;em&gt;ACTION_TAG_DISCOVERED&lt;/em&gt; Intent is fired. The following graphic shows the process:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/Reading-NFC-Tags-with-Android_3.png" alt="nfc tag dispatch" /&gt;&lt;br
/&gt; &lt;/figure&gt;&lt;p&gt;In summary this means that each app needs to filter after the Intent with the highest priority. In our case, this is the NDEF Intent. We implement the &lt;em&gt;ACTION_TECH_DISCOVERED&lt;/em&gt; Intent first to highlight the difference between priorities.&lt;/p&gt;&lt;hr/&gt;&lt;h2&gt;Tech Discovered Intent&lt;/h2&gt;&lt;p&gt;We must specify the technology we are interested in. For this purpose, we create a subfolder called &lt;em&gt;xml&lt;/em&gt; in the &lt;em&gt;res&lt;/em&gt; folder. In this folder we create the file &lt;em&gt;nfc_tech_filter.xml&lt;/em&gt;, in which we specify the technologies.&lt;/p&gt;&lt;pre class="brush: xml; title: ;"&gt;
	&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;
	&amp;lt;resources xmlns:xliff=&amp;quot;urn:oasis:names:tc:xliff:document:1.2&amp;quot;&amp;gt;
	    &amp;lt;tech-list&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.Ndef&amp;lt;/tech&amp;gt;
	        &amp;lt;!-- class name --&amp;gt;
	    &amp;lt;/tech-list&amp;gt;
	&amp;lt;/resources&amp;gt;
	&amp;lt;!--
	&amp;lt;resources xmlns:xliff=&amp;quot;urn:oasis:names:tc:xliff:document:1.2&amp;quot;&amp;gt;
	    &amp;lt;tech-list&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.IsoDep&amp;lt;/tech&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.NfcA&amp;lt;/tech&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.NfcB&amp;lt;/tech&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.NfcF&amp;lt;/tech&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.NfcV&amp;lt;/tech&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.Ndef&amp;lt;/tech&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.NdefFormatable&amp;lt;/tech&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.MifareClassic&amp;lt;/tech&amp;gt;
	        &amp;lt;tech&amp;gt;android.nfc.tech.MifareUltralight&amp;lt;/tech&amp;gt;
	    &amp;lt;/tech-list&amp;gt;
	&amp;lt;/resources&amp;gt;
	--&amp;gt;
	&lt;/pre&gt;&lt;p&gt;Now we must create an IntentFilter in the manifest, and the app will be started when we attach a tag.&lt;/p&gt;&lt;pre class="brush: xml; title: ;"&gt;
	&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;
	&amp;lt;activity
		android:name=&amp;quot;net.vrallev.android.nfc.demo.MainActivity&amp;quot;
        android:label=&amp;quot;@string/app_name&amp;quot; &amp;gt;
        &amp;lt;intent-filter&amp;gt;
            &amp;lt;action android:name=&amp;quot;android.intent.action.MAIN&amp;quot; /&amp;gt;
            &amp;lt;category android:name=&amp;quot;android.intent.category.LAUNCHER&amp;quot; /&amp;gt;
        &amp;lt;/intent-filter&amp;gt;
        &amp;lt;intent-filter&amp;gt;
            &amp;lt;action android:name=&amp;quot;android.nfc.action.TECH_DISCOVERED&amp;quot; /&amp;gt;
        &amp;lt;/intent-filter&amp;gt;
        &amp;lt;meta-data
            android:name=&amp;quot;android.nfc.action.TECH_DISCOVERED&amp;quot;
            android:resource=&amp;quot;@xml/nfc_tech_filter&amp;quot; /&amp;gt;
    &amp;lt;/activity&amp;gt;
	&lt;/pre&gt;&lt;p&gt;If no other app is registered for this Intent, our Activity will start immediately. On my device, however, other apps are installed, so the activity chooser gets displayed.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/Reading-NFC-Tags-with-Android_4.png" alt="screenshot" /&gt;&lt;br
/&gt; &lt;/figure&gt;&lt;hr/&gt;&lt;h2&gt;NDEF Discovered Intent&lt;/h2&gt;&lt;p&gt;As I mentioned before, the Tech Discovered Intent has the second highest priority. However, since our app will support only NDEF, we can use the NDEF Discovered Intent instead, which has a higher priority. We can delete the technology list again and replace the IntentFilter with the following one.&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
	&amp;lt;intent-filter&amp;gt;
		&amp;lt;action android:name=&amp;quot;android.nfc.action.NDEF_DISCOVERED&amp;quot; /&amp;gt;
		&amp;lt;category android:name=&amp;quot;android.intent.category.DEFAULT&amp;quot; /&amp;gt;
		&amp;lt;data android:mimeType=&amp;quot;text/plain&amp;quot; /&amp;gt;
	&amp;lt;/intent-filter&amp;gt;
	&lt;/pre&gt;&lt;p&gt;When we attach the tag now, the app will be started like before. There is a difference for me, however. The activity chooser does not appear and the app starts immediately, because the NDEF Intent has a higher priority and the other apps only registered for the lower priorities. That&amp;#8217;s exactly what we want.&lt;/p&gt;&lt;hr/&gt;&lt;h2&gt;Foreground Dispatch&lt;/h2&gt;&lt;p&gt;Note that one problem remains. When our app is already opened and we attach the tag again, the app is opened a second time instead of delivering the tag directly. This is not our intended behavior. You can bypass the problem by using a Foreground Dispatch.&lt;/p&gt;&lt;p&gt;Instead of the system having distributed the Intent, you can register your Activity to receive the tag directly. This is important for a particular workflow, where it makes no sense to open another app.&lt;/p&gt;&lt;p&gt;I&amp;#8217;ve inserted the explanations at the appropriate places in the code.&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
	package net.vrallev.android.nfc.demo;
	import android.app.Activity;
	import android.app.PendingIntent;
	import android.content.Intent;
	import android.content.IntentFilter;
	import android.content.IntentFilter.MalformedMimeTypeException;
	import android.nfc.NfcAdapter;
	import android.os.Bundle;
	import android.widget.TextView;
	import android.widget.Toast;
	/**
	 * Activity for reading data from an NDEF Tag.
	 *
	 * @author Ralf Wondratschek
	 *
	 */
	public class MainActivity extends Activity {
		public static final String MIME_TEXT_PLAIN = &amp;quot;text/plain&amp;quot;;
		public static final String TAG = &amp;quot;NfcDemo&amp;quot;;
		private TextView mTextView;
		private NfcAdapter mNfcAdapter;
		@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
			setContentView(R.layout.activity_main);
			mTextView = (TextView) findViewById(R.id.textView_explanation);
			mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
			if (mNfcAdapter == null) {
				// Stop here, we definitely need NFC
				Toast.makeText(this, &amp;quot;This device doesn't support NFC.&amp;quot;, Toast.LENGTH_LONG).show();
				finish();
				return;
			}
			if (!mNfcAdapter.isEnabled()) {
				mTextView.setText(&amp;quot;NFC is disabled.&amp;quot;);
			} else {
				mTextView.setText(R.string.explanation);
			}
			handleIntent(getIntent());
		}
		@Override
		protected void onResume() {
			super.onResume();
			/**
			 * It's important, that the activity is in the foreground (resumed). Otherwise
			 * an IllegalStateException is thrown.
			 */
			setupForegroundDispatch(this, mNfcAdapter);
		}
		@Override
		protected void onPause() {
			/**
			 * Call this before onPause, otherwise an IllegalArgumentException is thrown as well.
			 */
			stopForegroundDispatch(this, mNfcAdapter);
			super.onPause();
		}
		@Override
		protected void onNewIntent(Intent intent) {
			/**
			 * This method gets called, when a new Intent gets associated with the current activity instance.
			 * Instead of creating a new activity, onNewIntent will be called. For more information have a look
			 * at the documentation.
			 *
			 * In our case this method gets called, when the user attaches a Tag to the device.
			 */
			handleIntent(intent);
		}
		private void handleIntent(Intent intent) {
			// TODO: handle Intent
		}
		/**
		 * @param activity The corresponding {@link Activity} requesting the foreground dispatch.
		 * @param adapter The {@link NfcAdapter} used for the foreground dispatch.
		 */
		public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
			final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
			intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
			final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);
			IntentFilter[] filters = new IntentFilter[1];
			String[][] techList = new String[][]{};
			// Notice that this is the same filter as in our manifest.
			filters[0] = new IntentFilter();
			filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
			filters[0].addCategory(Intent.CATEGORY_DEFAULT);
			try {
				filters[0].addDataType(MIME_TEXT_PLAIN);
			} catch (MalformedMimeTypeException e) {
				throw new RuntimeException(&amp;quot;Check your mime type.&amp;quot;);
			}
			adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
		}
		/**
		 * @param activity The corresponding {@link BaseActivity} requesting to stop the foreground dispatch.
		 * @param adapter The {@link NfcAdapter} used for the foreground dispatch.
		 */
		public static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter) {
			adapter.disableForegroundDispatch(activity);
		}
	}
	&lt;/pre&gt;&lt;p&gt;Now, when you attach a tag and our app is already opened, onNewIntent is called and no new Activity is created.&lt;/p&gt;&lt;hr/&gt;&lt;h2&gt;Reading Data From an NDEF Tag&lt;/h2&gt;&lt;p&gt;The last step is to read the data from the tag. The explanations are inserted at the appropriate places in the code once again. The NdefReaderTask is a private inner class.&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
	package net.vrallev.android.nfc.demo;
	import java.io.UnsupportedEncodingException;
	import java.util.Arrays;
	import android.app.Activity;
	import android.app.PendingIntent;
	import android.content.Intent;
	import android.content.IntentFilter;
	import android.content.IntentFilter.MalformedMimeTypeException;
	import android.nfc.NdefMessage;
	import android.nfc.NdefRecord;
	import android.nfc.NfcAdapter;
	import android.nfc.Tag;
	import android.nfc.tech.Ndef;
	import android.os.AsyncTask;
	import android.os.Bundle;
	import android.util.Log;
	import android.widget.TextView;
	import android.widget.Toast;
	/*
	 * ... other code parts
	 */
	private void handleIntent(Intent intent) {
		String action = intent.getAction();
		if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
			String type = intent.getType();
			if (MIME_TEXT_PLAIN.equals(type)) {
				Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
				new NdefReaderTask().execute(tag);
			} else {
				Log.d(TAG, &amp;quot;Wrong mime type: &amp;quot; + type);
			}
		} else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
			// In case we would still use the Tech Discovered Intent
			Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
			String[] techList = tag.getTechList();
			String searchedTech = Ndef.class.getName();
			for (String tech : techList) {
				if (searchedTech.equals(tech)) {
					new NdefReaderTask().execute(tag);
					break;
				}
			}
		}
	}
	&lt;/pre&gt;&lt;pre class="brush: jscript; title: ;"&gt;
	/**
	 * Background task for reading the data. Do not block the UI thread while reading.
	 *
	 * @author Ralf Wondratschek
	 *
	 */
	private class NdefReaderTask extends AsyncTask&amp;lt;Tag, Void, String&amp;gt; {
		@Override
		protected String doInBackground(Tag... params) {
			Tag tag = params[0];
			Ndef ndef = Ndef.get(tag);
			if (ndef == null) {
				// NDEF is not supported by this Tag.
				return null;
			}
			NdefMessage ndefMessage = ndef.getCachedNdefMessage();
			NdefRecord[] records = ndefMessage.getRecords();
			for (NdefRecord ndefRecord : records) {
				if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN &amp;amp;&amp;amp; Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
					try {
						return readText(ndefRecord);
					} catch (UnsupportedEncodingException e) {
						Log.e(TAG, &amp;quot;Unsupported Encoding&amp;quot;, e);
					}
				}
			}
			return null;
		}
		private String readText(NdefRecord record) throws UnsupportedEncodingException {
			/*
			 * See NFC forum specification for &amp;quot;Text Record Type Definition&amp;quot; at 3.2.1
			 *
			 * http://www.nfc-forum.org/specs/
			 *
			 * bit_7 defines encoding
			 * bit_6 reserved for future use, must be 0
			 * bit_5..0 length of IANA language code
			 */
			byte[] payload = record.getPayload();
			// Get the Text Encoding
			String textEncoding = ((payload[0] &amp;amp; 128) == 0) ? &amp;quot;UTF-8&amp;quot; : &amp;quot;UTF-16&amp;quot;;
			// Get the Language Code
			int languageCodeLength = payload[0] &amp;amp; 0063;
			// String languageCode = new String(payload, 1, languageCodeLength, &amp;quot;US-ASCII&amp;quot;);
			// e.g. &amp;quot;en&amp;quot;
			// Get the Text
			return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
		}
		@Override
		protected void onPostExecute(String result) {
			if (result != null) {
				mTextView.setText(&amp;quot;Read content: &amp;quot; + result);
			}
		}
	}
	&lt;/pre&gt;&lt;p&gt;The app now successfully reads the content.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/Reading-NFC-Tags-with-Android_5.png" alt="screen" /&gt;&lt;br
/&gt; &lt;/figure&gt;&lt;hr/&gt;&lt;h2&gt;Useful Apps&lt;/h2&gt;&lt;p&gt;To check whether data is read and written properly, I personally like to use following apps:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a
href="https://play.google.com/store/apps/details?id=at.mroland.android.apps.nfctaginfo"&gt;NFC TagInfo&lt;/a&gt; by NFC Research Lab for reading data&lt;/li&gt;&lt;li&gt;&lt;a
href="https://play.google.com/store/apps/details?id=com.nxp.taginfolite"&gt;TagInfo&lt;/a&gt; by NXP SEMICONDUCTORS for reading data&lt;/li&gt;&lt;li&gt;&lt;a
href="https://play.google.com/store/apps/details?id=com.nxp.nfc.tagwriter"&gt;TagWriter&lt;/a&gt; by NXP SEMICONDUCTORS for writing data&lt;/li&gt;&lt;/ul&gt;&lt;hr/&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;In this tutorial I have shown you how the data from a NDEF tag can be extracted. You could expand the example to other mime types and chip technologies; a feature to write data would be useful as well. The first step to work with NFC was made. However, the Android SDK offers much more possibilities, such as an easy exchange of data (called Android Beam).&lt;/p&gt;&lt;hr/&gt;&lt;h2&gt;About the Author&lt;/h2&gt;&lt;p&gt;Ralf Wondratschek is a computer science student from Germany. In addition to his studies, Ralf works as a freelancer in the field of mobile computing. In the last few years he has worked with Java, XML, HTML, JSP, JSF, Eclipse, Google App Engine, and of course Android. He has published two Android apps to date &lt;a
href="https://market.android.com/search?q=de.vrallev&amp;#038;so=1&amp;#038;c=apps"&gt;which can be found here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;You can find out more about the author’s work on his homepage &lt;a
href="http://www.vrallev.net"&gt;vrallev.net&lt;/a&gt;.&lt;/p&gt;&lt;hr/&gt;&lt;h2&gt;Sources&lt;/h2&gt;&lt;p&gt;&lt;a
href="http://www.nfc-forum.org/home/n-mark.jpg"&gt;http://www.nfc-forum.org/home/n-mark.jpg&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a
href="http://commons.wikimedia.org/wiki/File%3A%C3%9Cberlagert.jpg"&gt;http://commons.wikimedia.org/wiki/File%3A%C3%9Cberlagert.jpg&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a
href="http://developer.android.com/images/nfc_tag_dispatch.png"&gt;http://developer.android.com/images/nfc_tag_dispatch.png&lt;/a&gt;&lt;/p&gt;&lt;hr/&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/fAK8stCZ8eI" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/tutorials/android/reading-nfc-tags-with-android/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/tutorials/android/reading-nfc-tags-with-android/</feedburner:origLink></item> <item><title>Connecting to an External Database With NuSOAP</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/123d4EOG3g8/</link> <comments>http://mobile.tutsplus.com/tutorials/windows/connecting-to-an-external-database-with-nusoap/#comments</comments> <pubDate>Wed, 15 May 2013 17:57:52 +0000</pubDate> <dc:creator>Tomasz Wójcik</dc:creator> <category><![CDATA[Windows Phone]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=15832</guid> <description>&lt;p&gt;Many mobile applications use external databases that are located remotely on the Internet. In the following tutorial, we will create a simple database, connect to it, and receive or send data from this source. To do this we will use a Windows Phone 7 Silverlight based application and a NuSOAP web service. Let&amp;#8217;s begin!&lt;br
/&gt;&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;1. &lt;/span&gt;Install the Software&lt;/h2&gt;&lt;p&gt;Before we start we need to install the following programs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a
href="http://bangowinphone75sdk.codeplex.com/"&gt;Windows Phone 7.5 SDK&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a
href="http://silverlight.codeplex.com/releases/view/78435"&gt;Silverlight 5 Toolkit&lt;/a&gt;&lt;/li&gt;&lt;li&gt;PHP Library to run the web service (&lt;a
href="http://sourceforge.net/projects/nusoap/"&gt;NuSOAP&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Obviously, we will also need some sort of web hosting where we can create a database. Most of the free hosting options will be enough for us, but they may have some limitations which I will discuss later on in this tutorial.&lt;/p&gt;&lt;p&gt;We also need an FTP client. In this tutorial I will use a &lt;a
href="https://addons.mozilla.org/en-US/firefox/addon/fireftp/"&gt;FireFTP&lt;/a&gt; add-on to Mozilla Firefox. But you can use anything you want.&lt;/p&gt;&lt;p&gt;When all the programs are installed, we can continue.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;2. &lt;/span&gt;Creating an External Database&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;To begin, check that the hosting uses phpMyAdmin because I use it in the examples. But if you choose to use something else all the commands should be similar.&lt;/p&gt;&lt;p&gt;First we need to create a simple database, in our case it will contain only one table and three attributes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;ID&lt;/b&gt; &amp;#8211; this value identifies the record. It must be set as an &lt;b&gt;autoincrement&lt;/b&gt; field.&lt;/li&gt;&lt;li&gt;&lt;b&gt;FirstName&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;LastName&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The table name is &lt;b&gt;MyUsers&lt;/b&gt;.&lt;/p&gt;&lt;p&gt;To do this just click &lt;b&gt;&amp;#8220;create table&amp;#8221;&lt;/b&gt;:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_1.png" /&gt;&lt;/figure&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;After that, fill in the cells as shown in this screenshot:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_2.png" /&gt;&lt;/figure&gt;&lt;p&gt;The table structure should now look like this:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_3.png" /&gt;&lt;/figure&gt;&lt;h3&gt;Step 3&lt;/h3&gt;&lt;p&gt;At this step we must note a few important points:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Host address&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;As I wrote earlier, free hostings have limits, one of these is to possibly connect only from &lt;b&gt;localhost&lt;/b&gt;, remember that!&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt; Database user&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Our username to log into the database:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Database password&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Database name&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;3. &lt;/span&gt;NuSOAP Server &amp;#8211; Starting Web Service&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;Starting our web service is very simple:&lt;/p&gt;&lt;p&gt;First, we must copy some files to the ftp server. I recommend a ftp server because it is directly connected to our hosting because of the &lt;b&gt;localhost&lt;/b&gt; issue.&lt;/p&gt;&lt;p&gt;Once we&amp;#8217;re connected, we need to copy the nusoap.php file which was downloaded earlier. We also require a file that will contain specific functions written by us that is necessary for our application; I called it &lt;b&gt;MyService.php&lt;/b&gt;.&lt;/p&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;After we copy the files, our &lt;b&gt;FTP&lt;/b&gt; root directory should look like the image below:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_4.png" /&gt;&lt;/figure&gt;&lt;p&gt;Now open the &lt;b&gt;MyService.php&lt;/b&gt; file and write into it:&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
&amp;lt;?php
// Pull in the NuSOAP
code require_once('nusoap.php');
// Create the server instance
$server = new soap_server();
// Initialize WSDL support
(MyService is name of our service)
$server-----&amp;gt;configureWSDL('MyService', 'urn:MyService');
        // Character encoding
        $server-&amp;gt;soap_defencoding = 'utf-8';
        //-------------------------------------------------
        //Registrations of our functions
        //-------------------------------------------------
        //Our web service functions will be here.
        //-------------------------------------------------
        $HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
        $server-&amp;gt;service($HTTP_RAW_POST_DATA);
?&amp;gt;
    &lt;/pre&gt;&lt;p&gt;The most important points are explained in the upper comments to code.&lt;/p&gt;&lt;h3&gt;Step 3&lt;/h3&gt;&lt;p&gt;From now on our service should work. We can check this by typing in the web browser:&lt;/p&gt;&lt;p&gt;&lt;b&gt;http://www.ourdomain.com/MyService.php&lt;/b&gt;&lt;/p&gt;&lt;p&gt;If all went well you should see something like this:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_5.png" /&gt;&lt;/figure&gt;&lt;p&gt;After we successfully started the web service, we can go to the next step.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;4. &lt;/span&gt;Writing Web Service Functions&lt;/h2&gt;&lt;p&gt;In our service we need two functions:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The first function adds data to the online database.&lt;/li&gt;&lt;li&gt;The second function receives data from it.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;Let&amp;#8217;s open &lt;b&gt;MyService.php&lt;/b&gt;. We need to register the new function, to do this we should type:&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
    $server-&amp;gt;register(
        'InsertData',   //Name of function
      	array('FirstName' =&amp;gt; 'xsd:string', 'LastName' =&amp;gt; 'xsd:string'), //Input Values
      	array('return' =&amp;gt;'xsd:boolean'), //Output Values
    	  'urn:MyServicewsdl',  //Namespace
        'urn:MyServicewsdl#InsertData',  //SoapAction
        'rpc',       //style
        'literal',   //can be encoded but it doesn't work with silverlight
        'Some_comments_about_function'
    );
    &lt;/pre&gt;&lt;p&gt;Remember that it needs to be placed before the body function and after the server directives.&lt;/p&gt;&lt;p&gt;Here is some code explanation:&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
      'FirstName' =&amp;gt; 'xsd:string'
    &lt;/pre&gt;&lt;p&gt;&lt;b&gt;&amp;#8220;FirstName&amp;#8221;&lt;/b&gt; is the name of variable, &lt;b&gt;&amp;#8220;string&amp;#8221;&lt;/b&gt; is the type of variable (i.e. it can be &lt;b&gt;int, longint, boolean&lt;/b&gt;, etc.).&lt;/p&gt;&lt;p&gt;When the function registers, we need to write the body of it. Below is the code and explanation:&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
    function InsertData($FirstName, $LastName) {
    	$connect = mysql_pconnect(&amp;quot;Host&amp;quot;,&amp;quot;UserName&amp;quot;,&amp;quot;UserPassword&amp;quot;));
    	if ($connect) {
    		if(mysql_select_db(&amp;quot;DatabaseName&amp;quot;, $connect)) {
          mysql_query(&amp;quot;INSERT INTO MyUser SET FirstName='$FirstName', LastName='$LastName'&amp;quot;);
    			return true;
    		}
    	}
    	return false;
    }
    &lt;/pre&gt;&lt;pre class="brush: php; title: ;"&gt;
          InsertData($FirstName, $LastName)
        &lt;/pre&gt;&lt;p&gt;Here&amp;#8217;s the name of the function and its attributes. They must be the same as in the registration section.&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
          $connect = mysql_pconnect(&amp;quot;Host&amp;quot;,&amp;quot;UserName&amp;quot;,&amp;quot;UserPassword&amp;quot;);
        &lt;/pre&gt;&lt;p&gt;Here we can insert the data we noticed when we created the database.&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
          if(mysql_select_db(&amp;quot;DatabaseName&amp;quot;, $connect)) {
        &lt;/pre&gt;&lt;p&gt;And also here.&lt;/p&gt;&lt;p&gt;After that it is a simple MySQL query that adds data to our database:&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
          mysql_query(&amp;quot;INSERT INTO MyUser SET FistName='$FirstName', LastName='$LastName'&amp;quot;);
        &lt;/pre&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;Now it&amp;#8217;s time to write the second function. The structure will be similar to the first.&lt;/p&gt;&lt;p&gt;Here is the code of method registration:&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
      $server-&amp;gt;register(
          'GetData',
        	array('ID' =&amp;gt; 'xsd:int'),
        	array('return' =&amp;gt;'xsd:string'),
      	  'urn:MyServicewsdl',
          'urn:MyServicewsdl#GetData',
          'rpc',
          'literal',
          'Some comments about function 2'
      );
       &lt;/pre&gt;&lt;p&gt;The main differences are in the Input/Output values section (changed types of variables).&lt;/p&gt;&lt;p&gt;Here&amp;#8217;s the body function code:&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
       function GetData($ID) {
      	$connect = mysql_pconnect(&amp;quot;Host&amp;quot;,&amp;quot;UserName&amp;quot;,&amp;quot;UserPassword&amp;quot;);
      	if ($connect) {
      		if(mysql_select_db(&amp;quot;DatabaseName&amp;quot;, $connect)) {
            $sql = &amp;quot;SELECT FirstName, LastName FROM MyUser WHERE ID = '$ID'&amp;quot;;
      			$result = mysql_fetch_array(mysql_query($sql));
      			return $result['FirstName'].&amp;quot;-&amp;quot;.$result['LastName'];
      		}
      	}
      	return false;
      }
      &lt;/pre&gt;&lt;p&gt;Here is a little code explanation:&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
         return $result['FirstName'].&amp;quot;-&amp;quot;.$result['LastName'];
&lt;/pre&gt;&lt;p&gt;This line states what must return to the Windows Phone application.&lt;/p&gt;&lt;h3&gt;Step 3&lt;/h3&gt;&lt;p&gt;After writing all the functions the MyService.php file should look like this:&lt;/p&gt;&lt;pre class="brush: php; title: ;"&gt;
&amp;lt;?php
// Pull in the NuSOAP code
require_once('nusoap.php');
// Create the server instance
$server = new soap_server();
// Initialize WSDL support
configureWSDL('MyService', 'urn:MyService');
                // Character encoding
                $server-&amp;gt;soap_defencoding = 'utf-8';
                //-------------------------------------------------
                //Register InsertData function
                $server-&amp;gt;register(
                        'InsertData',
                      	array('FirstName' =&amp;gt; 'xsd:string', 'LastName' =&amp;gt; 'xsd:string'),
                      	array('return' =&amp;gt;'xsd:boolean'),
                    	  'urn:MyServicewsdl',
                        'urn:MyServicewsdl#InsertData',
                        'rpc',
                        'literal',
                        'Some comments about function'
                    );
                //Register GetData function
                $server-&amp;gt;register(
                        'GetData',
                      	array('ID' =&amp;gt; 'xsd:int'),
                      	array('return' =&amp;gt;'xsd:string'),
                    	  'urn:MyServicewsdl',
                        'urn:MyServicewsdl#GetData',
                        'rpc',
                        'literal',
                        'Some comments about function 2'
                    );
                //-------------------------------------------------
                //Body InsterData function
                function InsertData($FirstName, $LastName) {
                	$connect = mysql_pconnect(&amp;quot;Host&amp;quot;,&amp;quot;UserName&amp;quot;,&amp;quot;UserPassword&amp;quot;);
                	if ($connect) {
                		if(mysql_select_db(&amp;quot;DatabaseName&amp;quot;, $connect)) {
                      mysql_query(&amp;quot;INSERT INTO MyUser SET FirstName='$FirstName', LastName='$LastName'&amp;quot;);
                			return true;
                		}
                	}
                	return false;
                }
                //Body GetData function
                function GetData($ID) {
                	$connect = mysql_pconnect(&amp;quot;Host&amp;quot;,&amp;quot;UserName&amp;quot;,&amp;quot;UserPassword&amp;quot;);
                	if ($connect) {
                		if(mysql_select_db(&amp;quot;DatabaseName&amp;quot;, $connect)) {
                      $sql = &amp;quot;SELECT FirstName, LastName FROM MyUser WHERE ID = '$ID'&amp;quot;;
                			$result = mysql_fetch_array(mysql_query($sql));
                			return $result['FirstName'].&amp;quot;-&amp;quot;.$result['LastName'];
                		}
                	}
                	return false;
                }
                //-------------------------------------------------
                $HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
                $server-&amp;gt;service($HTTP_RAW_POST_DATA);
        ?&amp;gt;
      &lt;/pre&gt;&lt;p&gt;To validate the functions we can again type &lt;b&gt;http://www.ourdomain.com/MyService.php&lt;/b&gt; in the browser. Now the site should look a little bit different, but similar to this:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_6.png" width="359" height="250" /&gt;&lt;/figure&gt;&lt;p&gt;Now we&amp;#8217;re ready to go to next step.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;5. &lt;/span&gt;Creating a Simple Layout for a Windows Phone Application&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;Firstly, we must create a Windows Phone app. Let&amp;#8217;s run Microsoft Visual Studio for Windows Phone 2010. After Visual Studio starts, click &amp;#8220;File&amp;#8221; then &amp;#8220;New Project&amp;#8221;. You should see a dialog window like in the screenshot below:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_7.png" /&gt;&lt;/figure&gt;&lt;p&gt;Our app will use Silverlight, so we must check this template. We can also change the project name, like localisations, etc. In my case, the project name is &amp;#8220;MyApplication&amp;#8221;. After you have done this, click the OK button.&lt;/p&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;At this point we must note what we need in our app. We need:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Two text boxes for sending data to the database (First Name, Last Name)&lt;/li&gt;&lt;li&gt;One text box to receive the data (ID)&lt;/li&gt;&lt;li&gt;Two buttons to approve our actions&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Adding objects (such as buttons) to our application is easy, just drag it from &amp;#8220;ToolBox&amp;#8221; and drop it onto preview of the app. Keep in mind that you have a free hand in setting your own layout.&lt;/p&gt;&lt;p&gt;This is how it looks on my app:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_8.png" /&gt;&lt;/figure&gt;&lt;p&gt;Another important aspect is the names of the elements used in Visual Studio (they&amp;#8217;re used later in code).&lt;/p&gt;&lt;p&gt;To change it, just click on element. Then in properties you can see text like &amp;#8220;Textbox1&amp;#8243;, click on it, and change it to something that you can remember, that is crucial. I used these names for my elements:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&amp;#8220;FirstNameBox&amp;#8221;, &amp;#8220;LastNameBox&amp;#8221;, and &amp;#8220;IdBox&amp;#8221; for text boxes&lt;/li&gt;&lt;li&gt;&amp;#8220;SendBTN&amp;#8221; and &amp;#8220;ReadBTN&amp;#8221; for buttons&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;That&amp;#8217;s all we need to do in this step, we can move on.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;6. &lt;/span&gt;Connecting to Service&lt;/h2&gt;&lt;p&gt;To connect to the web service we must right click on &amp;#8220;Reference&amp;#8221; in &amp;#8220;Solution Explorer&amp;#8221; dialog and select &amp;#8220;Add Service Reference&amp;#8230;&amp;#8221;&lt;/p&gt;&lt;p&gt;Here how this looks:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_9.png" /&gt;&lt;/figure&gt;&lt;p&gt;After that a new window will appear. In it we must write the address of our web service and the name of namespace.&lt;/p&gt;&lt;p&gt;In our case an address will be created, like on this scheme: http://www.ourdomain.com/MyService.php?wsdl.&lt;/p&gt;&lt;p&gt;After entering an address and clicking &amp;#8220;Go&amp;#8221; you should see something like this:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_10.png" /&gt;&lt;/figure&gt;&lt;p&gt;If you see the operations called &amp;#8220;GetData&amp;#8221; and &amp;#8220;InsertData&amp;#8221; that means the connection was successfully created! Remember to type namespace, in my case it is &amp;#8220;MyService&amp;#8221;. Now click OK.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;7. &lt;/span&gt;Writing Windows Phone Functions&lt;/h2&gt;&lt;p&gt;We&amp;#8217;re almost at the end of this tutorial; we only need to write two simple functions.&lt;/p&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;The first function will be behind the &amp;#8220;Send&amp;#8221; button, so double click it. Now we must add at the top of the file a &amp;#8220;using&amp;#8221; directive of our service:&lt;/p&gt;&lt;pre class="brush: csharp; title: ;"&gt;
      using MyApplication.MyService;
    &lt;/pre&gt;&lt;p&gt;Let&amp;#8217;s look at the send function behind the &amp;#8220;Send&amp;#8221; button, now it is empty:&lt;/p&gt;&lt;pre class="brush: csharp; title: ;"&gt;
        private void SendBTN_Click(object sender, RoutedEventArgs e)
        {
        }
    &lt;/pre&gt;&lt;p&gt;Our complete function should look like this:&lt;/p&gt;&lt;pre class="brush: csharp; title: ;"&gt;
        private void SendBTN_Click(object sender, RoutedEventArgs e)
        {
            //Creating new proxy object of our service
            MyServicePortTypeClient send = new MyServicePortTypeClient();
            send.InsertDataCompleted += new EventHandler&amp;lt;InsertDataCompletedEventArgs&amp;gt;(send_InsertDataCompleted);
            //Calling method, as a parameters we type text contained in FirstNameBox and LastNameBox
            //This data will be sent to web service and later to database
            send.InsertDataAsync(FirstNameBox.Text, LastNameBox.Text);
        }
        void send_InsertDataCompleted(object sender, InsertDataCompletedEventArgs e)
        {
            //If our server return true, that means we're added data to database...
            if (e.Result)
                MessageBox.Show(&amp;quot;Successfully added!&amp;quot;);
            //...if return false we aren't.
            else
                MessageBox.Show(&amp;quot;Some problems occured!&amp;quot;);
        }
    &lt;/pre&gt;&lt;p&gt;The most important points are explained in the code comments, but we must be aware of the following:&lt;/p&gt;&lt;p&gt;Method &amp;#8220;send_InsertDataCompleted&amp;#8221; executes when we get an answer from the server. Also this function is not in the &amp;#8220;SendBTN&amp;#8221; object, it is outside.&lt;/p&gt;&lt;p&gt;Now it&amp;#8217;s time to test our work! Start to debug and fill out the boxes with some data. Here I have entered John as a first name and Doe as a last name, then I clicked Send:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_11.png" /&gt;&lt;/figure&gt;&lt;p&gt;Let&amp;#8217;s see how the database looks now:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_12.png" /&gt;&lt;/figure&gt;&lt;p&gt;Yes, the new record with ID = 1 has appeared and everything is going well.&lt;/p&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;Now we&amp;#8217;ve reached the final function for receiving. It is similar to the previous method. Double click on the &amp;#8220;Read&amp;#8221; button and copy the code:&lt;/p&gt;&lt;pre class="brush: csharp; title: ;"&gt;
      private void ReadBTN_Click(object sender, RoutedEventArgs e)
        {
            //Creating new proxy object of our service
            MyServicePortTypeClient read = new MyServicePortTypeClient();
            read.GetDataCompleted += new EventHandler&amp;lt;GetDataCompletedEventArgs&amp;gt;(read_GetDataCompleted);
            //Calling method, as a parameters we type text contained in IdTextBox
            //but we must change text type of string to integer (ID in web service have integer type)
            read.GetDataAsync(Convert.ToInt32(IdBox.Text));
        }
        void read_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
        {
            MessageBox.Show(e.Result);
        }
    &lt;/pre&gt;&lt;p&gt;This is the same process as before, &amp;#8220;read_GetDataCompleted&amp;#8221; executes after getting data from the database. In this method we&amp;#8217;ll use a message box to show our result, i.e. first and last name. There is one more step to do; we need to change the type of text in IdBox from string to integer because the variable called ID in the web service has an integer type. To do this I used a function called &lt;b&gt;&amp;#8220;Convert.ToIn32()&amp;#8221;&lt;/b&gt;&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;8. &lt;/span&gt;Testing&lt;/h2&gt;&lt;p&gt;Now we can see if this works. Enter ID into &amp;#8220;IdTextBox&amp;#8221;. I entered &amp;#8220;1&amp;#8243;, then clicked the &amp;#8220;Read&amp;#8221; button.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/andrea-morris/WP7WebService_13.png" /&gt;&lt;/figure&gt;&lt;p&gt;Everything is working! Our application is now complete!&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;In this tutorial we created a database using a Windows Phone 7 Silverlight based application and NuSOAP web service. This database is helpful to receive or send data. External databases are important because they are used by many mobile applications.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/123d4EOG3g8" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/tutorials/windows/connecting-to-an-external-database-with-nusoap/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/tutorials/windows/connecting-to-an-external-database-with-nusoap/</feedburner:origLink></item> <item><title>Create a Weather App with Forecast – Project Setup</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/WlKCi2IWoag/</link> <comments>http://mobile.tutsplus.com/tutorials/iphone/create-a-weather-app-with-forecast-project-setup/#comments</comments> <pubDate>Mon, 13 May 2013 19:52:08 +0000</pubDate> <dc:creator>Bart Jacobs</dc:creator> <category><![CDATA[iOS SDK]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=17014</guid> <description>&lt;p&gt;In March of this year, the company behind &lt;a
href="http://darkskyapp.com"&gt;Dark Sky&lt;/a&gt; and &lt;a
href="http://forecast.io"&gt;Forecast.io&lt;/a&gt; introduced Forecast. Forecast is a simple weather API that provides both short- and longterm weather data. In this series, I will show you how to create a beautiful weather application for iOS powered by Forecast.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;Introduction&lt;/h2&gt;&lt;p&gt;&lt;a
href="http://blog.forecast.io/announcing-forecast/"&gt;Earlier this year&lt;/a&gt;, the Dark Sky Company introduced &lt;a
href="https://developer.forecast.io"&gt;Forecast&lt;/a&gt;, a simple yet powerful weather API that provides short and longterm weather predictions. In this series we will create an iOS application that is powered by the Forecast API. Forecast is free for up to a thousand API calls per day, so feel free to &lt;a
href="https://developer.forecast.io/register"&gt;sign up&lt;/a&gt; for a developer account and follow along with me.&lt;/p&gt;&lt;p&gt;Even though a number of open source wrappers for the Forecast API are available, in this series we will use the AFNetworking library to query the Forecast API. In the first part of this series, we will create the project&amp;#8217;s foundation and implement a basic user interface. Even though our application is simple in scope, it will support multiple locations and that is what we will focus on in this article.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;1.&lt;/span&gt; Project Setup&lt;/h2&gt;&lt;h3&gt;Step 1: Creating the Project&lt;/h3&gt;&lt;p&gt;Fire up Xcode and create a new project based on the &lt;strong&gt;Empty Application&lt;/strong&gt; template (figure 1). Name the application &lt;strong&gt;Rain&lt;/strong&gt; and enable &lt;strong&gt;Automatic Reference Counting&lt;/strong&gt; (figure 2).&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130510-1.png" alt="Creating a Weather App for iOS with Forecast.io: Part 1 - Setting Up the Project " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 1: Setting Up the Project&lt;/figcaption&gt; &lt;/figure&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130510-2.png" alt="Creating a Weather App for iOS with Forecast.io: Part 1 - Configuring the Project " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 2: Configuring the Project&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 2: Adding Libraries&lt;/h3&gt;&lt;p&gt;For this project, we will be using three open source libraries, &lt;a
href="https://github.com/samvermette/SVProgressHUD"&gt;SVProgressHUD&lt;/a&gt; created by &lt;a
href="https://github.com/samvermette"&gt;Sam Vermette&lt;/a&gt;, &lt;a
href="https://github.com/AFNetworking/AFNetworking"&gt;AFNetworking&lt;/a&gt; created by &lt;a
href="https://github.com/mattt"&gt;Mattt Thompson&lt;/a&gt;, and &lt;a
href="https://github.com/Inferis/ViewDeck"&gt;ViewDeck&lt;/a&gt; created by &lt;a
href="https://github.com/Inferis"&gt;Tom Adriaenssen&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Since I am an avid fan of &lt;a
href="http://cocoapods.org"&gt;CocoaPods&lt;/a&gt;, I will be using it to install and manage the libraries of our project. If you are not familiar with CocoaPods, then I recommend visiting the website of &lt;a
href="http://cocoapods.org"&gt;CocoaPods&lt;/a&gt; or reading my &lt;a
href="http://mobile.tutsplus.com/tutorials/iphone/streamlining-cocoa-development-with-cocoapods/"&gt;introduction to CocoaPods&lt;/a&gt;. You can also manually add each library to your project if you prefer to not use CocoaPods.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Close your project&lt;/strong&gt;, browse to the project&amp;#8217;s root, and create a file named &lt;strong&gt;Podfile&lt;/strong&gt;. Open &lt;strong&gt;Podfile&lt;/strong&gt; in your favorite text editor and replace its contents with the snippet below. In the project&amp;#8217;s pod file, we specify the platform, deployment target, and the pods we want to include in the project.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
platform :ios, '6.0'
pod 'ViewDeck', '~&amp;gt; 2.2.11'
pod 'AFNetworking', '~&amp;gt; 1.2.1'
pod 'SVProgressHUD', '~&amp;gt; 0.9.0'
&lt;/pre&gt;&lt;p&gt;Open the &lt;strong&gt;Terminal&lt;/strong&gt; application, browse to the project&amp;#8217;s root, and install the libraries by executing &lt;code&gt;pod install&lt;/code&gt;. In addition to installing the three pods that we specified in the project&amp;#8217;s pod file, CocoaPods has already created an Xcode workspace for us. Open the new workspace by executing the &lt;code&gt;open Rain.xcworkspace&lt;/code&gt; command from the command line.&lt;/p&gt;&lt;h3&gt;Step 3: Adding Dependencies&lt;/h3&gt;&lt;p&gt;Before we can continue, we need to link our project against a handful of frameworks. The AFNetworking library depends on the &lt;strong&gt;Mobile Core Services&lt;/strong&gt; and &lt;strong&gt;System Configuration&lt;/strong&gt; frameworks and the ViewDeck library makes use of the &lt;strong&gt;Quartz Core&lt;/strong&gt; framework. Select the project from the &lt;strong&gt;Project Navigator&lt;/strong&gt; on the left, select the &lt;strong&gt;Rain&lt;/strong&gt; target in the list of targets, open the &lt;strong&gt;Build Phases&lt;/strong&gt; tab at the top, and expand the &lt;strong&gt;Link Binary With Libraries&lt;/strong&gt; drawer. Click the plus button to link your project against the aforementioned frameworks. We will be using the &lt;strong&gt;Core Location&lt;/strong&gt; framework a bit later in this article so now is good time to link your project against that framework as well (figure 3).&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130510-3.png" alt="Creating a Weather App for iOS with Forecast.io: Part 1 - Linking the Project Against a Handful of Frameworks" /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 3: Linking the Project Against a Handful of Frameworks&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 4: Edit the Precompiled Header File&lt;/h3&gt;&lt;p&gt;Before we start implementing the basic structure of our weather application, it is a good idea to edit the precompiled header file of our project. Add an import statement for each of the frameworks we added to our project a moment ago and do the same for the AFNetworking, SVProgressHUD, and ViewDeck libraries.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;lt;Availability.h&amp;gt;
#ifndef __IPHONE_3_0
#warning &amp;quot;This project uses features only available in iOS SDK 3.0 and later.&amp;quot;
#endif
#ifdef __OBJC__
    #import &amp;lt;UIKit/UIKit.h&amp;gt;
    #import &amp;lt;Foundation/Foundation.h&amp;gt;
    #import &amp;lt;QuartzCore/QuartzCore.h&amp;gt;
    #import &amp;lt;CoreLocation/CoreLocation.h&amp;gt;
    #import &amp;lt;MobileCoreServices/MobileCoreServices.h&amp;gt;
    #import &amp;lt;SystemConfiguration/SystemConfiguration.h&amp;gt;
    #import &amp;quot;AFNetworking.h&amp;quot;
    #import &amp;quot;SVProgressHUD.h&amp;quot;
    #import &amp;quot;IIViewDeckController.h&amp;quot;
#endif
&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;2.&lt;/span&gt; Laying the Foundation&lt;/h2&gt;&lt;p&gt;The concept and structure of the application is simple. The application manages three views, (1) a view in the center showing the current weather for a particular location, a view on the right showing the weather for the next few days, and a view on the left with a list of locations. The basic structure will make more sense once we&amp;#8217;ve implemented it. To create this structure, we make use of the terrific &lt;a
href="https://github.com/Inferis/ViewDeck"&gt;ViewDeck library&lt;/a&gt;, created and maintained by &lt;a
href="https://github.com/Inferis"&gt;Tom Adriaenssen&lt;/a&gt;. The ViewDeck library is one of the most powerful implementations of the sliding view design pattern originally introduced in Facebook for iOS.&lt;/p&gt;&lt;h3&gt;Step 1: View Controllers&lt;/h3&gt;&lt;p&gt;Before we put the ViewDeck library to use, we need to create the view controller classes that will manage the three views I mentioned in the previous paragraph. Create three &lt;code&gt;UIViewController&lt;/code&gt; subclasses named &lt;code&gt;MTWeatherViewController&lt;/code&gt;, &lt;code&gt;MTForecastViewController&lt;/code&gt;, and &lt;code&gt;MTLocationsViewController&lt;/code&gt;, respectively (figure 4). Don&amp;#8217;t forget to create a user interface or XIB file for each class (figure 4).&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130510-4.png" alt="Creating a Weather App for iOS with Forecast.io: Part 1 - Creating the Three Main View Controller Classes" /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 4: Creating the Three Main View Controller Classes&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 2: Creating a View Deck Controller&lt;/h3&gt;&lt;p&gt;Open &lt;strong&gt;MTAppDelegate.m&lt;/strong&gt; and import the header file the three &lt;code&gt;UIViewController&lt;/code&gt; subclasses. Add a class extension and create a property of type &lt;code&gt;IIViewDeckController&lt;/code&gt; and name it &lt;code&gt;viewDeckController&lt;/code&gt;. The reason for this will become clear in a moment.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;quot;MTAppDelegate.h&amp;quot;
#import &amp;quot;MTWeatherViewController.h&amp;quot;
#import &amp;quot;MTForecastViewController.h&amp;quot;
#import &amp;quot;MTLocationsViewController.h&amp;quot;
@interface MTAppDelegate ()
@property (strong, nonatomic) IIViewDeckController *viewDeckController;
@end
&lt;/pre&gt;&lt;p&gt;In &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt;, we start by creating an instance of each of the three &lt;code&gt;UIViewController&lt;/code&gt; subclasses. We then initialize an instance of the &lt;code&gt;IIViewDeckController&lt;/code&gt; class and pass the view controller objects as arguments to &lt;code&gt;initWithCenterViewController:leftViewController:rightViewController:&lt;/code&gt;. As the initializer indicates, the view deck controller that we create manages a center, left, and right view controller. The rest of the implementation of &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt; should be familiar to you. We initialize the application window and set the view deck controller as its root view controller.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Initialize View Controllers
    MTLocationsViewController *leftViewController = [[MTLocationsViewController alloc] initWithNibName:@&amp;quot;MTLocationsViewController&amp;quot; bundle:nil];
    MTForecastViewController *rightViewController = [[MTForecastViewController alloc] initWithNibName:@&amp;quot;MTForecastViewController&amp;quot; bundle:nil];
    MTWeatherViewController *centerViewController = [[MTWeatherViewController alloc] initWithNibName:@&amp;quot;MTWeatherViewController&amp;quot; bundle:nil];
    // Initialize View Deck Controller
    self.viewDeckController = [[IIViewDeckController alloc] initWithCenterViewController:centerViewController leftViewController:leftViewController rightViewController:rightViewController];
    // Initialize Window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Configure Window
    [self.window setRootViewController:self.viewDeckController];
    [self.window makeKeyAndVisible];
    return YES;
}
&lt;/pre&gt;&lt;p&gt;Build and run the application in the iOS Simulator or on a physical device to see the ViewDeck library in action. Even though the views of the view controllers are empty, the fundamental application structure is ready.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;3.&lt;/span&gt; Adding Locations&lt;/h2&gt;&lt;p&gt;As I mentioned in the introduction, in this tutorial we will also add the ability to add locations to the list of locations managed by the application. The &lt;code&gt;MTLocationsViewController&lt;/code&gt; class is in charge of managing the list of locations and presenting them in a table view. The user can add the current location to the list of locations by tapping the table view&amp;#8217;s first row, which will be labeled &amp;#8220;Add Current Location&amp;#8221;. Adding a new location with the Core Location framework is a responsibility of the &lt;code&gt;MTWeatherViewController&lt;/code&gt; class as we will see in a few minutes.&lt;/p&gt;&lt;p&gt;This leaves us with a problem. How is the weather view controller notified when the user has tapped the first row of the locations view controller? Notification center? Delegation is a better choice in this context. Whenever I encounter the need for a one-way communication between two objects, I tend to choose for delegation in favor of notifications. A delegate protocol is much easier to wrap your head around and extending it is as simple as declaring another method.&lt;/p&gt;&lt;p&gt;Another option would be to pass a reference to the weather view controller to the locations view controller, but I don&amp;#8217;t like this type of tight coupling. Tight coupling makes code less reusable and it results in unnecessarily complex object hierarchies when the code base grows over time. Delegation is the right choice for this problem.&lt;/p&gt;&lt;h3&gt;Step 1: Declaring the Delegate Protocol&lt;/h3&gt;&lt;p&gt;Open &lt;strong&gt;MTLocationsViewController.h&lt;/strong&gt; and update the header file as shown below. We create a property for the view controller&amp;#8217;s delegate and we declare the &lt;code&gt;MTLocationsViewControllerDelegate&lt;/code&gt; protocol. The protocol defines two methods, (1) &lt;code&gt;controllerShouldAddCurrentLocation:&lt;/code&gt;, which is invoked when the first row in the view controller&amp;#8217;s table view is tapped, and (2) &lt;code&gt;controller:didSelectLocation:&lt;/code&gt;, which is invoked when the user select a location from the list of locations.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;lt;UIKit/UIKit.h&amp;gt;
@protocol MTLocationsViewControllerDelegate;
@interface MTLocationsViewController : UIViewController
@property (weak, nonatomic) id&amp;lt;MTLocationsViewControllerDelegate&amp;gt; delegate;
@end
@protocol MTLocationsViewControllerDelegate &amp;lt;NSObject&amp;gt;
- (void)controllerShouldAddCurrentLocation:(MTLocationsViewController *)controller;
- (void)controller:(MTLocationsViewController *)controller didSelectLocation:(NSDictionary *)location;
@end
&lt;/pre&gt;&lt;h3&gt;Step 2: Adding the Table View&lt;/h3&gt;&lt;p&gt;Revisit &lt;strong&gt;MTLocationsViewController.h&lt;/strong&gt; one more time. Create an outlet for the view controller&amp;#8217;s table view and make sure to conform the &lt;code&gt;MTLocationsViewController&lt;/code&gt; class to the &lt;code&gt;UITableViewDataSource&lt;/code&gt; and &lt;code&gt;UITableViewDelegate&lt;/code&gt; protocols.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;lt;UIKit/UIKit.h&amp;gt;
@protocol MTLocationsViewControllerDelegate;
@interface MTLocationsViewController : UIViewController &amp;lt;UITableViewDataSource, UITableViewDelegate&amp;gt;
@property (weak, nonatomic) id&amp;lt;MTLocationsViewControllerDelegate&amp;gt; delegate;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@end
@protocol MTLocationsViewControllerDelegate &amp;lt;NSObject&amp;gt;
- (void)controllerShouldAddCurrentLocation:(MTLocationsViewController *)controller;
- (void)controller:(MTLocationsViewController *)controller didSelectLocation:(NSDictionary *)location;
@end
&lt;/pre&gt;&lt;p&gt;Open &lt;strong&gt;MTLocationsViewController.xib&lt;/strong&gt;, add a table view to the view controller&amp;#8217;s view, and set the table view&amp;#8217;s &lt;code&gt;dataSource&lt;/code&gt; and &lt;code&gt;delegate&lt;/code&gt; outlets to the &lt;strong&gt;File&amp;#8217;s Owner&lt;/strong&gt; object. Select the &lt;strong&gt;File&amp;#8217;s Owner&lt;/strong&gt; object and connect its &lt;code&gt;tableView&lt;/code&gt; outlet with the table view that we added to the view controller&amp;#8217;s view (figure 5).&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130510-5.png" alt="Creating a Weather App for iOS with Forecast.io: Part 1 - Adding a Table View to List the Locations " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 5: Adding a Table View to List the Locations&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 3: Populating the Table View&lt;/h3&gt;&lt;p&gt;Before we implement the &lt;code&gt;UITableViewDataSource&lt;/code&gt; and &lt;code&gt;UITableViewDelegate&lt;/code&gt; protocols, we need to create a property that will serve as the table view&amp;#8217;s data source. Create a class extension at the top of &lt;strong&gt;MTLocationsViewController.m&lt;/strong&gt; and create a property named &lt;code&gt;locations&lt;/code&gt; of type &lt;code&gt;NSMutableArray&lt;/code&gt;. It will store the locations managed by our application.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;quot;MTLocationsViewController.h&amp;quot;
@interface MTLocationsViewController ()
@property (strong, nonatomic) NSMutableArray *locations;
@end
&lt;/pre&gt;&lt;p&gt;Implementing the &lt;code&gt;UITableViewDataSource&lt;/code&gt; and the &lt;code&gt;UITableViewDelegate&lt;/code&gt; protocols is pretty straightforward. We start by declaring a static string constant for the cell reuse identifier. In the view controller&amp;#8217;s &lt;code&gt;viewDidLoad&lt;/code&gt; method we invoke &lt;code&gt;setupView&lt;/code&gt;, a helper method in which we configure the view controller&amp;#8217;s user interface. In &lt;code&gt;setupView&lt;/code&gt;, we tell the table view to use the &lt;code&gt;UITableViewCell&lt;/code&gt; class to instantiate new table view cells for the reuse identifier we declared earlier.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
static NSString *LocationCell = @&amp;quot;LocationCell&amp;quot;;
&lt;/pre&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)viewDidLoad {
    [super viewDidLoad];
    // Setup View
    [self setupView];
}
&lt;/pre&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)setupView {
    // Register Class for Cell Reuse
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:LocationCell];
}
&lt;/pre&gt;&lt;p&gt;Implementing the &lt;code&gt;UITableViewDataSource&lt;/code&gt; and &lt;code&gt;UITableViewDelegate&lt;/code&gt; protocols is trivial as you can see below. Two implementation details require a bit explaining. We store each location as a dictionary with four keys, (1) city, (2) country, (3) latitude, and (4) longitude. With this in mind, the implementation of &lt;code&gt;configureCell:atIndexPath:&lt;/code&gt; should become a bit clearer. Note that &lt;code&gt;configureCell:atIndexPath:&lt;/code&gt; is nothing more than another helper method.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return ([self.locations count] + 1);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:LocationCell forIndexPath:indexPath];
    // Configure Cell
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row == 0) {
        [cell.textLabel setText:@&amp;quot;Add Current Location&amp;quot;];
    } else {
        // Fetch Location
        NSDictionary *location = [self.locations objectAtIndex:(indexPath.row - 1)];
        // Configure Cell
        [cell.textLabel setText:[NSString stringWithFormat:@&amp;quot;%@, %@&amp;quot;, location[@&amp;quot;city&amp;quot;], location[@&amp;quot;country&amp;quot;]]];
    }
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}
&lt;/pre&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    if (indexPath.row == 0) {
        // Notify Delegate
        [self.delegate controllerShouldAddCurrentLocation:self];
    } else {
        // Fetch Location
        NSDictionary *location = [self.locations objectAtIndex:(indexPath.row - 1)];
        // Notify Delegate
        [self.delegate controller:self didSelectLocation:location];
    }
    // Show Center View Controller
    [self.viewDeckController closeLeftViewAnimated:YES];
}
&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;tableView:didSelectRowAtIndexPath:&lt;/code&gt; also requires a short explanation. If the user taps the first row, labeled &lt;strong&gt;Add Current Location&lt;/strong&gt;, the delegate is notified that the current location should be added to the list of locations. If any other row in the table view is tapped, the corresponding location is passed as the second argument of &lt;code&gt;controller:didSelectLocation:&lt;/code&gt;, another delegate method of the &lt;code&gt;MTLocationsViewController&lt;/code&gt; delegate protocol. This informs the delegate that the application should set the new location as the application&amp;#8217;t default location and the weather data for that location should be fetched.&lt;/p&gt;&lt;p&gt;The last line of &lt;code&gt;tableView:didSelectRowAtIndexPath:&lt;/code&gt; is also worth mentioning. The &lt;code&gt;IIViewDeckController&lt;/code&gt; instance assigns itself to the view controllers it manages. The &lt;code&gt;viewDeckController&lt;/code&gt; property provides access to a view controller&amp;#8217;s view deck controller, which is convenient if you need to access the view controller&amp;#8217;s view deck. It works very much like the &lt;code&gt;navigationController&lt;/code&gt; property of a view controller instance. In the last line of &lt;code&gt;tableView:didSelectRowAtIndexPath:&lt;/code&gt;, we tell the view deck controller to close the left view, which means that the center view becomes visible again.&lt;/p&gt;&lt;h3&gt;Step 4: Keys and Constants&lt;/h3&gt;&lt;p&gt;Before we continue populating the locations table view, we need to pay attention to some best practices. We currently use string literals to access the values of the location dictionary. Even though this works perfectly fine, it is better and safer to use string constants for this purpose. To make all this easy and maintainable, we declare the string constants in a central location. Let me show you how this works.&lt;/p&gt;&lt;p&gt;Create a subclass of &lt;code&gt;NSObject&lt;/code&gt; and name it &lt;code&gt;MTConstants&lt;/code&gt;. Replace the contents of &lt;strong&gt;MTConstants.h&lt;/strong&gt; and &lt;strong&gt;MTConstants.m&lt;/strong&gt; with the snippets shown below. It should be clear that &lt;code&gt;MTConstants&lt;/code&gt; is not an Objective-C class. It is nothing more than a central place to store a set of constants specific to our project.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#pragma mark -
#pragma mark User Defaults
extern NSString * const MTRainUserDefaultsLocation;
extern NSString * const MTRainUserDefaultsLocations;
#pragma mark -
#pragma mark Notifications
extern NSString * const MTRainDidAddLocationNotification;
extern NSString * const MTRainLocationDidChangeNotification;
#pragma mark -
#pragma mark Location Keys
extern NSString * const MTLocationKeyCity;
extern NSString * const MTLocationKeyCountry;
extern NSString * const MTLocationKeyLatitude;
extern NSString * const MTLocationKeyLongitude;
&lt;/pre&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;quot;MTConstants.h&amp;quot;
#pragma mark -
#pragma mark User Defaults
NSString * const MTRainUserDefaultsLocation = @&amp;quot;location&amp;quot;;
NSString * const MTRainUserDefaultsLocations = @&amp;quot;locations&amp;quot;;
#pragma mark -
#pragma mark Notifications
NSString * const MTRainDidAddLocationNotification = @&amp;quot;com.mobileTuts.MTRainDidAddLocationNotification&amp;quot;;
NSString * const MTRainLocationDidChangeNotification = @&amp;quot;com.mobileTuts.MTRainLocationDidChangeNotification&amp;quot;;
#pragma mark -
#pragma mark Location Keys
NSString * const MTLocationKeyCity = @&amp;quot;city&amp;quot;;
NSString * const MTLocationKeyCountry = @&amp;quot;country&amp;quot;;
NSString * const MTLocationKeyLatitude = @&amp;quot;latitude&amp;quot;;
NSString * const MTLocationKeyLongitude = @&amp;quot;longitude&amp;quot;;
&lt;/pre&gt;&lt;p&gt;To make &lt;code&gt;MTConstants&lt;/code&gt; really useful, add an import statement for &lt;strong&gt;MTConstants.h&lt;/strong&gt; to your project&amp;#8217;s precompiled header file so the constants declared in &lt;code&gt;MTConstants&lt;/code&gt; are available throughout the project.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;lt;Availability.h&amp;gt;
#ifndef __IPHONE_3_0
#warning &amp;quot;This project uses features only available in iOS SDK 3.0 and later.&amp;quot;
#endif
#ifdef __OBJC__
    #import &amp;lt;UIKit/UIKit.h&amp;gt;
    #import &amp;lt;Foundation/Foundation.h&amp;gt;
    #import &amp;lt;QuartzCore/QuartzCore.h&amp;gt;
    #import &amp;lt;CoreLocation/CoreLocation.h&amp;gt;
    #import &amp;lt;MobileCoreServices/MobileCoreServices.h&amp;gt;
    #import &amp;lt;SystemConfiguration/SystemConfiguration.h&amp;gt;
    #import &amp;quot;AFNetworking.h&amp;quot;
    #import &amp;quot;SVProgressHUD.h&amp;quot;
    #import &amp;quot;IIViewDeckController.h&amp;quot;
    #import &amp;quot;MTConstants.h&amp;quot;
#endif
&lt;/pre&gt;&lt;p&gt;We can now update &lt;code&gt;configureCell:atIndexPath:&lt;/code&gt; (&lt;strong&gt;MTLocationsViewController.m&lt;/strong&gt;) as shown below. Not only will this give use code completion and a very, very small performance gain, the true benefit of this best practice is that the compiler will warn us in case of typos. I&amp;#8217;m sure I don&amp;#8217;t have to tell you that typos are one of the most common causes of bugs in software development.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row == 0) {
        [cell.textLabel setText:@&amp;quot;Add Current Location&amp;quot;];
    } else {
        // Fetch Location
        NSDictionary *location = [self.locations objectAtIndex:(indexPath.row - 1)];
        // Configure Cell
        [cell.textLabel setText:[NSString stringWithFormat:@&amp;quot;%@, %@&amp;quot;, location[MTLocationKeyCity], location[MTLocationKeyCountry]]];
    }
}
&lt;/pre&gt;&lt;p&gt;At the moment, the &lt;code&gt;locations&lt;/code&gt; property is empty and so will the table view. In &lt;code&gt;initWithNibName:bundle:&lt;/code&gt;, we invoke &lt;code&gt;loadLocations&lt;/code&gt;, a helper method that loads the array of locations. In &lt;code&gt;loadLocations&lt;/code&gt;, load the array of locations that is stored in the application&amp;#8217;s user defaults database. Note that we use another string constant that we declared in &lt;strong&gt;MTConstants.h&lt;/strong&gt;.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Load Locations
        [self loadLocations];
    }
    return self;
}
&lt;/pre&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)loadLocations {
    self.locations = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:MTRainUserDefaultsLocations]];
}
&lt;/pre&gt;&lt;h3&gt;Step 5: Delegate Assignment&lt;/h3&gt;&lt;p&gt;As I mentioned earlier, the &lt;code&gt;MTWeatherViewController&lt;/code&gt; instance will serve as the delegate of the locations view controller. Revisit &lt;strong&gt;MTAppDelegate.m&lt;/strong&gt; and update &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt; as shown below.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Initialize View Controllers
    MTLocationsViewController *leftViewController = [[MTLocationsViewController alloc] initWithNibName:@&amp;quot;MTLocationsViewController&amp;quot; bundle:nil];
    MTForecastViewController *rightViewController = [[MTForecastViewController alloc] initWithNibName:@&amp;quot;MTForecastViewController&amp;quot; bundle:nil];
    MTWeatherViewController *centerViewController = [[MTWeatherViewController alloc] initWithNibName:@&amp;quot;MTWeatherViewController&amp;quot; bundle:nil];
    // Configure Locations View Controller
    [leftViewController setDelegate:centerViewController];
    // Initialize View Deck Controller
    self.viewDeckController = [[IIViewDeckController alloc] initWithCenterViewController:centerViewController leftViewController:leftViewController rightViewController:rightViewController];
    // Initialize Window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Configure Window
    [self.window setRootViewController:self.viewDeckController];
    [self.window makeKeyAndVisible];
    return YES;
}
&lt;/pre&gt;&lt;p&gt;By making this change, a warning should immediately pop up telling you that &lt;code&gt;MTWeatherViewController&lt;/code&gt; does not conform to the &lt;code&gt;MTLocationsViewControllerDelegate&lt;/code&gt; protocol. The compiler is right so let&amp;#8217;s fix this.&lt;/p&gt;&lt;p&gt;Open &lt;strong&gt;MTWeatherViewController.h&lt;/strong&gt;, import the header file of &lt;code&gt;MTLocationsViewController&lt;/code&gt;, and conform &lt;code&gt;MTWeatherViewController&lt;/code&gt; to the &lt;code&gt;MTLocationsViewControllerDelegate&lt;/code&gt; protocol.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;lt;UIKit/UIKit.h&amp;gt;
#import &amp;quot;MTLocationsViewController.h&amp;quot;
@interface MTWeatherViewController : UIViewController &amp;lt;MTLocationsViewControllerDelegate&amp;gt;
@end
&lt;/pre&gt;&lt;p&gt;Wait. Another warning? We haven&amp;#8217;t implemented the two required methods of the delegate protocol yet hence the warning. Open &lt;strong&gt;MTWeatherViewController.m&lt;/strong&gt; and add a stub implementation for each of the delegate methods.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)controllerShouldAddCurrentLocation:(MTLocationsViewController *)controller {
    NSLog(@&amp;quot;%s&amp;quot;, __PRETTY_FUNCTION__);
}
- (void)controller:(MTLocationsViewController *)controller didSelectLocation:(NSDictionary *)location {
    NSLog(@&amp;quot;%s&amp;quot;, __PRETTY_FUNCTION__);
}
&lt;/pre&gt;&lt;h3&gt;Step 6: Fetching the Current Location&lt;/h3&gt;&lt;p&gt;It is finally time to fetch the current location of the device. Add a class extension at the top of &lt;strong&gt;MTWeatherViewController.m&lt;/strong&gt; and declare two properties, (1) &lt;code&gt;location&lt;/code&gt; (&lt;code&gt;NSDictionary&lt;/code&gt;) to store the application&amp;#8217;s default location and (2) &lt;code&gt;locationManager&lt;/code&gt; (&lt;code&gt;CLLocationManager&lt;/code&gt;), which we will use to fetch the device&amp;#8217;s location. Conform the &lt;code&gt;MTWeatherViewController&lt;/code&gt; class to the &lt;code&gt;CLLocationManagerDelegate&lt;/code&gt; protocol and declare an instance variable named &lt;code&gt;_locationFound&lt;/code&gt; of type &lt;code&gt;BOOL&lt;/code&gt;. The purpose of &lt;code&gt;_locationFound&lt;/code&gt; will become clear in a few minutes.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;quot;MTWeatherViewController.h&amp;quot;
@interface MTWeatherViewController () &amp;lt;CLLocationManagerDelegate&amp;gt; {
    BOOL _locationFound;
}
@property (strong, nonatomic) NSDictionary *location;
@property (strong, nonatomic) CLLocationManager *locationManager;
@end
&lt;/pre&gt;&lt;p&gt;In the class&amp;#8217;s designated initializer, &lt;code&gt;initWithNibName:bundle:&lt;/code&gt;, we initialize and configure the location manager. We assign the view controller as the location manager&amp;#8217;s delegate and set the location manager&amp;#8217;s &lt;code&gt;accuracy&lt;/code&gt; property to &lt;code&gt;kCLLocationAccuracyKilometer&lt;/code&gt;. There is no need for better accuracy as we only need the location for weather data.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Initialize Location Manager
        self.locationManager = [[CLLocationManager alloc] init];
        // Configure Location Manager
        [self.locationManager setDelegate:self];
        [self.locationManager setDesiredAccuracy:kCLLocationAccuracyKilometer];
    }
    return self;
}
&lt;/pre&gt;&lt;p&gt;The next piece of the puzzle is implementing, &lt;code&gt;locationManager:didUpdateLocations:&lt;/code&gt;, one of the methods of the &lt;code&gt;CLLocationManagerDelegate&lt;/code&gt; protocol, which is invoked each time the location manager has updated the device&amp;#8217;s location. The second argument of &lt;code&gt;locationManager:didUpdateLocations:&lt;/code&gt; is an array &lt;code&gt;CLLocation&lt;/code&gt; instances. The implementation of &lt;code&gt;locationManager:didUpdateLocations:&lt;/code&gt; also reveals the purpose of &lt;code&gt;_locationFound&lt;/code&gt;. Despite the fact that we tell the location manager to stop updating the location as soon as &lt;code&gt;locationManager:didUpdateLocations:&lt;/code&gt; is invoked, it is not uncommon that another update of the location invokes &lt;code&gt;locationManager:didUpdateLocations:&lt;/code&gt; again even after sending the location manager a message of &lt;code&gt;stopUpdatingLocation&lt;/code&gt;. If this were to happen, the same location would be added twice to the list of locations. The simple solution is to use a helper variable, &lt;code&gt;_locationFound&lt;/code&gt;, that keeps track of the state that we&amp;#8217;re in.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    if (![locations count] || _locationFound) return;
    // Stop Updating Location
    _locationFound = YES;
    [manager stopUpdatingLocation];
    // Current Location
    CLLocation *currentLocation = [locations objectAtIndex:0];
    // Reverse Geocode
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
        if ([placemarks count]) {
            _locationFound = NO;
            [self processPlacemark:[placemarks objectAtIndex:0]];
        }
    }];
}
&lt;/pre&gt;&lt;p&gt;We extract the first location from the array of locations and use the &lt;code&gt;CLGeocoder&lt;/code&gt; class to reverse geocode that location. Reverse geocoding simply means finding out the name of the (closest) city and the location&amp;#8217;s country. The completion handler of &lt;code&gt;reverseGeocodeLocation:&lt;/code&gt; returns an array of placemarks. A placemark object is nothing more than a container for storing location data for a coordinate.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)processPlacemark:(CLPlacemark *)placemark {
    // Extract Data
    NSString *city = [placemark locality];
    NSString *country = [placemark country];
    CLLocationDegrees lat = placemark.location.coordinate.latitude;
    CLLocationDegrees lon = placemark.location.coordinate.longitude;
    // Create Location Dictionary
    NSDictionary *currentLocation = @{ MTLocationKeyCity : city,
                                       MTLocationKeyCountry : country,
                                       MTLocationKeyLatitude : @(lat),
                                       MTLocationKeyLongitude : @(lon) };
    // Add to Locations
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    NSMutableArray *locations = [NSMutableArray arrayWithArray:[ud objectForKey:MTRainUserDefaultsLocations]];
    [locations addObject:currentLocation];
    [locations sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:MTLocationKeyCity ascending:YES]]];
    [ud setObject:locations forKey:MTRainUserDefaultsLocations];
    // Synchronize
    [ud synchronize];
    // Update Current Location
    self.location = currentLocation;
    // Post Notifications
    NSNotification *notification2 = [NSNotification notificationWithName:MTRainDidAddLocationNotification object:self userInfo:currentLocation];
    [[NSNotificationCenter defaultCenter] postNotification:notification2];
}
&lt;/pre&gt;&lt;p&gt;In &lt;code&gt;processPlacemark:&lt;/code&gt;, we extract the data that we&amp;#8217;re looking for, city, country, latitude, and longitude, store it in a dictionary, and update the array of locations in the application&amp;#8217;s user defaults database. Note that we sort the array of locations before updating the user defaults database. The view controller&amp;#8217;s &lt;code&gt;location&lt;/code&gt; property is updated with the new location and a notification is sent to notify any object interested in this event.&lt;/p&gt;&lt;p&gt;That&amp;#8217;s not all, though. I have also overridden the setter of the view controller&amp;#8217;s &lt;code&gt;location&lt;/code&gt; property. Because the &lt;code&gt;MTWeatherViewController&lt;/code&gt; class is in charge of adding new locations, we can delegate a few additional responsibilities to this class, such as updating the default location in the user defaults database. Because other parts of the application also need to know about a change in location, a notification with name &lt;code&gt;MTRainLocationDidChangeNotification&lt;/code&gt; is posted. We also invoke &lt;code&gt;updateView&lt;/code&gt;, another helper method that we will implement shortly.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)setLocation:(NSDictionary *)location {
    if (_location != location) {
        _location = location;
        // Update User Defaults
        NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
        [ud setObject:location forKey:MTRainUserDefaultsLocation];
        [ud synchronize];
        // Post Notification
        NSNotification *notification1 = [NSNotification notificationWithName:MTRainLocationDidChangeNotification object:self userInfo:location];
        [[NSNotificationCenter defaultCenter] postNotification:notification1];
        // Update View
        [self updateView];
    }
}
&lt;/pre&gt;&lt;h3&gt;Step 7: Adding a Label&lt;/h3&gt;&lt;p&gt;We won&amp;#8217;t spend much time on the user interface in this tutorial, but to make sure that everything works as we expect, it is good to have some visual feedback by adding a label to the weather view controller&amp;#8217;s view displaying the selected location. Open &lt;strong&gt;MTWeatherViewController.h&lt;/strong&gt; and create an outlet of type &lt;code&gt;UILabel&lt;/code&gt; and name it &lt;code&gt;labelLocation&lt;/code&gt;.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
#import &amp;lt;UIKit/UIKit.h&amp;gt;
#import &amp;quot;MTLocationsViewController.h&amp;quot;
@interface MTWeatherViewController : UIViewController &amp;lt;MTLocationsViewControllerDelegate&amp;gt;
@property (weak, nonatomic) IBOutlet UILabel *labelLocation;
@end
&lt;/pre&gt;&lt;p&gt;Open &lt;strong&gt;MTWeatherViewController.xib&lt;/strong&gt;, add a label to the view controller&amp;#8217;s view, and connect the outlet with the newly added label (figure 6). In &lt;code&gt;updateView&lt;/code&gt; (&lt;strong&gt;MTWeatherViewController.m)&lt;/strong&gt;, we update the label with the new location as shown below.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130510-6.png" alt="Creating a Weather App for iOS with Forecast.io: Part 1 - Adding the Location Label to the Weather View Controller " /&gt;&lt;br
/&gt; &lt;/figure&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 6: Adding the Location Label to the Weather View Controller&lt;/figcaption&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)updateView {
    // Update Location Label
    [self.labelLocation setText:[self.location objectForKey:MTLocationKeyCity]];
}
&lt;/pre&gt;&lt;h3&gt;Step 8: Implementing the Delegate Protocol&lt;/h3&gt;&lt;p&gt;Thanks to the work we have done so far, implementing the two methods of the &lt;code&gt;MTLocationsViewControllerDelegate&lt;/code&gt; protocol is easy. In &lt;code&gt;controllerShouldAddCurrentLocation:&lt;/code&gt;, we tell the location manager to start updating the location. In &lt;code&gt;controller:didSelectLocation:&lt;/code&gt;, we set the view controller&amp;#8217;s &lt;code&gt;location&lt;/code&gt; property to the location that the user selected in the locations view controller, which in turn invokes the setter method we overrode a bit earlier.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)controllerShouldAddCurrentLocation:(MTLocationsViewController *)controller {
    // Start Updating Location
    [self.locationManager startUpdatingLocation];
}
- (void)controller:(MTLocationsViewController *)controller didSelectLocation:(NSDictionary *)location {
    // Update Location
    self.location = location;
}
&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;4.&lt;/span&gt; Final Touches&lt;/h2&gt;&lt;p&gt;Before wrapping up the first installment of this series, we need to add a few final touches. When the user launches the application, for example, the &lt;code&gt;location&lt;/code&gt; property of the &lt;code&gt;MTWeatherViewController&lt;/code&gt; class needs to be set to the location stored in the application&amp;#8217;s user defaults. In addition, when the user launches our application for the very first time, a default location is not yet set in the application&amp;#8217;s user defaults. This isn&amp;#8217;t a big problem, but to offer a good user experience it would be better to automatically fetch the user&amp;#8217;s current location when the application launches for the first time.&lt;/p&gt;&lt;p&gt;We can make both changes by amending the weather view controller&amp;#8217;s &lt;code&gt;viewDidLoad&lt;/code&gt; as shown below. The view controller&amp;#8217;s location property is set to the location stored in the user defaults database. If no location is found, that is, &lt;code&gt;self.location&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;, we tell the location manager to start updating the location. In other words, when the application is launched for the first time, the current location is automatically retrieved and stored.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)viewDidLoad {
    [super viewDidLoad];
    // Load Location
    self.location = [[NSUserDefaults standardUserDefaults] objectForKey:MTRainUserDefaultsLocation];
    if (!self.location) {
        [self.locationManager startUpdatingLocation];
    }
}
&lt;/pre&gt;&lt;p&gt;There is one more loose end that we need to tie up. When a new location is added to the array of locations, the weather view controller posts a notification. We need to update the &lt;code&gt;MTLocationsViewController&lt;/code&gt; class so that it adds itself as an observer for these notifications. By doing so, the locations view controller can update its table view whenever a new location is added.&lt;/p&gt;&lt;p&gt;Revisit &lt;strong&gt;MTLocationsViewController.m&lt;/strong&gt; and update the &lt;code&gt;initWithNibName:bundle:&lt;/code&gt; as shown below. We add the view controller as an observer for notifications with a name of &lt;code&gt;MTRainDidAddLocationNotification&lt;/code&gt;. The implementation of &lt;code&gt;didAddLocation:&lt;/code&gt; is straightforward, that is, we add the new location to the array of locations, sort the array by city, and reload the table view.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Load Locations
        [self loadLocations];
        // Add Observer
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didAddLocation:) name:MTRainDidAddLocationNotification object:nil];
    }
    return self;
}
&lt;/pre&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)didAddLocation:(NSNotification *)notification {
    NSDictionary *location = [notification userInfo];
    [self.locations addObject:location];
    [self.locations sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:MTLocationKeyCity ascending:YES]]];
    [self.tableView reloadData];
}
&lt;/pre&gt;&lt;p&gt;Don&amp;#8217;t forget to remove the view controller as an observer in the view controller&amp;#8217;s &lt;code&gt;dealloc&lt;/code&gt; method. It is also good practice to set the view controller&amp;#8217;s &lt;code&gt;delegate&lt;/code&gt; property to &lt;code&gt;nil&lt;/code&gt; in &lt;code&gt;dealloc&lt;/code&gt;.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
- (void)dealloc {
    // Remove Observer
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    if (_delegate) {
        _delegate = nil;
    }
}
&lt;/pre&gt;&lt;p&gt;Build and run the application to see how all this works together. You may want to run the application in the iOS Simulator instead of a on physical device, because the iOS Simulator supports location simulation (figure 7), which makes it much easier to test location based applications such as the one we created.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130510-7.png" alt="Creating a Weather App for iOS with Forecast.io: Part 1 - Simulating Locations with the iOS Simulator " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 7: Simulating Locations with the iOS Simulator&lt;/figcaption&gt; &lt;/figure&gt;&lt;hr
/&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Even though we haven&amp;#8217;t even touched the Forecast API, we did quite a bit of work in this article. I hope you have tried CocoaPods and are convinced of its power and flexibility. In the next installment of this series, we focus on the Forecast API and the AFNetworking library.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/WlKCi2IWoag" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/tutorials/iphone/create-a-weather-app-with-forecast-project-setup/feed/</wfw:commentRss> <slash:comments>2</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/tutorials/iphone/create-a-weather-app-with-forecast-project-setup/</feedburner:origLink></item> <item><title>iOS SDK: Customizing Popovers</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/h6IW_c1OyYM/</link> <comments>http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-customizing-popovers/#comments</comments> <pubDate>Thu, 09 May 2013 15:47:26 +0000</pubDate> <dc:creator>Aaron Crabtree</dc:creator> <category><![CDATA[iOS SDK]]></category> <category><![CDATA[custom popover]]></category> <category><![CDATA[ios]]></category> <category><![CDATA[UIPopoverBackgroundView]]></category> <category><![CDATA[UIPopoverController]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=16090</guid> <description>&lt;p&gt;Popovers are a great way to display supplementary information in an iPad app. Inevitably, as with most iOS objects, a little customization goes a long way in creating a design that is unique and fits in with your own app. In this tutorial, we will build a basic popover, then explore customizations one at a time, giving you an easy-to-follow path to implement customizations in your own app.&lt;br
/&gt;&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;1.&lt;/span&gt; Setting Up Your Project&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;Launch Xcode and choose &lt;em&gt;File &amp;gt; New &amp;gt; Project&lt;/em&gt; to create a new project. Select an &lt;strong&gt;iOS Single View Application&lt;/strong&gt; and click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;a
href="http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-customizing-popovers/attachment/custom-popovers-create-new-project/" rel="attachment wp-att-16133"&gt;&lt;img
class="alignnone size-full wp-image-16133" alt="Figure 1" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/aaron-crabtree/custom-popovers-create-new-project.jpg" width="596" height="404" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 1&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;Fill in the text fields with your project name, organization name, and company identifier. Select &lt;strong&gt;iPad&lt;/strong&gt; from the &lt;em&gt;Devices&lt;/em&gt; drop down and make sure the box next to &lt;em&gt;Use Automatic Reference Counting&lt;/em&gt; is checked. Leave the boxes for &lt;em&gt;Use Storyboards&lt;/em&gt; and &lt;em&gt;Include Unit Tests&lt;/em&gt; unchecked and click &lt;strong&gt;Next&lt;/strong&gt;. Choose a location to save your file and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;a
href="http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-customizing-popovers/attachment/custom-popover-naming-project/" rel="attachment wp-att-16129"&gt;&lt;img
class="alignnone size-full wp-image-16129" alt="Figure 2" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/aaron-crabtree/custom-popover-naming-project.jpg" width="600" height="404" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 2&lt;/figcaption&gt; &lt;/figure&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;2.&lt;/span&gt; Adding a Navigation Controller&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;Let’s use a navigation controller so that we can add a button to show the popover. Click on &lt;em&gt;AppDelegate.m&lt;/em&gt; and find the &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt; method. Add the following code to create a navigation controller and set it as the root view controller.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = navController;&lt;/pre&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;Now we can add a plus button to the navigation bar. Click on &lt;em&gt;ViewController.m&lt;/em&gt; and add the following code to the &lt;code&gt;viewDidLoad&lt;/code&gt; method just below &lt;code&gt;[super viewDidLoad];&lt;/code&gt;.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;UIBarButtonItem *popoverButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
                     target:self
                     action:@selector(showPopover:)];
self.navigationItem.rightBarButtonItem = popoverButton;
&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;UIBarButtonSystemItemAdd&lt;/code&gt; creates a plus button; we’ll add it to the right side of the navigation bar. Next we’ll implement the &lt;code&gt;showPopover:&lt;/code&gt; method used as the selector.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;3.&lt;/span&gt; Showing the Popover&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;Before implementing the &lt;code&gt;showPopover:&lt;/code&gt; method, let’s add a property for the popover controller. Click on the &lt;em&gt;ViewController.h&lt;/em&gt; file and add the following property.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;@property (nonatomic, strong) UIPopoverController *popController;&lt;/pre&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;Navigate back to the &lt;em&gt;ViewController.m&lt;/em&gt; file and declare the &lt;code&gt;showPopover:&lt;/code&gt; method in the class extension as demonstrated below.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;@interface ViewController ()
- (void)showPopover:(id)sender;
@end&lt;/pre&gt;&lt;h3&gt;Step 3&lt;/h3&gt;&lt;p&gt;Add the following code to define the method under &lt;code&gt;@implementation&lt;/code&gt;.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;- (void)showPopover:(id)sender
{
    if (self.popController.popoverVisible) {
        [self.popController dismissPopoverAnimated:YES];
        return;
    }
    UIViewController *contentViewController = [[UIViewController alloc] init];
    contentViewController.view.backgroundColor = [UIColor yellowColor];
    UIPopoverController *popController = [[UIPopoverController alloc] initWithContentViewController:contentViewController];
    popController.popoverContentSize = CGSizeMake(300.0f, 600.0f);
    self.popController = popController;
    [self.popController presentPopoverFromBarButtonItem:sender
                    permittedArrowDirections:UIPopoverArrowDirectionUp
                                    animated:YES];
}&lt;/pre&gt;&lt;p&gt;First we check to see if the popover is already being shown on the screen. If it is visible, the popover is dismissed and the method returns. If the popover is not being shown on the screen, we create a view controller to be displayed in the popover. Then we create the popover controller and set its size. The last line of code tells the popover controller to present itself from the navigation bar button that was tapped &amp;#8211; in this case the plus button -and allows the arrow direction to point up only. One benefit to using this method is that if the user taps another button on the navigation bar, the tap is passed through to the navigation bar.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;4.&lt;/span&gt; Testing the Standard Popover&lt;/h2&gt;&lt;p&gt;At this point, we have implemented a standard popover. Build and run your project and tap the plus button to see a basic popover appear. Let’s take a look at the basics of customizing its appearance.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;5.&lt;/span&gt; Subclassing &lt;code&gt;UIPopoverBackgroundView&lt;/code&gt;&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;In order to customize the appearance of the popover, we’ll need to subclass &lt;code&gt;UIPopoverBackgroundView&lt;/code&gt;. Click &lt;em&gt;File &amp;gt; New &amp;gt; File&lt;/em&gt;, choose an &lt;strong&gt;iOS Cocoa Touch Objective-C Class&lt;/strong&gt;, and click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;a
href="http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-customizing-popovers/attachment/custom-popover-create-new-file/" rel="attachment wp-att-16128"&gt;&lt;img
class="alignnone size-full wp-image-16128" alt="Figure 3" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/aaron-crabtree/custom-popover-create-new-file.jpg" width="600" height="405" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 3&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;Name the class &lt;em&gt;PopoverBackgroundView&lt;/em&gt; and choose &lt;code&gt;UIPopoverBackgroundView&lt;/code&gt; from the &lt;em&gt;Subclass of&lt;/em&gt; drop down.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;a
href="http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-customizing-popovers/attachment/custom-popover-naming-subclass/" rel="attachment wp-att-16130"&gt;&lt;img
class="alignnone size-full wp-image-16130" alt="Figure 4" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/aaron-crabtree/custom-popover-naming-subclass.jpg" width="600" height="405" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 4&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 3&lt;/h3&gt;&lt;p&gt;There are two &lt;code&gt;UIPopoverBackgroundView&lt;/code&gt; properties that need to be overridden. Add the following code to synthesize the arrow direction and arrow offset, overriding the setter and getter methods for these two properties.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;
@synthesize arrowDirection  = _arrowDirection;
@synthesize arrowOffset     = _arrowOffset;&lt;/pre&gt;&lt;h3&gt;Step 4&lt;/h3&gt;&lt;p&gt;There are three class methods that need to be overridden. Let’s define some values to use with the methods.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;#define kArrowBase 30.0f
#define kArrowHeight 20.0f
#define kBorderInset 8.0f&lt;/pre&gt;&lt;h3&gt;Step 5&lt;/h3&gt;&lt;p&gt;Add the code below to override the &lt;code&gt;arrowBase&lt;/code&gt;, &lt;code&gt;arrowHeight&lt;/code&gt; and &lt;code&gt;contentViewInsets&lt;/code&gt; methods.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;+ (CGFloat)arrowBase
{
    return kArrowBase;
}
+ (CGFloat)arrowHeight
{
    return kArrowHeight;
}
+ (UIEdgeInsets)contentViewInsets
{
    return UIEdgeInsetsMake(kBorderInset, kBorderInset, kBorderInset, 		kBorderInset);
}&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;arrowBase&lt;/code&gt; method determines the width of the arrow’s base, while the &lt;code&gt;arrowHeight&lt;/code&gt; method determines the height of the arrow. The &lt;code&gt;contentViewInsets&lt;/code&gt; method indicates how far from the edge of the background to display the content.&lt;/p&gt;&lt;h3&gt;Step 6&lt;/h3&gt;&lt;p&gt;Let’s add a background color so we can see the different pieces clearly. Add the following code inside the &lt;code&gt;if&lt;/code&gt; statement in the &lt;code&gt;initWithFrame:&lt;/code&gt; method.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;self.backgroundColor = [UIColor grayColor];&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;6.&lt;/span&gt; Setting the Popover Background View Class Property&lt;/h2&gt;&lt;p&gt;Before we can test the popover, we’ll need to import and set the popover controller’s popover background view class property. Click on the &lt;em&gt;ViewController.m&lt;/em&gt; file and import the popover background view header file as demonstrated below.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;#import &amp;quot;PopoverBackgroundView.h&amp;quot;&lt;/pre&gt;&lt;p&gt;While we&amp;#8217;re still in the &lt;em&gt;ViewController.m&lt;/em&gt; file, add the following line of code just below where we created the &lt;code&gt;UIPopoverController&lt;/code&gt; in the &lt;code&gt;showPopover:&lt;/code&gt; method.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;popController.popoverBackgroundViewClass = [PopoverBackgroundView class];&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;7.&lt;/span&gt; Testing the Popover Background View&lt;/h2&gt;&lt;p&gt;Build and run the project and tap the plus button to see the popover. You can see that the standard popover has been replaced with the customizations we’ve added so far. The gray border around the popover shows the insets returned from the &lt;code&gt;contentViewInsets&lt;/code&gt; method. You can adjust the insets as needed to achieve a desired look. We’ll draw an arrow later in the tutorial to display on the screen.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;8.&lt;/span&gt; Setting the Shadows and Rounded Corners&lt;/h2&gt;&lt;p&gt;The &lt;code&gt;wantsDefaultContentAppearance&lt;/code&gt; method determines whether the default inset shadows and rounded corners are displayed in the popover. By returning &lt;code&gt;NO&lt;/code&gt;, the popover background view will no longer show the default shadows and rounded corners, allowing you to implement your own. Add the following code to override the method.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;+ (BOOL)wantsDefaultContentAppearance
{
    return NO;
}&lt;/pre&gt;&lt;p&gt;Build and run the project and you will be able to see the difference.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;9.&lt;/span&gt; Adding the Arrow&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;We’ll need to create and manage the arrow ourselves; let’s declare a property for an image view that will display the arrow. Add the following code to the class extension.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;@property (nonatomic, strong) UIImageView *arrowImageView; &lt;/pre&gt;&lt;p&gt;Now we can instantiate the image view. Replace the code inside the &lt;code&gt;if&lt;/code&gt; statement in &lt;code&gt;initWithFrame:&lt;/code&gt; with the following code.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;self.backgroundColor = [UIColor clearColor];
UIImageView *arrowImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
self.arrowImageView = arrowImageView;
[self addSubview:self.arrowImageView];&lt;/pre&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;Let’s change the border inset by updating the the &lt;code&gt;kBorderInset&lt;/code&gt; defined in PopoverBackgroundView.m with the following code.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;#define kBorderInset 0.0f&lt;/pre&gt;&lt;h3&gt;Step 3&lt;/h3&gt;&lt;p&gt;In order to draw the arrow, we’ll need to declare a method to perform the drawing. Add the following method declaration in the class extension in &lt;em&gt;PopoverBackgroundView.m&lt;/em&gt;.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;- (UIImage *)drawArrowImage:(CGSize)size;&lt;/pre&gt;&lt;h3&gt;Step 4&lt;/h3&gt;&lt;p&gt;Now add the method definition under &lt;code&gt;@implementation&lt;/code&gt;.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;- (UIImage *)drawArrowImage:(CGSize)size
{
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [[UIColor clearColor] setFill];
    CGContextFillRect(ctx, CGRectMake(0.0f, 0.0f, size.width, size.height));
    CGMutablePathRef arrowPath = CGPathCreateMutable();
    CGPathMoveToPoint(arrowPath, NULL, (size.width/2.0f), 0.0f);
    CGPathAddLineToPoint(arrowPath, NULL, size.width, size.height);
    CGPathAddLineToPoint(arrowPath, NULL, 0.0f, size.height);
    CGPathCloseSubpath(arrowPath);
    CGContextAddPath(ctx, arrowPath);
    CGPathRelease(arrowPath);
    UIColor *fillColor = [UIColor yellowColor];
    CGContextSetFillColorWithColor(ctx, fillColor.CGColor);
    CGContextDrawPath(ctx, kCGPathFill);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}&lt;/pre&gt;&lt;p&gt;Instead of using an imported image, the code above programmatically creates the arrow.&lt;/p&gt;&lt;h3&gt;Step 5&lt;/h3&gt;&lt;p&gt;Each time the bounds of the popover background view subclass changes, the frame of the arrow needs to be recalculated. We can accomplish this by overriding &lt;code&gt;layoutSubviews&lt;/code&gt;. Add the following code to &lt;em&gt;PopoverBackgroundView.m&lt;/em&gt;.&lt;/p&gt;&lt;pre class="brush: objc; title: ;"&gt;- (void)layoutSubviews
{
    [super layoutSubviews];
	CGSize arrowSize = CGSizeMake([[self class] arrowBase], [[self class] arrowHeight]);
    self.arrowImageView.image = [self drawArrowImage:arrowSize];
    self.arrowImageView.frame = CGRectMake(((self.bounds.size.width - arrowSize.width) kBorderInset), 0.0f, arrowSize.width, arrowSize.height);
}&lt;/pre&gt;&lt;p&gt;The frame of the arrow’s image view and the arrow’s image are calculated based on the bounds of the popover background view, the border’s insets, and the arrow&amp;#8217;s base and height.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;10.&lt;/span&gt; Testing the Popover&lt;/h2&gt;&lt;p&gt;Build and run your project to see the customized popover. Even though the border inset is set to zero, the arrow is adjusted to line up with the inside edge of the right inset. By subtracting the border inset when determining the x coordinate for the image view frame for the arrow, we are able to align the image view appropriately.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;This tutorial is designed to get you up and running with customizing a popover. There are many directions you can take the project from here; for example, adding another image view to display a custom border image. To do this, you would follow a similar pattern like we did when we created the image view for the arrow. Furthermore, you may want to customize the popover based on the arrow’s direction. In layoutSubviews, a series of if statements could help you test for the arrow’s direction so you could adjust the arrow accordingly. Leave a comment or question below if you have a specific direction you’d like to go with your customizations.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/h6IW_c1OyYM" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-customizing-popovers/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-customizing-popovers/</feedburner:origLink></item> <item><title>Build an AudioPlayer with PhoneGap: Application Tuning</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/SvPPSVbeEqA/</link> <comments>http://mobile.tutsplus.com/tutorials/phonegap/build-an-audioplayer-with-phonegap-application-tuning/#comments</comments> <pubDate>Wed, 08 May 2013 15:44:00 +0000</pubDate> <dc:creator>Aurelio De Rosa</dc:creator> <category><![CDATA[PhoneGap]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=16364</guid> <description>&lt;p&gt;This is the third and final part of the series about &lt;em&gt;Audero Audio Player&lt;/em&gt;. In this article, I will go over the remaining files so that you can finish the project and play around with it.&lt;br
/&gt;&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;Series Overview&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/?p=16347"&gt;Application Setup&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/?p=16358"&gt;Application Logic&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/?p=16364"&gt;Application Tuning&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr
/&gt;&lt;h2&gt;Style Tuning&lt;/h2&gt;&lt;p&gt;jQuery Mobile does a lot of the work for you by enhancing pages&amp;#8217; elements for devices with smaller screens. However, there are some that I don&amp;#8217;t care for, and we&amp;#8217;ll adjust these in our &lt;code&gt;style.css&lt;/code&gt; file. This is also used to style the player&amp;#8217;s markup.&lt;/p&gt;&lt;p&gt;By default, even if you don&amp;#8217;t have buttons within the header or the footer of a page, the framework still reserves some space on both side of the elements and truncates long titles. This behavior is applied to other elements as well. We can change it simply by applying the rule &lt;code&gt;white-space: normal !important;&lt;/code&gt; to our targeted elements as &lt;code&gt;.ui-title&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The source of the stylesheet is shown here:&lt;/p&gt;&lt;pre class="brush: css; title: ;"&gt;
.ui-header .ui-title,
.ui-footer .ui-title,
.ui-btn-inner *
{
  white-space: normal !important;
}
.photo
{
  text-align: center;
}
#player-play,
#player-stop
{
  display: inline-block;
  width: 48px;
  height: 48px;
}
#player-play
{
  background-image: url('../images/play.png');
}
#player-stop
{
  background-image: url('../images/stop.png');
}
#time-slider
{
  display: none;
}
div.ui-slider-track
{
  margin: 0;
  width: 100%;
}
&lt;/pre&gt;&lt;h3&gt;jQuery Mobile Custom Configuration&lt;/h3&gt;&lt;p&gt;jQuery Mobile has a default configuration that should be good enough for most projects with simple requirements. However, there will be times when you will want to modify or take control of some default behavior. You can achieve this by writing a configuration file. The file &lt;code&gt;jquery.mobile.config.js&lt;/code&gt; is exactly where we&amp;#8217;ll have the configuration. Please note that you must include the configuration file &lt;strong&gt;before&lt;/strong&gt; the jQuery Mobile files. When jQuery Mobile starts, it fires the &lt;code&gt;mobileinit&lt;/code&gt; event, which is the one you must bind to override the default settings.&lt;/p&gt;&lt;p&gt;We&amp;#8217;ll make the change by assigning values to the properties of the &lt;code&gt;$.mobile&lt;/code&gt; object. I won&amp;#8217;t change a lot of properties. I&amp;#8217;ll instead change the option to have the text shown on the page loader widget, and the theme.&lt;/p&gt;&lt;p&gt;The full source of the file is listed below:&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
$(document).on(
   'mobileinit',
   function()
   {
      // Page Loader Widget
      $.mobile.loader.prototype.options.text = 'Loading...';
      $.mobile.loader.prototype.options.textVisible = true;
      // Theme
      $.mobile.page.prototype.options.theme  = 'a';
      $.mobile.page.prototype.options.headerTheme = 'a';
      $.mobile.page.prototype.options.contentTheme = 'a';
      $.mobile.page.prototype.options.footerTheme = 'a';
      $.mobile.page.prototype.options.backBtnTheme = 'a';
   }
);
&lt;/pre&gt;&lt;h3&gt;Build Configuration&lt;/h3&gt;&lt;p&gt;The &lt;a
href="http://build.phonegap.com"&gt;Adobe PhoneGap Build&lt;/a&gt; service gives you the ability to specify the metadata of an application, like author and description, by using a configuration file. This file is called &lt;code&gt;config.xml&lt;/code&gt;. Explaining the format in depth is outside the scope of this series, but I&amp;#8217;ll give you a brief overview. If you want to read more on this topic, take a look at the &lt;a
href="https://build.phonegap.com/docs/config-xml"&gt;official documentation page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;config.xml&lt;/code&gt; file follows the &lt;a
href="http://www.w3.org/TR/widgets/"&gt;W3C widget specification&lt;/a&gt; and must stay inside the app&amp;#8217;s root, at the same level of the &lt;code&gt;index.html&lt;/code&gt; file. Its first line is the XML declaration, and the root of the document is a &lt;code&gt;&amp;lt;widget&amp;gt;&lt;/code&gt; tag that has several possible attributes. The most important ones are &lt;code&gt;id&lt;/code&gt; (the unique identifier for your project), and &lt;code&gt;version&lt;/code&gt; (which specifies the version of the application). Inside the &lt;code&gt;&amp;lt;widget&amp;gt;&lt;/code&gt; tag, you can write the metadata of your application. In our file we&amp;#8217;ll use a lot of them, but the most important are the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;name&lt;/strong&gt; (required): The name of the application. It doesn&amp;#8217;t have to be unique.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;description&lt;/strong&gt; (required): The description of the application. It&amp;#8217;s particularly important because it will be shown in the app&amp;#8217;s marketplace listing.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;icon&lt;/strong&gt; (optional): The icon to display on the devices that will install your app. If you do not specify it, the Cordova logo will be used.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;splash&lt;/strong&gt; (optional): This tag sets the splash screen of the application, which is the image shown during loading.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;feature&lt;/strong&gt; (optional): Specifies the features you want to use. For example, Android, before installing any app, shows the user the permissions it requires and, if the user agrees, it goes on.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;preference&lt;/strong&gt; (optional): A set of preferences you want to apply to your app. It&amp;#8217;s a closed tag and you can have zero or more &lt;code&gt;&amp;lt;preference&amp;gt;&lt;/code&gt; tags inside the file. It has two attributes, and both are required: &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;. There are a lot of preferences that you can define, but the most important one in my opinion is specifying the Cordova version used.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The &lt;code&gt;&amp;lt;access&amp;gt;&lt;/code&gt; tag is also very important because, to cite &lt;a
href="https://build.phonegap.com/docs/config-xml"&gt;the documentation&lt;/a&gt;, it &lt;q
cite="https://build.phonegap.com/docs/config-xml"&gt;provides your app with access to resources on other domains &amp;#8211; in particular, it allows your app to load pages from external domains that can take over your entire webview.&lt;/q&gt; Recalling what we discussed in the section &lt;strong&gt;Managing External Links&lt;/strong&gt; from the previous post, to open the external links in the Cordova WebView, we must add them to the app whitelist. Since our application won&amp;#8217;t retrieve links from external and unsafe sources, we can shorten the process to allow for any external resource using the &lt;code&gt;*&lt;/code&gt; special character. For example:&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;access origin="*" /&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;I&amp;#8217;ve pointed out the key points of the format, and now you can understand the source of the configuration file. The complete file is below:&lt;/p&gt;&lt;pre class="brush: xml; title: ;"&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;widget xmlns     = &amp;quot;http://www.w3.org/ns/widgets&amp;quot;
        xmlns:gap = &amp;quot;http://phonegap.com/ns/1.0&amp;quot;
        id        = &amp;quot;com.audero.free.player.auderoaudioplayer&amp;quot;
        version   = &amp;quot;1.0.0&amp;quot;&amp;gt;
   &amp;lt;name&amp;gt;Audero Audio Player&amp;lt;/name&amp;gt;
   &amp;lt;description&amp;gt;Audero Audio Player is a basic audio player that collects the audio files and then allows the user to listen to them. This app also enables you to update the list at any time to include other files that may have been downloaded after running the operation for the first time. You can also remove any unwanted audio from the list by clicking an icon on the right side of the song's name. The sound list is ordered alphabetically with letter dividers to organize and group the list items, and has a search box to filter the files.&amp;lt;/description&amp;gt;
   &amp;lt;author href=&amp;quot;http://www.audero.it&amp;quot; email=&amp;quot;aurelioderosa@gmail.com&amp;quot;&amp;gt;Aurelio De Rosa&amp;lt;/author&amp;gt;
   &amp;lt;feature name=&amp;quot;http://api.phonegap.com/1.0/network&amp;quot;/&amp;gt;
   &amp;lt;feature name=&amp;quot;http://api.phonegap.com/1.0/media&amp;quot;/&amp;gt;
   &amp;lt;feature name=&amp;quot;http://api.phonegap.com/1.0/file&amp;quot;/&amp;gt;
   &amp;lt;feature name=&amp;quot;http://api.phonegap.com/1.0/notification&amp;quot;/&amp;gt;
   &amp;lt;preference name=&amp;quot;phonegap-version&amp;quot; value=&amp;quot;2.3.0&amp;quot; /&amp;gt;
   &amp;lt;preference name=&amp;quot;target-device&amp;quot; value=&amp;quot;universal&amp;quot; /&amp;gt;
   &amp;lt;access origin=&amp;quot;*&amp;quot; /&amp;gt;
   &amp;lt;!-- Icons --&amp;gt;
   &amp;lt;icon src=&amp;quot;icon.png&amp;quot; width=&amp;quot;64&amp;quot; height=&amp;quot;64&amp;quot; gap:role=&amp;quot;default&amp;quot; /&amp;gt;
   &amp;lt;icon src=&amp;quot;images/icon-72x72.png&amp;quot; width=&amp;quot;72&amp;quot; height=&amp;quot;72&amp;quot; gap:platform=&amp;quot;android&amp;quot; gap:density=&amp;quot;hdpi&amp;quot; /&amp;gt;
   &amp;lt;icon src=&amp;quot;images/icon-96x96.png&amp;quot; width=&amp;quot;96&amp;quot; height=&amp;quot;96&amp;quot; gap:platform=&amp;quot;android&amp;quot; gap:density=&amp;quot;xhdpi&amp;quot; /&amp;gt;
   &amp;lt;icon src=&amp;quot;images/icon-72x72.png&amp;quot; width=&amp;quot;72&amp;quot; height=&amp;quot;72&amp;quot; gap:platform=&amp;quot;ios&amp;quot; /&amp;gt;
   &amp;lt;!-- Splash Screens --&amp;gt;
   &amp;lt;gap:splash src=&amp;quot;splash.png&amp;quot; /&amp;gt;
   &amp;lt;gap:splash src=&amp;quot;images/splash-160x220.png&amp;quot; gap:platform=&amp;quot;android&amp;quot; gap:density=&amp;quot;ldpi&amp;quot; /&amp;gt;
   &amp;lt;gap:splash src=&amp;quot;splash.png&amp;quot; gap:platform=&amp;quot;android&amp;quot; gap:density=&amp;quot;mdpi&amp;quot; /&amp;gt;
   &amp;lt;gap:splash src=&amp;quot;images/splash-450x650.png&amp;quot; gap:platform=&amp;quot;android&amp;quot; gap:density=&amp;quot;hdpi&amp;quot; /&amp;gt;
&amp;lt;/widget&amp;gt;
&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;Running the Application&lt;/h2&gt;&lt;p&gt;In the last section, all the business logic, HTML and CSS files for the application were built, so now it&amp;#8217;s time to set the entry functions for the application and play. The targeted function will be the &lt;code&gt;initApplication()&lt;/code&gt; method of the &lt;code&gt;Application&lt;/code&gt; class. It will run once Cordova is fully loaded, ensuring that you can safely call the Cordova APIs. To do this,  we&amp;#8217;ll set &lt;code&gt;initApplication()&lt;/code&gt; as a callback function for the &lt;code&gt;deviceready&lt;/code&gt; event by adding the following code to the &lt;code&gt;index.html&lt;/code&gt; file. You can see this by looking at the next snippet:&lt;/p&gt;&lt;pre class="brush: xml; title: ;"&gt;
&amp;lt;script&amp;gt;
  $(document).on('pagebeforecreate orientationchange', Application.updateIcons);
  $(document).one('deviceready', Application.initApplication);
&amp;lt;/script&amp;gt;
&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;Possible Improvements&lt;/h2&gt;&lt;p&gt;You are now at the end of the project. That being said, every project has room for improvements and new feature, so before I conclude the series, I would like to suggest some of these to you.&lt;/p&gt;&lt;p&gt;The first feature that you can add is the internationalization (i18n) of the application. Our player doesn&amp;#8217;t have much text, so translating it into other languages should be very easy. To translate the application, you can use the &lt;a
href="http://docs.phonegap.com/en/2.3.0/cordova_globalization_globalization.md.html#Globalization" target="_blank"&gt;Globalization API&lt;/a&gt;, an API added to the core starting from version 2.2.0. In addition, a specific jQuery library like &lt;a
href="http://code.google.com/p/jquery-i18n-properties/"&gt;jquery-i18n-properties&lt;/a&gt; or &lt;a
href="http://recursive-design.com/projects/jquery-i18n/"&gt;jQuery-i18n&lt;/a&gt; would surely be useful for this feature.&lt;/p&gt;&lt;p&gt;Other minor suggestions are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Allow the user to create playlist.&lt;/li&gt;&lt;li&gt;Create a &amp;#8220;Play All&amp;#8221; button to play all the songs in the list.&lt;/li&gt;&lt;li&gt;Create a ratings system for the audio so that the user can filter and order songs by rating.&lt;/li&gt;&lt;li&gt;Add a &amp;#8220;Repeat&amp;#8221; button so that the user can continue listening to the current song.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These suggestions are just some of the potential improvements you can make to the &lt;em&gt;Audero Audio Player&lt;/em&gt;. Using the information from this tutorial and your own skills, you can do much, much more.&lt;/p&gt;&lt;hr
/&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;p&gt;As you&amp;#8217;ve seen throughout this series, you can build powerful and useful apps using web technologies and popular frameworks. Now it&amp;#8217;s your turn to play around with this project. Try starting your own project to test what you learned in this series!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/SvPPSVbeEqA" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/tutorials/phonegap/build-an-audioplayer-with-phonegap-application-tuning/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/tutorials/phonegap/build-an-audioplayer-with-phonegap-application-tuning/</feedburner:origLink></item> <item><title>Tuts+ Premium Cash Back Offer: 3 Days to Go</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/eBXENG6WVdU/</link> <comments>http://mobile.tutsplus.com/articles/news/try-tuts-premium-get-cash-back/#comments</comments> <pubDate>Tue, 07 May 2013 16:30:36 +0000</pubDate> <dc:creator>Joel Bankhead</dc:creator> <category><![CDATA[News]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=16930</guid> <description>&lt;p&gt;&lt;strong&gt;This offer ends soon! Act now and don’t miss out on cash back when trying a monthly Tuts+ Premium subscription.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;At $19 a month, Tuts+ Premium is fantastic value. But it&amp;#8217;s even better when we hand your first $19 right back to you!&lt;/p&gt;&lt;p&gt;For a limited time we&amp;#8217;re offering $19 cash back to new Tuts+ Premium monthly subscribers when signing up via PayPal. If you’ve been thinking about checking out our extensive library of courses, tutorials, eBooks and guides there’s never been a better time to join up and dive in.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;This offer ends at noon on the 20th of May AEST, so act fast.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a
href="https://tutsplus.com/paypal-cash-back-offer/?utm_source=mobiletutsend&amp;#038;utm_medium=post&amp;#038;utm_campaign=paypal_cashback&amp;#038;wt.mc_id=paypal"&gt;Become a Tuts+ Premium Member and take your creative &amp;#038; technical skills to a new level.&lt;/a&gt;&lt;br
/&gt;&lt;/p&gt;&lt;hr
/&gt; What can you learn on Tuts+ Premium? Glad you asked! Currently, more than 15,000 members are sharpening their skills in a wide range of areas including web design, web development, Photoshop, vectors, video effects, and many more.&lt;/p&gt;&lt;p&gt;With Tuts+ Premium you learn from expert instructors in every field, such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Designer Justin Maller (Nike, Verizon, DC Shoe Co.)&lt;/li&gt;&lt;li&gt;Illustrator Russell Tate (McDonald&amp;#8217;s, Coca-Cola)&lt;/li&gt;&lt;li&gt;Developer Burak Guzel (Software Engineer at Facebook)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Join now and get instant access to your very own library of courses, tutorials, and eBooks, available whenever you need them. Become part of a community of over 15,000 members and start getting better at the skills you care about. Our dedicated team adds new content weekly, so there&amp;#8217;s always something fresh to sink your teeth into.&lt;/p&gt;&lt;p&gt;&lt;a
href="https://tutsplus.com/paypal-cash-back-offer/?utm_source=mobiletutsend&amp;#038;utm_medium=post&amp;#038;utm_campaign=paypal_cashback&amp;#038;wt.mc_id=paypal"&gt;Join Tuts+ Premium&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/eBXENG6WVdU" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/articles/news/try-tuts-premium-get-cash-back/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/articles/news/try-tuts-premium-get-cash-back/</feedburner:origLink></item> <item><title>How To Submit an iOS App to the App Store</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/Kn8UGqXgb3M/</link> <comments>http://mobile.tutsplus.com/tutorials/iphone/how-to-submit-an-ios-app-to-the-app-store/#comments</comments> <pubDate>Mon, 06 May 2013 17:09:11 +0000</pubDate> <dc:creator>Bart Jacobs</dc:creator> <category><![CDATA[iOS SDK]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=16812</guid> <description>&lt;p&gt;You have worked weeks or months on your first iOS application and you are ready to submit your masterpiece to Apple&amp;#8217;s App Store. How do you do this? Is your application ready for submission? I am sure that some of these questions have entered your mind at one point or another. Is submitting an application as simple as sending Apple your application&amp;#8217;s binary? Not quite. With this tutorial, I will provide you with a detailed map to get your application submitted to Apple&amp;#8217;s App Store.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;Introduction&lt;/h2&gt;&lt;p&gt;Even though the App Store review process is a black box for the most part, that doesn&amp;#8217;t mean that you can&amp;#8217;t prepare yourself and your application for Apple&amp;#8217;s review process. Apple provides guidelines to help you stay within the  sometimes invisible boundaries of what is and isn&amp;#8217;t allowed in the App Store.&lt;/p&gt;&lt;p&gt;The first time you submit an application to the App Store is exciting and nerve-racking at the same time. Even for experienced iOS developers, submitting an application to the App Store is often a stressful undertaking because it is something that most developers don&amp;#8217;t do on a daily basis.&lt;/p&gt;&lt;p&gt;Throughout this article, I am assuming that you are a registered iOS developer which means that you are enrolled in Apple&amp;#8217;s iOS Developer Program and are allowed to submit applications for publication in the App Store. To submit an iOS application to the App Store, you need to be a registered iOS developer. Red flag? Don&amp;#8217;t worry. You can enroll in Apple&amp;#8217;s iOS Developer Program by visiting &lt;a
href="https://developer.apple.com/programs/ios/"&gt;this link&lt;/a&gt; and clicking the &lt;strong&gt;Enroll Now&lt;/strong&gt; button.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-1.png" alt="How To Submit an iOS App to the App Store - Enrolling in Apple's iOS Developer Program" /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 1: Enrolling in Apple&amp;#8217;s iOS Developer Program&lt;/figcaption&gt; &lt;/figure&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;1.&lt;/span&gt; Is your application ready?&lt;/h2&gt;&lt;h3&gt;Step 1: Testing&lt;/h3&gt;&lt;p&gt;An application isn&amp;#8217;t necessarily ready when you&amp;#8217;ve written the last line of code or implemented the final feature of the application&amp;#8217;s specification. Have you tested your application on one or more physical devices? Have you profiled your application for memory leaks and performance issues? Does your application crash from time to time? The family of iOS devices has grown substantially over the past years and it is important to test your application on as many iOS devices as you can lay your hands on. Common issues include not optimizing an application for the iPhone 5&amp;#8242;s 4&amp;#8243; screen or the iPad Mini&amp;#8217;s 7.9&amp;#8243; screen.&lt;/p&gt;&lt;p&gt;The iOS Simulator is a great tool, but it runs on your Mac, which has more memory and processing power than the phone in your pocket. I can assure you that the differences in performance between an old(er) iPhone 3GS and an iPhone 5 are like night and day. As an iOS developer, you should never get rid of an old iOS device as long as you build or maintain applications that can run on any of these older devices.&lt;/p&gt;&lt;p&gt;Apple&amp;#8217;s Review Process isn&amp;#8217;t airtight, but it is very capable of identifying problems that might affect your application&amp;#8217;s user experience. If your application crashes from time to time or it becomes slow after ten minutes of use, then you have some work to do before submitting it to the App Store. Even if Apple&amp;#8217;s review team doesn&amp;#8217;t spot the problem, your users will. If the people using your application are not pleased, they will leave bad reviews on the App Store, which may harm sales or inhibit downloads.&lt;/p&gt;&lt;h3&gt;Step 2: Rules and Guidelines&lt;/h3&gt;&lt;p&gt;As I mentioned earlier, Apple provides developers with a number of documents that are a great help during the creation and development process of your application. The documents that you should be aware of are the &lt;a
href="https://developer.apple.com/library/ios/#documentation/userexperience/conceptual/mobilehig/Introduction/Introduction.html"&gt;iOS Human Interface Guidelines&lt;/a&gt; and the &lt;a
href="https://developer.apple.com/appstore/resources/approval/guidelines.html"&gt;App Store Review Guidelines&lt;/a&gt;. Despite the availability of these documents, it seems that few developers take the time to browse them, let alone read them. It shouldn&amp;#8217;t be a surprise that some applications are therefore rejected even though the reason for the rejection is clearly stated in these documents.&lt;/p&gt;&lt;p&gt;Even if you don&amp;#8217;t intend to read the iOS Human Interface Guidelines or the App Store Review Guidelines, it is important to know about some of the rules that they talk about. Take a look at the short list below to get an idea of what your application should and shouldn&amp;#8217;t do.&lt;/p&gt;&lt;p&gt;Your application &amp;#8230;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;doesn&amp;#8217;t crash.&lt;/li&gt;&lt;li&gt;shouldn&amp;#8217;t use private API&amp;#8217;s.&lt;/li&gt;&lt;li&gt;shouldn&amp;#8217;t replicate the functionality of native applications.&lt;/li&gt;&lt;li&gt;should use In App Purchase for in-app (financial) transactions.&lt;/li&gt;&lt;li&gt;shouldn&amp;#8217;t use the camera or microphone without the user&amp;#8217;s knowledge.&lt;/li&gt;&lt;li&gt;only uses artwork that you have the copyright of or you have permission to use.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Keep in mind that this is a tiny subset of the guidelines included in the aforementioned documents. The majority of the rules and guidelines are trivial, but some are not and you might even violate some of them inadvertently. Let me give you an example. Before Apple started using its own maps, the MapKit framework used Google&amp;#8217;s maps. This was clear to the user because of the small Google logo in the bottom left corner of each map. However, if some part of your application&amp;#8217;s user interface covered or obscured Google&amp;#8217;s logo, your application would get rejected. This rule seems trivial, but it is a rule that is easily violated if you&amp;#8217;re not careful. Even automated tests won&amp;#8217;t cover you in this case.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;2.&lt;/span&gt; Prerequisites&lt;/h2&gt;&lt;p&gt;Before you can even start thinking about submitting your application to the App Store, you need to make sure that you have an App ID, a valid distribution certificate, and a valid provisioning profile. Let me show you what this entails.&lt;/p&gt;&lt;h3&gt;Step 1: App ID&lt;/h3&gt;&lt;p&gt;Every application needs an App ID or application identifier. There are two types of application identifiers, (1) an &lt;strong&gt;explicit App ID&lt;/strong&gt; and (2) a &lt;strong&gt;wildcard App ID&lt;/strong&gt;. A wildcard App ID can be used for building and installing multiple applications. Despite the convenience of a wildcard App ID, an explicit App ID is &lt;strong&gt;required&lt;/strong&gt; if your application uses iCloud or makes use of other iOS features, such as Game Center, Apple Push Notifications, or In App Purchase.&lt;/p&gt;&lt;p&gt;If you&amp;#8217;re not sure what App ID best fits your project, then I recommend reading &lt;a
href="http://developer.apple.com/library/ios/#qa/qa1713/_index.html"&gt;Technical Note QA1713&lt;/a&gt; for more information about this topic.&lt;/p&gt;&lt;h3&gt;Step 2: Distribution Certificate&lt;/h3&gt;&lt;p&gt;To submit an application to the App Store, you need to create an iOS provisioning profile for distribution. To create such a provisioning profile, you first need to create a distribution certificate. The process for creating a distribution certificate is very similar to creating a development certificate. If you have tested your application on a physical device, then you are probably already familiar with the creation of a development certificate.&lt;/p&gt;&lt;p&gt;If you need to refresh your memory, I suggest reading &lt;a
href="http://developer.apple.com/library/ios/#documentation/IDEs/Conceptual/AppDistributionGuide/CodeSigningYourApps/CodeSigningYourApps.html"&gt;Apple&amp;#8217;s detailed guide&lt;/a&gt; about signing certificates and provisioning profiles. The process is not difficult once you understand how the various pieces of the puzzle fit together.&lt;/p&gt;&lt;h3&gt;Step 3: Provisioning Profile&lt;/h3&gt;&lt;p&gt;Once you&amp;#8217;ve created an App ID and a distribution certificate, you can create an iOS provisioning profile for distributing your application through the App Store. Keep in mind that you cannot use the same provisioning profile that you use for ad hoc distribution. You need to create a separate provisioning profile for App Store distribution. If you use a wildcard App ID for your project, then you can use the same provisioning profile for multiple applications.&lt;/p&gt;&lt;h3&gt;Step 4: Build Settings&lt;/h3&gt;&lt;p&gt;With the App ID, distribution certificate, and provisioning profile in place, it is time to configure your target&amp;#8217;s build settings in Xcode. This means selecting the target from the list of targets in Xcode&amp;#8217;s &lt;strong&gt;Project Navigator&lt;/strong&gt;, opening the &lt;strong&gt;Build Settings&lt;/strong&gt; tab at the top, and updating the settings in the &lt;strong&gt;Code Signing&lt;/strong&gt; section to match the distribution provisioning profile you created earlier. Newly added provisioning profiles are sometimes not immediately visible in the &lt;strong&gt;Code Signing&lt;/strong&gt; section of the build settings. Quitting and relaunching Xcode remedies this issue.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-2.png" alt="How To Submit an iOS App to the App Store - Configuring the Target's Build Settings" /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 2: Configuring the Target&amp;#8217;s Build Settings&lt;/figcaption&gt; &lt;/figure&gt;&lt;p&gt;Even though the code signing process is fairly simple once you understand it, it is something that trips up a lot of developers. I don&amp;#8217;t know a single Cocoa developer who hasn&amp;#8217;t run into code signing issues at some point in their career. Once you&amp;#8217;ve taken this hurdle, the rest of the submission process is fairly easy.&lt;/p&gt;&lt;h3&gt;Step 5: Deployment Target&lt;/h3&gt;&lt;p&gt;It is useful to write a few words about your application&amp;#8217;s deployment target. Each target in an Xcode project, has a deployment target, which indicates the minimum version of the operating system that the application can run on. It is up to you to set the deployment target, but keep in mind that modifying the deployment target is not something you can do without consequences once your application is in the App Store. If you increase the deployment target for an update of your application, then users who already purchased your application but don&amp;#8217;t meet the new deployment target, cannot run the update. It gets really problematic when a user downloads an update through iTunes (not the device), replacing the previous version on their computer, and then discovers that the new update doesn&amp;#8217;t run on their device.&lt;/p&gt;&lt;p&gt;I have two very simple tips with regards to your application&amp;#8217;s deployment target. (1) Be very careful when you decide to increase the deployment target of an existing application. Mention this in the application&amp;#8217;s release notes of the updates that precede the change and again in the update that uses the new deployment target. If your warn your customers well in advance, you have done all you can to prevent potential problems. (2) For new applications, I almost always set the deployment target to the last major release, iOS 6 at the time of writing. Because of the incredible adoption rate of new iOS releases, there is no harm in doing this. Some people think that they miss out on a large chunk of the market, but that is not true. Take the release of iOS 6 as an example. One month after the release of iOS 6, &lt;a
href="http://gigaom.com/2012/10/22/1-month-later-ios-6-adoption-hits-61-percent-in-u-s-canada/"&gt;more than 60% of iOS devices had upgraded to the new version of iOS&lt;/a&gt;. Unfortunately, &lt;a
href="http://thenextweb.com/google/2012/11/02/android-4-1-jelly-bean-hits-2-7-adoption-ics-at-25-8-gingerbread-still-on-over-half-of-devices/"&gt;the same isn&amp;#8217;t true for Android&lt;/a&gt;.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;3.&lt;/span&gt; Assets&lt;/h2&gt;&lt;h3&gt;Step 1: Icons&lt;/h3&gt;&lt;p&gt;You probably know that an application icon is a vital component of every iOS application, but you need to make sure that your application ships with the correct sizes of the artwork. Take a look at the list below for an overview.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;iTunes Artwork: 1024px x 1024px (required)&lt;/li&gt;&lt;li&gt;iPad/iPad Mini: 72px x 72px &lt;strong&gt;and&lt;/strong&gt; 114px x 114px (required)&lt;/li&gt;&lt;li&gt;iPhone/iPod Touch: 57px x 57px &lt;strong&gt;and&lt;/strong&gt; 114px x 114px (required)&lt;/li&gt;&lt;li&gt;Search Icon: 29px x 29px &lt;strong&gt;and&lt;/strong&gt; 58px x 58px (optional)&lt;/li&gt;&lt;li&gt;Settings Application: 50px x 50px &lt;strong&gt;and&lt;/strong&gt; 100px x 100px (optional)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It goes without saying that you don&amp;#8217;t need to include an application icon for the iPad/iPad Mini device family if your application only targets the iPhone/iPod Touch device family, and vice versa.&lt;/p&gt;&lt;h3&gt;Step 2: Screenshots&lt;/h3&gt;&lt;p&gt;Each application can have up to five screenshots and you must provide at least one. If you are developing a universal application, then you need to provide separate screenshots for iPhone/iPod Touch and iPad/iPad Mini. In addition, you can optionally include separate screenshots for the 3.5&amp;#8243; and the 4&amp;#8243; screen sizes of the iPhone/iPod Touch. This is quite a bit of work and you want to make sure that the screenshots show your application from its best side. Shiny Development sells a Mac application, &lt;a
href="http://shinydevelopment.com/status-magic/"&gt;Status Magic&lt;/a&gt; that helps you get the status bar in your screenshots right. Status Magic will save you quite a bit of time.&lt;/p&gt;&lt;p&gt;It is important to spend some time thinking about the screenshots. Your application&amp;#8217;s screenshots are often the only thing that a customer can use to decide whether she purchases or downloads your application or not. What a lot of developers don&amp;#8217;t know is that the screenshots don&amp;#8217;t have to be actual screenshots. The hard rule is that the size of each screenshot needs to be that of the screen size of the target device. Many companies are creative with this rule. Take a look at the screenshots of &lt;a
href="https://itunes.apple.com/us/app/wheres-my-water/id449735650"&gt;Where&amp;#8217;s My Water?&lt;/a&gt;, for example. By using this strategy, screenshots can be much more attractive and compelling.&lt;/p&gt;&lt;h3&gt;Step 3: Metadata&lt;/h3&gt;&lt;p&gt;Before you submit your application, it is a good idea to have your application&amp;#8217;s metadata at hand. This includes (1) your application&amp;#8217;s name, (2) the version number, (3) the primary (and an optional secondary) category, (4) a concise description, (5) keywords, and (6) a support URL. If you are submitting an update, then you can also provide information for the &lt;strong&gt;What&amp;#8217;s new in this Version&lt;/strong&gt; section.&lt;/p&gt;&lt;p&gt;Does your application require users to sign in? Then you also need to provide Apple with a test or demo account to make sure that the review team can immediately sign in and use your application without first having to sign up for an account.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;4.&lt;/span&gt; Submission Preparation&lt;/h2&gt;&lt;p&gt;The submission process has become much easier since the release of Xcode 4. You can now validate and submit an application using Xcode, for example. First, however, you need to create your application in iTunes Connect.&lt;/p&gt;&lt;p&gt;Visit &lt;a
href="https://itunesconnect.apple.com"&gt;iTunes Connect&lt;/a&gt;, sign in with your iOS developer account, and click &lt;strong&gt;Manage Your Apps&lt;/strong&gt; on the right. Click the &lt;strong&gt;Add New App&lt;/strong&gt; in the top left, select &lt;strong&gt;iOS App&lt;/strong&gt;, and fill out the form.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-3.png" alt="How To Submit an iOS App to the App Store - Visit iTunes Connect to Get Started" /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 3: Visit iTunes Connect to Get Started&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 1: Basic Information&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;App Name&lt;/strong&gt;, which needs to be unique, is the name of your application as it will appear in the App Store. This can be different than the name that is displayed below your application icon on the home screen, but it is recommended to choose the same name. The &lt;strong&gt;SKU Number&lt;/strong&gt; is a unique string that identifies your application. I usually use the application&amp;#8217;s bundle identifier. The last piece of information is the &lt;strong&gt;Bundle ID&lt;/strong&gt; of your application. This means selecting the (wildcard or explicit) App ID that you created earlier from the drop down menu.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-4.png" alt="How To Submit an iOS App to the App Store - Specifying Name, SKU Number, and Bundle ID" /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 4: Specifying Name, SKU Number, and Bundle ID&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 2: Price and Availability&lt;/h3&gt;&lt;p&gt;In the next step, you specify your application&amp;#8217;s price and availability. Apple works with price tiers so that you don&amp;#8217;t have to specify a price for each country that Apple operates in. You can also specify in which stores your application should &amp;#8211; or shouldn&amp;#8217;t &amp;#8211; be available. The information that you enter in this step can be modified once your application is live in the App Store. In other words, you can change the price and availability of an application without having to submit an update.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-5.png" alt="How To Submit an iOS App to the App Store - Specifying Price and Availability" /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 5: Specifying Price and Availability&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 3: Metadata&lt;/h3&gt;&lt;p&gt;We&amp;#8217;ve already covered the application&amp;#8217;s metadata. The only aspect that I haven&amp;#8217;t talked about yet is your application&amp;#8217;s rating. Based on your application&amp;#8217;s content and functionality, it is given a rating. This rating is not only useful for telling users about your application&amp;#8217;s content and features, the rating is also used by the operating system for the parental controls features.&lt;/p&gt;&lt;p&gt;It is strongly recommended that you don&amp;#8217;t try to outsmart the rating system. Apple is well aware of this and will reject your application if it doesn&amp;#8217;t agree with the rating that you have set.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-6.png" alt="How To Submit an iOS App to the App Store - Entering Your Application's Metadata and Assigning a Rating" /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 6: Entering Your Application&amp;#8217;s Metadata and Assigning a Rating&lt;/figcaption&gt; &lt;/figure&gt;&lt;h3&gt;Step 4: Ready to Upload Binary&lt;/h3&gt;&lt;p&gt;Once your application&amp;#8217;s metadata is submitted, you will be presented with a summary of your application. Under &lt;strong&gt;Versions&lt;/strong&gt;, you should see the version that you submitted a moment ago. Click the &lt;strong&gt;View Details&lt;/strong&gt; button and click the &lt;strong&gt;Ready to Upload Binary&lt;/strong&gt; button in the top right. You are then asked one or more questions regarding your application and, if all went well, you should see a message telling you that you are now ready to upload your application binary. The status of your application has changed to &lt;strong&gt;Waiting for Upload&lt;/strong&gt;.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-7.png" alt="How To Submit an iOS App to the App Store - Your Application's Summary " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 7: Your Application&amp;#8217;s Summary &lt;/figcaption&gt; &lt;/figure&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;5. &lt;/span&gt; Uploading Binary&lt;/h2&gt;&lt;p&gt;To submit your application, you need to create an &lt;strong&gt;archive&lt;/strong&gt; of your application. You can only create an archive by building your application on a &lt;strong&gt;physical device&lt;/strong&gt;. If you select the iOS Simulator in the active scheme, you will notice that the &lt;strong&gt;Archive&lt;/strong&gt; option in Xcode&amp;#8217;s &lt;strong&gt;Product&lt;/strong&gt; menu is grayed out. Connect an iOS device to your Mac, select it in the active scheme, and select &lt;strong&gt;Archive&lt;/strong&gt; from Xcode&amp;#8217;s &lt;strong&gt;Product&lt;/strong&gt; menu.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-8.png" alt="How To Submit an iOS App to the App Store - Archiving Your Application using Xcode " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 8: Archiving Your Application using Xcode &lt;/figcaption&gt; &lt;/figure&gt;&lt;p&gt;If all went well, you should now have an archive and Xcode&amp;#8217;s Organizer should automatically open and show you the archive you just created. Select the archive from the list and click the &lt;strong&gt;Distribute&amp;#8230;&lt;/strong&gt; button on the right. From the options you are presented with, select &lt;strong&gt;Submit to the iOS App Store&lt;/strong&gt;. After entering your iOS developer account credentials and selecting the &lt;strong&gt;Application&lt;/strong&gt; and &lt;strong&gt;Code Signing Identity&lt;/strong&gt;, the application binary is uploaded to Apple&amp;#8217;s servers. During this process, your application is also validated. If an error occurs during the validation, the submission process will fail. The validation process is very useful as it will tell you if there is something wrong with your application binary that would otherwise result in a rejection by the App Store review team.&lt;/p&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-9.png" alt="How To Submit an iOS App to the App Store - Archiving Your Application using Xcode " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 9: Archiving Your Application using Xcode &lt;/figcaption&gt; &lt;/figure&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-10.png" alt="How To Submit an iOS App to the App Store - Submit Your Application to the iOS App Store " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 10: Submit Your Application to the iOS App Store &lt;/figcaption&gt; &lt;/figure&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-11.png" alt="How To Submit an iOS App to the App Store - Enter Your iOS Developer Account Credentials " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 11: Enter Your iOS Developer Account Credentials &lt;/figcaption&gt; &lt;/figure&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-12.png" alt="How To Submit an iOS App to the App Store - Select Application and Code Signing Identity " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 12: Select Application and Code Signing Identity&lt;/figcaption&gt; &lt;/figure&gt; &lt;figure
class="tutorial_image"&gt; &lt;img
src="http://cdn.tutsplus.com/mobile.tutsplus.com/uploads/2013/05/figure-20130504-13.png" alt="How To Submit an iOS App to the App Store - An Error is Shown if Validation Fails " /&gt;&lt;/p&gt; &lt;figcaption&gt;Figure 13: An Error is Shown if Validation Fails&lt;/figcaption&gt; &lt;/figure&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;6. &lt;/span&gt; Waiting&lt;/h2&gt;&lt;p&gt;If the submission process went without problems, your application&amp;#8217;s status will change to &lt;strong&gt;Waiting for Review&lt;/strong&gt;. It takes several days for Apple to review your application and the time it takes, tends to fluctuate over time. To get an idea of the average review times of iOS and Mac applications, I recommend visiting the website of &lt;a
href="http://reviewtimes.shinydevelopment.com"&gt;Shiny Development&lt;/a&gt; (Dave Verwer). This will give you a good indication of how long the review process will take.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;The submission process is quite lengthy for a new application, but submitting an update to the App Store is much less cumbersome. Keep in mind that the submission process is much more involving if your application is localized in various languages as your application&amp;#8217;s metadata needs to be localized as well. However, localizing your application is well worth the effort as it often results in higher sales and positive customer feedback.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/Kn8UGqXgb3M" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/tutorials/iphone/how-to-submit-an-ios-app-to-the-app-store/feed/</wfw:commentRss> <slash:comments>4</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/tutorials/iphone/how-to-submit-an-ios-app-to-the-app-store/</feedburner:origLink></item> <item><title>Wanted: Producer for Tuts+ Premium</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/7F9NW4c6-vI/</link> <comments>http://mobile.tutsplus.com/articles/news/wanted-producer-for-tuts-premium/#comments</comments> <pubDate>Mon, 06 May 2013 15:02:46 +0000</pubDate> <dc:creator>Amanda Hackwith</dc:creator> <category><![CDATA[News]]></category> <category><![CDATA[tutspremium]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=16800</guid> <description>&lt;p&gt;&lt;strong&gt;&lt;a
href="https://tutsplus.com/"&gt;Tuts+ Premium&lt;/a&gt; is hiring for an expert in mobile development.&lt;/strong&gt; We’re looking for a developer who’s passionate and experienced with mobile design and development to produce courses with us! You need to be experienced in mobile development platforms, be proactive and engaged with the industry, and have a passion for teaching. Sound like you? Read on!&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;We’re looking for a new Producer to join the Tuts+ Premium Content team. A Producer is responsible for recruiting and producing courses for Tuts+ Premium members and is a vital part of the content team. They should have professional experience in designing and developing for mobile (apps, websites, etc) and additional experience in screencasting or teaching.&lt;/p&gt;&lt;p&gt;A Producer role requires a commitment of 20 hours a week and ability to perform daily responsibilities, but you’ll have flexibility in scheduling your time. This role is a contract position and is paid monthly.&lt;/p&gt;&lt;p&gt;An ideal Producer on our team will have:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;At least 3+ years professional experience in mobile development or design. This could include successfully developing iOS/Android apps, web apps, or other mobile interfaces. Should love every aspect of mobile development&amp;#8211;from concept and design, to development and deployment, on up to iteration and strategy. Hands-on knowledge of backend development is required.&lt;/li&gt;&lt;li&gt;Experience screencasting or teaching in a on-line environment preferred.&lt;/li&gt;&lt;li&gt;Preferred experience working with WordPress, S3 and other content publishing tools.&lt;/li&gt;&lt;li&gt;Excellent written and verbal communication skills to recruit and work with other talented professionals. Good working relationships in the mobile dev industry.&lt;/li&gt;&lt;li&gt;Experience working with high performance remote teams using a variety of web-based management tools and technologies. (Such as email, Basecamp, Skype and other web-based tools.)&lt;/li&gt;&lt;li&gt;A knack for time management, reliable performance and managing multiple projects at once.&lt;/li&gt;&lt;li&gt;A passion for teaching and strong ideas about how to make Tuts+ Premium even better!&lt;/li&gt;&lt;/ul&gt;&lt;hr
/&gt;&lt;h2&gt;Apply!&lt;/h2&gt;&lt;p&gt;Think you fit the bill? Then we want to hear from you! Send a us a short email to &lt;a
href="mailto:amanda@envato.com"&gt;amanda@envato.com&lt;/a&gt; telling us how you’re the perfect fit for the Tuts+ Premium team. Make sure to include links or samples to back up your claims. A great application will be sure to mention:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Your professional experience in mobile development and design. Links to public apps or sites is highly preferred.&lt;/li&gt;&lt;li&gt;Previous samples of screencasting and teaching work. The more you’ve done, the better!&lt;/li&gt;&lt;li&gt;Your member name if you’re already active with &lt;a
href="https://tutsplus.com/forums/"&gt;the community&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Your available hours and local time zone.&lt;/li&gt;&lt;li&gt;Your top suggestion or idea for Tuts+ Premium courses. If you were brought on as a Producer, what would be the first course you’d want to make?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Make sure to get your email in by &lt;strong&gt;May 14, 2013&lt;/strong&gt;. While every email will be considered we may not be able to respond to every application, but if you sound like a good fit you’ll hear from us soon. Join the team and help us make Tuts+ Premium even better!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/7F9NW4c6-vI" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/articles/news/wanted-producer-for-tuts-premium/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/articles/news/wanted-producer-for-tuts-premium/</feedburner:origLink></item> <item><title>Android SDK: Working with Google Maps –  Displaying Places of Interest</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/SwahBuPjdqg/</link> <comments>http://mobile.tutsplus.com/tutorials/android/android-sdk-working-with-google-maps-displaying-places-of-interest/#comments</comments> <pubDate>Fri, 03 May 2013 11:30:37 +0000</pubDate> <dc:creator>Sue Smith</dc:creator> <category><![CDATA[Android SDK]]></category> <category><![CDATA[android]]></category> <category><![CDATA[Google Maps]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=16145</guid> <description>&lt;p&gt;With Google Maps in your Android apps, you can provide users with localization functions, such as geographical information. Throughout this series we have been building an Android app in which the Google Maps Android API v2 combines with the Google Places API. So far we have displayed a map, in which the user can see their current location, and we have submitted a Google Places query to return data about nearby places of interest. This required setting up API access for both services. In the final part of the series, we will parse the Google Places JSON data and use it to show the user nearby places of interest. We will also make the app update the markers when the user location changes.&lt;/p&gt;&lt;p&gt;This is the last of four parts in a tutorial series on &lt;em&gt;Using Google Maps and Google Places in Android&lt;/em&gt; apps:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/tutorials/android/android-sdk-working-with-google-maps-application-setup/"&gt;Working with Google Maps &amp;#8211; Application Setup&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/tutorials/android/android-sdk-working-with-google-maps-map-setup/"&gt;Working with Google Maps &amp;#8211; Map Setup&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/?p=16054"&gt;Working with Google Maps &amp;#8211; Places Integration&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/?p=16145"&gt;Working with Google Maps &amp;#8211; Displaying Nearby Places&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="App We Are Working Towards" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/sue-smith/final_app.png" /&gt;&lt;/p&gt; &lt;figcaption&gt;This is a snapshot of the final app.&lt;/figcaption&gt; &lt;/figure&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;1.&lt;/span&gt; Process the Place Data&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;You will need to add the following import statements to your Activity class for this tutorial:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
&lt;/pre&gt;&lt;p&gt;In the last tutorial we created an inner &lt;em&gt;AsyncTask&lt;/em&gt; class to handle fetching the data from Google Places in the background. We added the &lt;em&gt;doInBackground&lt;/em&gt; method to request and retrieve the data. Now we can implement the &lt;em&gt;onPostExecute&lt;/em&gt; method to parse the JSON string returned from &lt;em&gt;doInBackground, &lt;/em&gt;inside your &lt;em&gt;AsyncTask&lt;/em&gt; class, after the &lt;em&gt;doInBackground&lt;/em&gt; method:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
protected void onPostExecute(String result) {
	//parse place data returned from Google Places
}
&lt;/pre&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;Back in the second part of this series, we created a Marker object to indicate the user&amp;#8217;s last recorded location on the map. We are also going to use Markers to show the nearby places of interest. We will use an array to store these Markers. At the top of your Activity class declaration, add the following instance variable:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
private Marker[] placeMarkers;
&lt;/pre&gt;&lt;p&gt;By default, the Google Places API returns a maximum of 20 places, so let&amp;#8217;s define this as a constant too:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
private final int MAX_PLACES = 20;
&lt;/pre&gt;&lt;p&gt;When we create the Markers for each place, we will use &lt;em&gt;MarkerOptions&lt;/em&gt; objects to configure the Marker details. Create another array instance variable for these:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
private MarkerOptions[] places;
&lt;/pre&gt;&lt;p&gt;Now let&amp;#8217;s instantiate the array. In your Activity &lt;em&gt;onCreate&lt;/em&gt; method, after the line in which we set the map type, create an array of the maximum required size:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
placeMarkers = new Marker[MAX_PLACES];
&lt;/pre&gt;&lt;p&gt;Now let&amp;#8217;s turn to the &lt;em&gt;onPostExecute&lt;/em&gt; method we created. First, loop through the Marker array, removing any existing Markers. This method will execute multiple times as the user changes location:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
if(placeMarkers!=null){
	for(int pm=0; pm&amp;lt;placeMarkers.length; pm++){
		if(placeMarkers[pm]!=null)
			placeMarkers[pm].remove();
	}
}
&lt;/pre&gt;&lt;p&gt;When the app code first executes, new Markers will be created. However, when the user changes location, these methods will execute again to update the places displayed. For this reason the first thing we must do is remove any existing Markers from the map to prepare for creating a new batch.&lt;/p&gt;&lt;h3&gt;Step 3&lt;/h3&gt;&lt;p&gt;We will be using Java JSON resources to process the retrieved place data. Since these classes throw certain exceptions, we need to build in a level of error handling throughout this section. Start by adding &lt;em&gt;try&lt;/em&gt; and &lt;em&gt;catch&lt;/em&gt; blocks:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
try {
	//parse JSON
}
catch (Exception e) {
	e.printStackTrace();
}
&lt;/pre&gt;&lt;p&gt;Inside the &lt;em&gt;try&lt;/em&gt; block, create a new &lt;em&gt;JSONObject&lt;/em&gt; and pass it to the result JSON string returned from &lt;em&gt;doInBackground&lt;/em&gt;:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
JSONObject resultObject = new JSONObject(result);
&lt;/pre&gt;&lt;p&gt;If you look at the &lt;a
href="https://developers.google.com/places/documentation/search"&gt;Place Search&lt;/a&gt; page on the Google Places API documentation, you can see a sample of what the query actually returns in JSON. You will see that the places are contained within an array named &amp;#8220;results&amp;#8221;. Let&amp;#8217;s first retrieve that array from the returned JSON object:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
JSONArray placesArray = resultObject.getJSONArray(&amp;quot;results&amp;quot;);
&lt;/pre&gt;&lt;p&gt;You should refer to the sample JSON result as we complete each section of this process &amp;#8211; keep the page open in a browser while you complete the remainder of the tutorial. Next let&amp;#8217;s instantiate the &lt;em&gt;MarkerOptions&lt;/em&gt; array we created with the length of the returned &amp;#8220;results&amp;#8221; array:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
places = new MarkerOptions[placesArray.length()];
&lt;/pre&gt;&lt;p&gt;This should give us a &lt;em&gt;MarkerOptions&lt;/em&gt; object for each place returned. Add a loop to iterate through the array of places:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
//loop through places
for (int p=0; p&amp;lt;placesArray.length(); p++) {
	//parse each place
}
&lt;/pre&gt;&lt;h3&gt;Step 4&lt;/h3&gt;&lt;p&gt;Now we can parse the data for each place returned. Inside the &lt;em&gt;for&lt;/em&gt; loop, we will build details to pass to the &lt;em&gt;MarkerOptions&lt;/em&gt; object for the current place. This will include latitude and longitude, place name, type and vicinity, which is an excerpt of the address data for the place. We will retrieve all of this data from the Google Places JSON, passing it to the Marker for the place via its &lt;em&gt;MarkerOptions&lt;/em&gt; object. If any of the values are missing in the returned JSON feed, we will simply not display a Marker for that place, in case of Exceptions. To keep track of this, add a boolean flag:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
boolean missingValue=false;
&lt;/pre&gt;&lt;p&gt;Now add local variables for each aspect of the place we need to retrieve and pass to the Marker:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
LatLng placeLL=null;
String placeName=&amp;quot;&amp;quot;;
String vicinity=&amp;quot;&amp;quot;;
int currIcon = otherIcon;
&lt;/pre&gt;&lt;p&gt;We create and initialize a &lt;em&gt;LatLng&lt;/em&gt; object for the latitude and longitude, strings for the place name and vicinity and initially set the icon to use the default icon drawable we created. Now we need another &lt;em&gt;try&lt;/em&gt; block, so that we can detect whether any values are in fact missing:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
try{
	//attempt to retrieve place data values
}
catch(JSONException jse){
	missingValue=true;
	jse.printStackTrace();
}
&lt;/pre&gt;&lt;p&gt;We set the missing value flag to true for checking later. Inside this &lt;em&gt;try&lt;/em&gt; block, we can now attempt to retrieve the required values from the place data. Start by initializing the boolean flag to false, assuming that there are no missing values until we discover otherwise:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
missingValue=false;
&lt;/pre&gt;&lt;p&gt;Now get the current object from the place array:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
JSONObject placeObject = placesArray.getJSONObject(p);
&lt;/pre&gt;&lt;p&gt;If you look back at the sample Place Search data, you will see that each place section includes a &amp;#8220;geometry&amp;#8221; section which in turn contains a &amp;#8220;location&amp;#8221; section. This is where the latitude and longitude data for the place is, so retrieve it now:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
JSONObject loc = placeObject.getJSONObject(&amp;quot;geometry&amp;quot;).getJSONObject(&amp;quot;location&amp;quot;);
&lt;/pre&gt;&lt;p&gt;Attempt to read the latitude and longitude data from this, referring to the &amp;#8220;lat&amp;#8221; and &amp;#8220;lng&amp;#8221; values in the JSON:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
placeLL = new LatLng(
	Double.valueOf(loc.getString(&amp;quot;lat&amp;quot;)),
	Double.valueOf(loc.getString(&amp;quot;lng&amp;quot;)));
&lt;/pre&gt;&lt;p&gt;Next get the &amp;#8220;types&amp;#8221; array you can see in the JSON sample:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
JSONArray types = placeObject.getJSONArray(&amp;quot;types&amp;quot;);
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; We know this is an array as it appears in the JSON feed surrounded by the &amp;#8220;[" and "]&amp;#8221; characters. We treat any other nested sections as JSON objects rather than arrays.&lt;/p&gt;&lt;p&gt;Loop through the type array:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
for(int t=0; t&amp;lt;types.length(); t++){
	//what type is it
}
&lt;/pre&gt;&lt;p&gt;Get the type string:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
String thisType=types.get(t).toString();
&lt;/pre&gt;&lt;p&gt;We are going to use particular icons for certain place types (food, bar and store) so add a conditional:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
if(thisType.contains(&amp;quot;food&amp;quot;)){
	currIcon = foodIcon;
	break;
}
else if(thisType.contains(&amp;quot;bar&amp;quot;)){
	currIcon = drinkIcon;
	break;
}
else if(thisType.contains(&amp;quot;store&amp;quot;)){
	currIcon = shopIcon;
	break;
}
&lt;/pre&gt;&lt;p&gt;The type list for a place may actually contain more than one of these places, but for convenience we will simply use the first one encountered. If the list of types for a place does not contain any of these, we will leave it displaying the default icon. Remember that we specified these types in the Place Search URL query string last time:&lt;/p&gt;&lt;pre class="brush: plain; title: ;"&gt;
food|bar|store|museum|art_gallery
&lt;/pre&gt;&lt;p&gt;This means that the only place types using the default icon will be museums or art galleries, as these are the only other types we asked for.&lt;/p&gt;&lt;p&gt;After the loop through the type array, retrieve the vicinity data:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
vicinity = placeObject.getString(&amp;quot;vicinity&amp;quot;);
&lt;/pre&gt;&lt;p&gt;Finally, retrieve the place name:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
placeName = placeObject.getString(&amp;quot;name&amp;quot;);
&lt;/pre&gt;&lt;h3&gt;Step 5&lt;/h3&gt;&lt;p&gt;After the &lt;em&gt;catch&lt;/em&gt; block in which you set the &lt;em&gt;missingValue&lt;/em&gt; flag to true, check that value and set the place &lt;em&gt;MarkerOptions&lt;/em&gt; object to null, so that we don&amp;#8217;t attempt to instantiate any Marker objects with missing data:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
if(missingValue)	places[p]=null;
&lt;/pre&gt;&lt;p&gt;Otherwise, we can create a &lt;em&gt;MarkerOptions&lt;/em&gt; object at this position in the array:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
else
	places[p]=new MarkerOptions()
	.position(placeLL)
	.title(placeName)
	.icon(BitmapDescriptorFactory.fromResource(currIcon))
	.snippet(vicinity);
&lt;/pre&gt;&lt;h3&gt;Step 6&lt;/h3&gt;&lt;p&gt;Now, at the end of &lt;em&gt;onPostExecute&lt;/em&gt; after the outer &lt;em&gt;try&lt;/em&gt; and &lt;em&gt;catch&lt;/em&gt; blocks, loop through the array of &lt;em&gt;MarkerOptions&lt;/em&gt;, instantiating a Marker for each, adding it to the map and storing a reference to it in the array we created:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
if(places!=null &amp;amp;&amp;amp; placeMarkers!=null){
	for(int p=0; p&amp;lt;places.length &amp;amp;&amp;amp; p&amp;lt;placeMarkers.length; p++){
		//will be null if a value was missing
		if(places[p]!=null)
			placeMarkers[p]=theMap.addMarker(places[p]);
	}
}
&lt;/pre&gt;&lt;p&gt;Storing a reference to the Marker allows us to easily remove it when the places are updated, as we implemented at the beginning of the &lt;em&gt;onPostExecute&lt;/em&gt; method. Notice that we include two conditional tests each time this loop iterates, in case the Place Search did not return the full 20 places. We also check in case the &lt;em&gt;MarkerOptions&lt;/em&gt; is null, indicating that a value was missing.&lt;/p&gt;&lt;h3&gt;Step 7&lt;/h3&gt;&lt;p&gt;Finally, we can instantiate and execute our &lt;em&gt;AsyncTask&lt;/em&gt; class. In your &lt;em&gt;updatePlaces&lt;/em&gt; method, after the existing code in which we built the search query string, start this background processing to fetch the place data using that string:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
new GetPlaces().execute(placesSearchStr);
&lt;/pre&gt;&lt;p&gt;You can run your app now to see it in action. It should display your last recorded location together with nearby places of interest. The colors you see on the Markers will depend on the places returned. Here is the app displaying a user location in Glasgow city center, UK:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="App in Glasgow" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/sue-smith/app_init_glasgow.png" /&gt;&lt;/figure&gt;&lt;p&gt;Perhaps unsurprisingly a lot of the places listed in Glasgow are bars.&lt;/p&gt;&lt;p&gt;When the user taps a Marker, they will see the place name and snippet info:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="Tapping Marker in Glasgow" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/sue-smith/glasgow_tap.png" /&gt;&lt;/figure&gt;&lt;hr
/&gt;&lt;h2&gt;&lt;span&gt;2.&lt;/span&gt; Update With User Location Changes&lt;/h2&gt;&lt;h3&gt;Step 1&lt;/h3&gt;&lt;p&gt;The app as it stands will execute once when it is launched. Let&amp;#8217;s build in the functionality required to make it update to reflect changes in the user location, refreshing the nearby place Markers at the same time.&lt;/p&gt;&lt;p&gt;Alter the opening line of the Activity class declaration to make it implement the &lt;em&gt;LocationListener&lt;/em&gt; interface so that we can detect changes in the user location:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
public class MyMapActivity extends Activity implements LocationListener {
&lt;/pre&gt;&lt;p&gt;A Location Listener can respond to various changes, each of which uses a dedicated method. Inside the Activity class, implement these methods:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
@Override
public void onLocationChanged(Location location) {
	Log.v(&amp;quot;MyMapActivity&amp;quot;, &amp;quot;location changed&amp;quot;);
	updatePlaces();
}
@Override
public void onProviderDisabled(String provider){
	Log.v(&amp;quot;MyMapActivity&amp;quot;, &amp;quot;provider disabled&amp;quot;);
}
@Override
public void onProviderEnabled(String provider) {
	Log.v(&amp;quot;MyMapActivity&amp;quot;, &amp;quot;provider enabled&amp;quot;);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
	Log.v(&amp;quot;MyMapActivity&amp;quot;, &amp;quot;status changed&amp;quot;);
}
&lt;/pre&gt;&lt;p&gt;The only one we are really interested in is the first, which indicates that the location has changed. In this case we call the &lt;em&gt;updatePlaces&lt;/em&gt; method again. Otherwise we simply write out a Log message.&lt;/p&gt;&lt;p&gt;At the end of the &lt;em&gt;updatePlaces&lt;/em&gt; method, add a request for the app to receive location updates:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
locMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 100, this);
&lt;/pre&gt;&lt;p&gt;We use the Location Manager we created earlier in the series, requesting updates using the network provider, at delays of 30 seconds (indicated in milliseconds), with a minimum location change of 100 meters and the Activity class itself to receive the updates. You can, of course, alter some of the parameters to suit your own needs.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Although the &lt;em&gt;requestLocationUpdates&lt;/em&gt; method specifies a minimum time and distance for updates, in reality it can cause the &lt;em&gt;onLocationChanged&lt;/em&gt; method to execute much more often, which has serious performance implications. In any apps you plan on releasing to users, you should therefore limit the frequency at which your code responds to these location updates. The alternative &lt;a
href="http://developer.android.com/reference/android/location/LocationManager.html"&gt;requestSingleUpdate&lt;/a&gt; method used on a timed basis may be worth considering.&lt;/p&gt;&lt;h3&gt;Step 2&lt;/h3&gt;&lt;p&gt;Last but not least, we need to take care of what happens when the app pauses and resumes. Override the two methods as follows:&lt;/p&gt;&lt;pre class="brush: java; title: ;"&gt;
@Override
protected void onResume() {
	super.onResume();
	if(theMap!=null){
		locMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 100, this);
	}
}
@Override
protected void onPause() {
	super.onPause();
	if(theMap!=null){
		locMan.removeUpdates(this);
	}
}
&lt;/pre&gt;&lt;p&gt;We check for the GoogleMap object before attempting any processing, as in &lt;em&gt;onCreate&lt;/em&gt;. If the app is pausing, we stop it from requesting location updates. If the app is resuming, we start requesting the updates again.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; We&amp;#8217;ve used the &lt;em&gt;LocationManager.NETWORK_PROVIDER&lt;/em&gt; a few times in this series. If you are exploring localization functionality in your apps, check out the alternative &lt;a
href="http://developer.android.com/reference/android/location/LocationManager.html"&gt;getBestProvider&lt;/a&gt; method with which you can specify criteria for Android to choose a provider based on such factors as accuracy and speed.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;Before We Finish&lt;/h2&gt;&lt;p&gt;That pretty much completes the app! However, there are many aspects of the Google Maps Android API v2 that we have not even touched on. Once you have your app running you can experiment with features such as rotation and tilting. The updated maps service displays indoor and 3D maps in certain places. The following image shows the 3D facility with the app if the user location was in Venice, Italy:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="3D Map" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/sue-smith/map_3d.png" /&gt;&lt;/figure&gt;&lt;p&gt;This has the map type set to normal &amp;#8211; here is another view of Venice with the hybrid map type set:&lt;/p&gt; &lt;figure
class="tutorial_image"&gt;&lt;img
alt="Hybrid Map in Venice" src="http://cdn.tutsplus.com/mobile.tutsplus.com/authors/sue-smith/hybrid_venice.png" /&gt;&lt;/figure&gt;&lt;hr
/&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;In this tutorial series we have worked through the process of integrating both Google Maps and Google Places APIs in a single Android app. We handled API key access, setting up the development environment, workspace and application to use Google Play Services. We utilized location data, showing the user location together with nearby places of interest, and displaying the data with custom UI elements. Although what we have covered in this series is fairly extensive, it really is only the beginning when it comes to building localization features into Android apps. With the release of Version 2 of the Maps API, Android apps are set to take such functions to the next level.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/SwahBuPjdqg" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/tutorials/android/android-sdk-working-with-google-maps-displaying-places-of-interest/feed/</wfw:commentRss> <slash:comments>6</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/tutorials/android/android-sdk-working-with-google-maps-displaying-places-of-interest/</feedburner:origLink></item> <item><title>Build an AudioPlayer with PhoneGap: Application Logic</title><link>http://feedproxy.google.com/~r/MobileTuts/~3/wfUFKlgjUbE/</link> <comments>http://mobile.tutsplus.com/tutorials/phonegap/build-an-audioplayer-with-phonegap-application-logic/#comments</comments> <pubDate>Fri, 03 May 2013 00:11:06 +0000</pubDate> <dc:creator>Aurelio De Rosa</dc:creator> <category><![CDATA[PhoneGap]]></category> <guid isPermaLink="false">http://mobile.tutsplus.com/?p=16358</guid> <description>&lt;p&gt;This is the second part of the series about &lt;em&gt;Audero Audio Player&lt;/em&gt;. In this article, we&amp;#8217;re going to create the business logic of our player. I&amp;#8217;ll also explain some of the Cordova APIs that were introduced in the &lt;a
href="http://mobile.tutsplus.com/tutorials/phonegap/build-an-audioplayer-with-phonegap-application-setup/"&gt;previous article&lt;/a&gt;.&lt;br
/&gt;&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;Series Overview&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/?p=16347"&gt;Application Setup&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/?p=16358"&gt;Application Logic&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a
href="http://mobile.tutsplus.com/?p=16364"&gt;Application Tuning&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr
/&gt;&lt;h2&gt;Creating the Player&lt;/h2&gt;&lt;p&gt;In this section, I&amp;#8217;ll show you the class called &lt;code&gt;Player&lt;/code&gt;, which let us play, stop, rewind and fast-forward. The class relies heavily on the Media API; without its methods, our player will be completely useless. In addition to the Media API, this class takes advantage of the &lt;code&gt;alert()&lt;/code&gt; method of the &lt;a
href="http://docs.phonegap.com/en/2.3.0/cordova_notification_notification.md.html#Notification"&gt;Notification API&lt;/a&gt;. The look of the alert varies among platforms. Most of the supported operating systems use a native dialog box but others, like Bada 2.X, use the classic browser&amp;#8217;s &lt;code&gt;alert()&lt;/code&gt; function, which is less customizable. The former method accepts up to four parameters:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;message&lt;/strong&gt;: A string containing the message to show&lt;/li&gt;&lt;li&gt;&lt;strong&gt;alertCallback&lt;/strong&gt;: A callback to invoke when the alert dialog is dismissed&lt;/li&gt;&lt;li&gt;&lt;strong&gt;title&lt;/strong&gt;: The title of the dialog (the default value is &amp;#8220;Alert&amp;#8221;)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;buttonName&lt;/strong&gt;: The button&amp;#8217;s text included in the dialog (the default value is &amp;#8220;OK&amp;#8221;)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Bear in mind that Windows Phone 7 ignores the button name and always uses the default. Windows Phone 7 and 8 don&amp;#8217;t have a built-in browser alert, so if you want to use &lt;code&gt;alert('message');&lt;/code&gt;, you have to assign &lt;code&gt;window.alert = navigator.notification.alert&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Now that I&amp;#8217;ve explained the APIs used by &lt;code&gt;Player&lt;/code&gt;, we can take a look at how it&amp;#8217;s made. We have three properties:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;media&lt;/code&gt;: the reference to the current sound object&lt;/li&gt;&lt;li&gt;&lt;code&gt;mediaTimer&lt;/code&gt;: which will contain a unique interval ID created using the &lt;code&gt;setInterval()&lt;/code&gt; function that we&amp;#8217;ll pass to &lt;code&gt;clearInterval()&lt;/code&gt; to stop the sound&amp;#8217;s timer&lt;/li&gt;&lt;li&gt;&lt;code&gt;isPlaying&lt;/code&gt;: a variable that specifies if the current sound is playing or not. In addition to the property, the class has several methods.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The &lt;code&gt;initMedia()&lt;/code&gt; method initializes the &lt;code&gt;media&lt;/code&gt; property with a &lt;code&gt;Media&lt;/code&gt; object that represents the sound selected by the user. The latter is notified using the &lt;strong&gt;Notification API&lt;/strong&gt; in case of error. The aim of the &lt;code&gt;playPause&lt;/code&gt;, &lt;code&gt;stop()&lt;/code&gt;, and &lt;code&gt;seekPosition()&lt;/code&gt; methods should be obvious, so I&amp;#8217;ll move on. The &lt;code&gt;resetLayout()&lt;/code&gt; and &lt;code&gt;changePlayButton()&lt;/code&gt; methods are very simple. They are used to reset or update the player&amp;#8217;s layout according to the action performed by the user. The last remaining method is &lt;code&gt;updateSliderPosition()&lt;/code&gt;,  which is similar to the time slider. The latter has zero (the beginning of the slider) as the default value for the current position, set using the &lt;code&gt;value="0"&lt;/code&gt; attribute. This must be updated accordingly while the sound is playing to give to the user visual feedback concerning elapsed playing time.&lt;/p&gt;&lt;p&gt;We&amp;#8217;ve uncovered all the details of this class, so here is the source code of the file:&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
var Player = {
   media: null,
   mediaTimer: null,
   isPlaying: false,
   initMedia: function(path) {
      Player.media = new Media(
         path,
         function() {
            console.log('Media file read succesfully');
            if (Player.media !== null)
               Player.media.release();
            Player.resetLayout();
         },
         function(error) {
            navigator.notification.alert(
               'Unable to read the media file.',
               function(){},
               'Error'
            );
            Player.changePlayButton('play');
            console.log('Unable to read the media file (Code): ' + error.code);
         }
      );
   },
   playPause: function(path) {
      if (Player.media === null)
         Player.initMedia(path);
      if (Player.isPlaying === false)
      {
         Player.media.play();
         Player.mediaTimer = setInterval(
            function() {
               Player.media.getCurrentPosition(
                  function(position) {
                     if (position &amp;gt; -1)
                     {
                        $('#media-played').text(Utility.formatTime(position));
                        Player.updateSliderPosition(position);
                     }
                  },
                  function(error) {
                     console.log('Unable to retrieve media position: ' + error.code);
                     $('#media-played').text(Utility.formatTime(0));
                  }
               );
            },
            1000
         );
         var counter = 0;
         var timerDuration = setInterval(
            function() {
               counter++;
               if (counter &amp;gt; 20)
                  clearInterval(timerDuration);
               var duration = Player.media.getDuration();
               if (duration &amp;gt; -1)
               {
                  clearInterval(timerDuration);
                  $('#media-duration').text(Utility.formatTime(duration));
                  $('#time-slider').attr('max', Math.round(duration));
                  $('#time-slider').slider('refresh');
               }
               else
                  $('#media-duration').text('Unknown');
            },
            100
         );
         Player.changePlayButton('pause');
      }
      else
      {
         Player.media.pause();
         clearInterval(Player.mediaTimer);
         Player.changePlayButton('play');
      }
      Player.isPlaying = !Player.isPlaying;
   },
   stop: function() {
      if (Player.media !== null)
      {
         Player.media.stop();
         Player.media.release();
      }
      clearInterval(Player.mediaTimer);
      Player.media = null;
      Player.isPlaying = false;
      Player.resetLayout();
   },
   resetLayout: function() {
      $('#media-played').text(Utility.formatTime(0));
      Player.changePlayButton('play');
      Player.updateSliderPosition(0);
   },
   updateSliderPosition: function(seconds) {
      var $slider = $('#time-slider');
      if (seconds &amp;lt; $slider.attr('min'))
         $slider.val($slider.attr('min'));
      else if (seconds &amp;gt; $slider.attr('max'))
         $slider.val($slider.attr('max'));
      else
         $slider.val(Math.round(seconds));
      $slider.slider('refresh');
   },
   seekPosition: function(seconds) {
      if (Player.media === null)
         return;
      Player.media.seekTo(seconds * 1000);
      Player.updateSliderPosition(seconds);
   },
   changePlayButton: function(imageName) {
      var background = $('#player-play')
      .css('background-image')
      .replace('url(', '')
      .replace(')', '');
      $('#player-play').css(
         'background-image',
         'url(' + background.replace(/images\/.*\.png$/, 'images/' + imageName + '.png') + ')'
      );
   }
};
&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;Managing the Audio Files&lt;/h2&gt;&lt;p&gt;This section illustrates the &lt;code&gt;AppFile&lt;/code&gt; class that will be used to create, delete, and load the sounds using the &lt;a
href="http://www.w3.org/TR/webstorage/"&gt;Web Storage API&lt;/a&gt;. This API has two areas, &lt;em&gt;Session&lt;/em&gt; and &lt;em&gt;Local&lt;/em&gt;, but Cordova uses the latter. All the sounds are stored in an item titled &amp;#8220;files&amp;#8221; as you can see by looking at the &lt;code&gt;_tableName&lt;/code&gt; properties.&lt;/p&gt;&lt;p&gt;Please note that this API is only able to store basic data. Therefore, to fit our need to store objects, we&amp;#8217;ll use the JSON format. JavaScript has a class to deal with this format called JSON. It uses the methods &lt;code&gt;parse()&lt;/code&gt; to parse a string and recreate the appropriate data, and &lt;code&gt;stringify()&lt;/code&gt; to convert the object in a string. As a final note, I won&amp;#8217;t be using the dot notation of the API because Windows Phone 7 doesn’t support it, so we&amp;#8217;ll use the &lt;code&gt;setItem()&lt;/code&gt; and &lt;code&gt;getItem()&lt;/code&gt; methods to ensure compatibility for all devices.&lt;/p&gt;&lt;p&gt;Now that you have an overview of how we&amp;#8217;ll store the data, let&amp;#8217;s talk about the data that we need to save. The only information that we need for each found sound is the name (&lt;code&gt;name&lt;/code&gt; property) and an absolute path (&lt;code&gt;fullPath&lt;/code&gt; property). The &lt;code&gt;AppFile&lt;/code&gt; class also has a &amp;#8220;constant&amp;#8221;, called &lt;code&gt;EXTENSIONS&lt;/code&gt;, where we&amp;#8217;ll set the extensions that will be tested against each file. If they match up, the file will be collected by the application. We have a method to add a file (&lt;code&gt;addFile()&lt;/code&gt;), one method to delete a file (&lt;code&gt;deleteFile()&lt;/code&gt;), one method that deletes the whole database (&lt;code&gt;deleteFiles()&lt;/code&gt;), and, lastly, two methods that retrieve the file from the database: &lt;code&gt;getAppFiles()&lt;/code&gt; to retrieve all the files, and &lt;code&gt;getAppFile()&lt;/code&gt; to retrieve just one. The class also has four comparison methods, two static (&lt;code&gt;compare()&lt;/code&gt; and &lt;code&gt;compareIgnoreCase()&lt;/code&gt;) and two non-static (&lt;code&gt;compareTo()&lt;/code&gt; and &lt;code&gt;compareToIgnoreCase()&lt;/code&gt;). The last method is the one used to retrieve the index of a certain file, &lt;code&gt;getIndex()&lt;/code&gt;. The &lt;code&gt;AppFile&lt;/code&gt; class enables you to perform all the basic operations you may need.&lt;/p&gt;&lt;p&gt;The code that implements what we&amp;#8217;ve discussed can be read here:&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
function AppFile(name, fullPath)
{
   var _db = window.localStorage;
   var _tableName = 'files';
   this.name = name;
   this.fullPath = fullPath;
   this.save = function(files)
   {
      _db.setItem(_tableName, JSON.stringify(files));
   }
   this.load = function()
   {
      return JSON.parse(_db.getItem(_tableName));
   }
}
AppFile.prototype.addFile = function()
{
   var index = AppFile.getIndex(this.fullPath);
   var files = AppFile.getAppFiles();
   if (index === false)
      files.push(this);
   else
      files[index] = this;
   this.save(files);
};
AppFile.prototype.deleteFile = function()
{
   var index = AppFile.getIndex(this.fullPath);
   var files = AppFile.getAppFiles();
   if (index !== false)
   {
      files.splice(index, 1);
      this.save(files);
   }
   return files;
};
AppFile.prototype.compareTo = function(other)
{
   return AppFile.compare(this, other);
};
AppFile.prototype.compareToIgnoreCase = function(other)
{
   return AppFile.compareIgnoreCase(this, other);
};
AppFile.EXTENSIONS = ['.mp3', '.wav', '.m4a'];
AppFile.compare = function(appFile, other)
{
   if (other == null)
      return 1;
   else if (appFile == null)
      return -1;
   return appFile.name.localeCompare(other.name);
};
AppFile.compareIgnoreCase = function(appFile, other)
{
   if (other == null)
      return 1;
   else if (appFile == null)
      return -1;
   return appFile.name.toUpperCase().localeCompare(other.name.toUpperCase());
};
AppFile.getAppFiles = function()
{
   var files = new AppFile().load();
   return (files === null) ? [] : files;
};
AppFile.getAppFile = function(path)
{
   var index = AppFile.getIndex(path);
   if (index === false)
      return null;
   else
   {
      var file = AppFile.getAppFiles()[index];
      return new AppFile(file.name, file.fullPath);
   }
};
AppFile.getIndex = function(path)
{
   var files = AppFile.getAppFiles();
   for(var i = 0; i &amp;lt; files.length; i++)
   {
      if (files[i].fullPath.toUpperCase() === path.toUpperCase())
         return i;
   }
   return false;
};
AppFile.deleteFiles = function()
{
   new AppFile().save([]);
};
&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;The Utility Class&lt;/h2&gt;&lt;p&gt;The &lt;code&gt;utility.js&lt;/code&gt; file is very short and easy to understand. It only has two methods. One is used to convert milliseconds into a formatted string that will be shown in the player, while the other is a JavaScript implementation of the well known Java method &lt;code&gt;endsWith&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Here is the source:&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
var Utility = {
   formatTime: function(milliseconds) {
      if (milliseconds &amp;lt;= 0)
         return '00:00';
      var seconds = Math.round(milliseconds);
      var minutes = Math.floor(seconds / 60);
      if (minutes &amp;lt; 10)
         minutes = '0' + minutes;
      seconds = seconds % 60;
      if (seconds &amp;lt; 10)
         seconds = '0' + seconds;
      return minutes + ':' + seconds;
   },
   endsWith: function(string, suffix) {
      return string.indexOf(suffix, string.length - suffix.length) !== -1;
   }
};
&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;Putting it All Together&lt;/h2&gt;&lt;p&gt;This section discusses the last JavaScript file of the project, &lt;code&gt;application.js&lt;/code&gt;, which contains the &lt;code&gt;Application&lt;/code&gt; class. Its aim is to attach events to the app page&amp;#8217;s elements. Those events will take advantage of the classes we&amp;#8217;ve seen thus far and enable the player to work properly.&lt;/p&gt;&lt;p&gt;The code of the illustrated function is listed below:&lt;/p&gt;&lt;pre class="brush: jscript; title: ;"&gt;
var Application = {
   initApplication: function() {
      $(document).on(
         'pageinit',
         '#files-list-page',
         function()
         {
            Application.initFilesListPage();
         }
      );
      $(document).on(
         'pageinit',
         '#aurelio-page',
         function()
         {
            Application.openLinksInApp();
         }
      );
      $(document).on(
         'pagechange',
         function(event, properties)
         {
            if (properties.absUrl === $.mobile.path.makeUrlAbsolute('player.html'))
            {
               Application.initPlayerPage(
                  JSON.parse(properties.options.data.file)
               );
            }
         }
      );
   },
   initFilesListPage: function() {
      $('#update-button').click(
         function()
         {
            $('#waiting-popup').popup('open');
            setTimeout(function(){
               Application.updateMediaList();
            }, 150);
         }
      );
      $(document).on('endupdate', function(){
         Application.createFilesList('files-list', AppFile.getAppFiles());
         $('#waiting-popup').popup('close');
      });
      Application.createFilesList('files-list', AppFile.getAppFiles());
   },
   initPlayerPage: function(file) {
      Player.stop();
      $('#media-name').text(file.name);
      $('#media-path').text(file.fullPath);
      $('#player-play').click(function() {
         Player.playPause(file.fullPath);
      });
      $('#player-stop').click(Player.stop);
      $('#time-slider').on('slidestop', function(event) {
         Player.seekPosition(event.target.value);
      });
   },
   updateIcons: function()
   {
      if ($(window).width() &amp;gt; 480)
      {
         $('a[data-icon], button[data-icon]').each(function() {
            $(this).removeAttr('data-iconpos');
         });
      }
      else
      {
         $('a[data-icon], button[data-icon]').each(function() {
            $(this).attr('data-iconpos', 'notext');
         });
      }
   },
   openLinksInApp: function()
   {
      $(&amp;quot;a[target=\&amp;quot;_blank\&amp;quot;]&amp;quot;).on('click', function(event) {
         event.preventDefault();
         window.open($(this).attr('href'), '_target');
      });
   },
   updateMediaList: function() {
      window.requestFileSystem(
         LocalFileSystem.PERSISTENT,
         0,
         function(fileSystem){
            var root = fileSystem.root;
            AppFile.deleteFiles();
            Application.collectMedia(root.fullPath, true);
         },
         function(error){
            console.log('File System Error: ' + error.code);
         }
      );
   },
   collectMedia: function(path, recursive, level) {
      if (level === undefined)
         level = 0;
      var directoryEntry = new DirectoryEntry('', path);
      if(!directoryEntry.isDirectory) {
         console.log('The provided path is not a directory');
         return;
      }
      var directoryReader = directoryEntry.createReader();
      directoryReader.readEntries(
         function (entries) {
            var appFile;
            var extension;
            for (var i = 0; i &amp;lt; entries.length; i++) {
               if (entries[i].name === '.')
                  continue;
               extension = entries[i].name.substr(entries[i].name.lastIndexOf('.'));
               if (entries[i].isDirectory === true &amp;amp;&amp;amp; recursive === true)
                  Application.collectMedia(entries[i].fullPath, recursive, level + 1);
               else if (entries[i].isFile === true &amp;amp;&amp;amp; $.inArray(extension, AppFile.EXTENSIONS) &amp;gt;= 0)
               {
                  appFile = new AppFile(entries[i].name, entries[i].fullPath);
                  appFile.addFile();
                  console.log('File saved: ' + entries[i].fullPath);
               }
            }
         },
         function(error) {
            console.log('Unable to read the directory. Errore: ' + error.code);
         }
      );
      if (level === 0)
         $(document).trigger('endupdate');
      console.log('Current path analized is: ' + path);
   },
   createFilesList: function(idElement, files)
   {
      $('#' + idElement).empty();
      if (files == null || files.length == 0)
      {
         $('#' + idElement).append('&amp;lt;p&amp;gt;No files to show. Would you consider a files update (top right button)?&amp;lt;/p&amp;gt;');
         return;
      }
      function getPlayHandler(file) {
         return function playHandler() {
            $.mobile.changePage(
               'player.html',
               {
                  data: {
                     file: JSON.stringify(file)
                  }
               }
            );
         };
      }
      function getDeleteHandler(file) {
         return function deleteHandler() {
            var oldLenght = AppFile.getAppFiles().length;
            var $parentUl = $(this).closest('ul');
            file = new AppFile('', file.fullPath);
            file.deleteFile();
            if (oldLenght === AppFile.getAppFiles().length + 1)
            {
               $(this).closest('li').remove();
               $parentUl.listview('refresh');
            }
            else
            {
               console.log('Media not deleted. Something gone wrong.');
               navigator.notification.alert(
                  'Media not deleted. Something gone wrong so please try again.',
                  function(){},
                  'Error'
               );
            }
         };
      }
      var $listElement, $linkElement;
      files.sort(AppFile.compareIgnoreCase);
      for(var i = 0; i &amp;lt; files.length; i++)
      {
         $listElement = $('&amp;lt;li&amp;gt;');
         $linkElement = $('&amp;lt;a&amp;gt;');
         $linkElement
         .attr('href', '#')
         .text(files[i].name)
         .click(getPlayHandler(files[i]));
         // Append the link to the &amp;lt;li&amp;gt; element
         $listElement.append($linkElement);
         $linkElement = $('&amp;lt;a&amp;gt;');
         $linkElement
         .attr('href', '#')
         .text('Delete')
         .click(getDeleteHandler(files[i]));
         // Append the link to the &amp;lt;li&amp;gt; element
         $listElement.append($linkElement);
         // Append the &amp;lt;li&amp;gt; element to the &amp;lt;ul&amp;gt; element
         $('#' + idElement).append($listElement);
      }
      $('#' + idElement).listview('refresh');
   }
};
&lt;/pre&gt;&lt;hr
/&gt;&lt;h2&gt;Managing External Links&lt;/h2&gt;&lt;p&gt;In the &lt;a
href="http://mobile.tutsplus.com/tutorials/phonegap/build-an-audioplayer-with-phonegap-application-setup/"&gt;previous part of this series&lt;/a&gt;, I mentioned that an interesting point of the credits page was the attribute &lt;code&gt;target="_blank"&lt;/code&gt; applied to the links. This section will explain why the &lt;code&gt;openLinksInApp()&lt;/code&gt; method of the &lt;code&gt;Application&lt;/code&gt; class makes sense.&lt;/p&gt;&lt;p&gt;Once upon a time, Cordova used to open external links in the same Cordova WebView that was running the application. When a link was open and the user clicked the &amp;#8220;back&amp;#8221; button, the last displayed page was shown exactly as it was before the user left it. In the newer version, this has changed. Nowadays, the external links are opened, by default, using the Cordova WebView if the URL is in your app&amp;#8217;s whitelist. URLs that aren&amp;#8217;t on your whitelist are opened using the InAppBrowser API. If you don&amp;#8217;t manage the links in the right way, or if the user taps a link that is shown in the InAppBrowser or the system and then chooses to go back, all the jQuery Mobile enhancements are lost. This behavior happens because the CSS and JavaScript files are loaded by the main page, and the following ones are loaded using AJAX. Before uncovering the solution, let&amp;#8217;s take a look at what&amp;#8217;s the InAppBrowser.&lt;/p&gt;&lt;p&gt;&lt;q
cite="http://docs.phonegap.com/en/2.3.0/cordova_inappbrowser_inappbrowser.md.html#InAppBrowser"&gt;The InAppBrowser is a web-browser that is shown in your app when you use the window.open call.&lt;/q&gt;&lt;/p&gt;&lt;p&gt;This API has three methods:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;addEventListener():&lt;/code&gt; Allows you to listen for three events (&lt;code&gt;loadstart&lt;/code&gt;, &lt;code&gt;loadstop&lt;/code&gt;, and &lt;code&gt;exit)&lt;/code&gt; and attach a function that runs as soon as those events are fired&lt;/li&gt;&lt;li&gt;&lt;code&gt;removeEventListener()&lt;/code&gt;: Removes a previously-attached listener.&lt;/li&gt;&lt;li&gt;&lt;code&gt;close()&lt;/code&gt;: Used to close the InAppBrowser window.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So, what&amp;#8217;s the solution? The aim of the &lt;code&gt;openLinksInApp()&lt;/code&gt; function, coupled with the whitelist specified in the configuration file, is to catch the clicks on all the external links recognized by using the &lt;code&gt;target="_blank"&lt;/code&gt; attribute, and open them using the &lt;code&gt;window.open()&lt;/code&gt; method. With this technique, we&amp;#8217;ll avoid the problem described, and our player will continue to look and work as expected.&lt;/p&gt;&lt;hr
/&gt;&lt;h2&gt;Next Part&lt;/h2&gt;&lt;p&gt;In the third and last installment of this series, we&amp;#8217;ll see the last remaining files so that you can complete the project and play around with it.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/MobileTuts/~4/wfUFKlgjUbE" height="1" width="1"/&gt;</description> <wfw:commentRss>http://mobile.tutsplus.com/tutorials/phonegap/build-an-audioplayer-with-phonegap-application-logic/feed/</wfw:commentRss> <slash:comments>2</slash:comments> <feedburner:origLink>http://mobile.tutsplus.com/tutorials/phonegap/build-an-audioplayer-with-phonegap-application-logic/</feedburner:origLink></item> </channel> </rss><!-- Dynamic Page Served (once) in 1.260 seconds -->
