Category Archives: iOS

Launching the Facebook & Twitter Websites From a Web App

25 Mar 2013

Creating a link within your mobile HTML5/web apps is exactly like creating a link that launches in a new window, you simply do the following:

...
<a href="http://somewhere.com" target="_blank"></a>
...

Or via JavaScript:

...
<script language="javascript">
  window.open('http://somewhere.com');
</script>
...

The above does what one would expect in most cases – launching the URL in the device’s mobile browser. However, on iOS if we are trying to go to a Facebook or Twitter site such as in this example:

...
<a href="http://www.facebook.com/pepsi" target="_blank"></a>
...

…and the Facebook App is installed on the iOS device the Facebook App itself will launch instead and present the Facebook login screen – which is not what we want to happen. In this case getting around this iOS quirk is easy – create a proxy page on a server of your choice that will redirect Mobile Safari to the desired location.

For example you might have a link like this one within your web app:

...
<a href="http://myserver.com/redir.html" target="_blank"></a>
...

Given the above your proxy page would contain a single line of JavaScript:

...
document.location.href = 'http://www.facebook.com/pepsi'; // go to the branded pepsi facebook page
...

Pretty simple… but in my specific example I want to have a single proxy page handle multiple Facebook and Twitter cases. Given this information the link within my web app looks similar to this:

...
<a href="http://myserver.com/redir.html?b=abc&sn=fb" target="_blank"></a>
...

The convention that I’ve setup here is that I have a link to a specific brand (“b”) whose branded social network (“sn”) website that I want the user to go to. In my “redir.html” document I have some simple JavaScript to inspect the URL’s name/value pairs and thus branch to the desired brand’s specific social network website based on that information.

...
<script language="javascript">
  var loc = document.location.href.split('?');
  var args = loc[1].split('&');
  var brand = args[0].split('=');
  var sn = args[1].split('=');
  if (brand[1] == 'abc'){
    if (sn[1] == 'fb'){
      // go to the brand's facebook site
      document.location.href = 'http://www.facebook.com/abc';
    } else if (sn[1] == 'tw'){
      // go to the brand's twitter website
      document.location.href = 'http://www.twitter.com/abc';
    }
  } else if (brand[1] == 'xyz'){
    // etc...
  } 
  // etc.....
</script>
...

Mobile Safari Debug Console Breaks @Media Query

03 Jul 2012

Spent a lot of time spinning my wheels on this – in these cases you always think about whats changed since the last time your layout did what you wanted…and I backed up all the way to settings I enabled/disabled on the iPhone, and found the issue!

If you’re using an @media query like the following:

...
@media screen and (max-width:320px) and (orientation: portrait){
    /* your css here */
}
...

You will notice that it **breaks** when Mobile Safari’s debug console is enabled.

We can look into this a little further by using JavaScript to get the device’s reported width and we can infer the orientation from the number of degrees of rotation. To discover this information:

...
alert('width: ' + window.innerWidth + '\n orientation: ' + window. orientation);
...

Holding the iPhone in a portrait orientation with the debug console on we see the following values when reloading the test page:

  • width: 320
  • orientation: 0

The same test with the debug console off… is the same as with it on – there’s no smoking gun here as to why this is happening. The above @media query for unknown reasons is just plain broken with the debug console enabled!

Formatting Credit Card Numbers – Dealing with Webkit Text Input Oddity

27 Apr 2012

Another day, another script added to my library…. I had a credit card text field in one of my Sencha Touch projects that needed to be “dash separated” while the person is typing. Below is my solution to this particular minor challenge. Please remove any other validation in your project as this already features:

  • Removal of all undesired alpha characters and symbols
  • Limited to a maximum of 16 numbers
  • Plays nice when you backspace from the end

Note that you shouldn’t try to correct a number in the middle of the string – if you delete a number the entire string gets re-written for the current string of numbers. Also, the first thing that may come to mind is the HTML 5 “pattern” attribute which accepts a regular expression with which to validate the text field value, however, that would validate as you type, not format the string in the desired manner while the typing is happening. Sencha has its own way of validating text fields which is essentially the same thing but the issue is that it also doesn’t do on-the-fly string formatting.

So here is the function itself, which works perfectly in a normal web-browser (yes, a caveat for android webkit follows):

...
    function do_ccFormat(str){
	var nums = str.substr(0,19).replace(/[^\d]/gi,'');
	var r = nums.match(/(\d){4}/g);
	if (r){	
	    var i=0, nStr = '';
	    for (;i

However, in Android 2.3.6's webkit there is some odd behavior that is a pattern that I'm sure others somewhere have come up against. So the pattern is this: first assume that you are doing some string manipulation on the keyup event, waiting for some criteria to be met at every key stroke before manipulating the string. In the case of my CC formatter its the 5th character that triggers the string re-write. So lets say you type 5 characters such as "12345". When "5" is typed the entire string is replaced with a new one that contains a new character/delimiter - like so: "1234-5". The very next character that you type will not go at the end where the cursor is but before the last character that was entered. So if we enter "6" the string ends up like this: "1234-65". Further, if you hit the backspace key you backup not from the cursor position (which is at the end of the string) but from where the last character was erroneously entered! Weird stuff! This gets worse with every new delimiter that is added to the string.

Upon inspection and some thought, I realize that replacing the value with a formatted value as you type is something that the Android 2.x webkit can't handle when it has to then figure out where the cursor should go. It seems that programatically setting the value does not update the cursor position for the field - it stays where it is. If you pay close attention when all this happens you'll notice that the cursor will quickly jump around.

I then hit upon the idea of blurring the field (thinking to therefore dump whatever erroneous machinations may be at play) and then quickly give focus back to it knowing that the act of giving focus to a field sets the cursor to the end. This works as long as you space out the blur/focus methods with a setTimeout. I spaced them out by 100 milliseconds hoping that the soft keyboard wouldn't flash. This appeared to work well Android 2.3.6 (a Samsung Galaxy S2). The keyboard would just sit there apparently none-the-wiser.

While this did seem to work it turned out to be unpredictable. In Android 2.2 the keyboard would go away and not reappear, sometimes that would happen in 2.3.6, and it does go away in Android 3.

Its unfortunate that this doesn't work in pre-ICS Android - it is what it is. Below is how I implemented it in Sencha Touch 1 - as you can see I apply it on keyup for iOS and ICS for as-you-type formatting. For pre-ICS Android I limit the keyed cc number length to a max of 16 characters and apply CC formater when the CC field is blurred (a length of 19 is needed otherwise since we are adding 3 additional characters in the form of the "-" delimiter).

I should end by saying that this obviously does not include a MOD10 check. I'll toss in my MOD10 checker at a later date as a separate article - it will include the ability to check not only entire cc numbers - which all of the readily available MOD10 checkers do - but it will also allow you to check a CC number for its **type** based on the first four digits. Until then, here's Credit Card String Formatter sample implementation:

...
items:[
  {
    xtype:'textfield',
    name:'cardNumber',
    inputType:'tel',
    maxLength:'19',//allow the 3 dashes needed to format the string to be included
    listeners:{
	keyup:function(o,e){
            if (Ext.is.iOS || (Ext.is.Android && Ext.is.AndroidVersion > 3)){
	        o.setValue(do_ccFormat(o.getValue()));
            } else { // assume old android
                o.setValue(o.setValue().substr(0,16));
            }
	},
        blur:function(o,e){
            if (Ext.is.Android && Ext.is.AndroidVersion < 3){
               o.setValue(o.setValue().substr(0,16));
               o.setValue(do_ccFormat(o.getValue()));
            }
        }
    }
  }
]
...

Prevent PhoneGap UIWebView Bounce in iOS

14 Mar 2012

Here’s a quick one – sometimes your html5 app will for whatever reason cause the UIWebView to break out of PhoneGap’s bounce-less restrictions (for lack of a better way to describe it). This will resolve the issue – in Xcode, look for your AppDelegate.m file, located within the Classes directory. Open that up and look for the function called:

...
 (void)webViewDidFinishLoad:(UIWebView *)theWebView
...

Within the curly braces paste the following line:

...
[[theWebView.subviews objectAtIndex:0] setBounces:NO]; 
...

[edit…]

The ability to control bounce has moved to the config.xml file – there may be multiples of this file within your PhoneGap project so if editing one doesn’t seem to do anything you’ve got the wrong file – do a search for the other one.

Within the config.xml add this node:

Making a Phone Call from Within PhoneGap in Android and iOS

22 Feb 2012

Today I came across a feature request that I had not done before – dialing a number from within an app. Some quick research shows that its possible using a specific URI scheme.

What are URI schemes? Honestly Wikipedia does a better job than I ever could in describing them but I think of them as something that allows a specific piece of functionality to happen over the internet, and thus they are usually referred to as protocols. You probably have already seen them – the most common ones are http: and https: (for web browsing), and ftp:, among others. Some are unique to an application and really don’t qualify as schemes and are definitely not a “protocol”, such as mailto: (to open up the mail client on a person’s computer), javascript: or about: – in fact, try typing about: in the address bar of your browser and hit “enter” on your keyboard, notice what happens…

In our case where we want to dial a number from within our app we need a way of telling the mobile phone that we want to make a call. There is a scheme for this purpose called tel:. A sample number using this scheme would look like this: “tel:+1-800-555-1234”. If you wanted a number to work around the world you would use an international number which includes the country code.

Implementing this is simple, we could do this within our mobile html5 app like so:

...
call this number
...

Ideally though we would delegate the event and fire a function to call our mythical phone number. To send the url (the “tel” url) to the browser we would write the following:

...
document.location.href = 'tel:+1-800-555-1234';
...

As of PhoneGap 3.6 all schemes are subject to whitelists. This means you have to add the tel scheme to a second whitelist that will allow your app to launch external applications. To do this you need to edit your config.XML to include the following (a mailto example is included):



Go here for more information: Cordova 3.6.0 Whitelist Guide.

Of interest to this topic is getting Android to treat phone numbers (as well as URLs and mailto schemes) as clickable links in text fields. I’ve not tested it but try adding the following to your config.xml.


Additional information on this can be found here: http://developer.android.com/reference/android/widget/TextView.html#attr_android:autoLink.

[EDIT: Note that what follows no longer applies but remains here for historical purposes.]

When we run the above code in Android 2.3.6 the phone dialer appears and does so with our number pre-populated ready to be dialed. Unfortunately on iOS 5 this doesn’t happen. A quick review of iOS documentation implies that it should work – so I suppose its just broken.
No need to panic, there is a PhoneGap plugin available which will take care of things. The plugin can be downloaded from here:

Click here to download the iOS Phone Dialer PhoneGap plugin

Its simple to install – just drag and drop the “m” and “h” files on to the classes folder of your xcode project. When you do this a dialog will appear with some options – be sure to click the radio button for copying “…files if needed..”.

Next, update the PhoneGap.plist file to reflect that you are adding a new plugin. The link for downloading the plugin explains the plist values as being “phonedialer > PhoneDialer”… but I think its easier to explain with an image:

The final step is to place the “PhoneDialer.js” javascript file somewhere within the root of your project and then to add it to your index.html file via a script tag.

Now that the Phone Dialer plugin is installed you’ll naturally want to know how to use it:

...
window.plugins.phoneDialer.dial('1-800-555-1234');
...

All in all pretty easy and straight forward, however now you have two methods of dialing a number within a single project. What you want is to use the tel: url scheme in Android and the Phone Dialer plugin in iOS.

Within Sencha Touch we have something called the Ext.is object whose attributes reflect everything that you could possibly want to know about the environment that your mobile app is living within.

For our purposes all we want to know is if we are in iOS or if we are in Android. These two lines provide us the answer:

...
Ext.is.Android // boolean, "true" for android, false otherwise
Ext.is.iOS // boolean, "true" for iOS, false otherwise
...

Thats all we need to impliment phone dialing across the two platforms within our mobile app. Lets build a function that makes use of one of the above (we don’t need both) and we should also give the user a choice in the matter, so the code below includes a message to the user to see if they really do want to suspend the app in favor of the device’s phone dialer:

...
function callSomeone(){
    var msg = Ext.Msg.confirm('Please Confirm','Are you sure you want to make a phone call?',
        function(r){
            if (r == 'yes'){
                if (Ext.is.Android){
                    document.location.href = 'tel:+1-800-555-1234';
		} else { // we assume the device is running iOS
		    window.plugins.phoneDialer.dial('1-800-555-1234');
		}
	    }   
	});
    msg.doComponentLayout();
}
...

All done… I suppose the very last thing to do here is to provide a complete working Sencha Touch example, and some screen captures…

...
Ext.setup({
    onReady: function(){
	
        var rootPanel = new Ext.form.FormPanel({
	    fullscreen: true,
	        items: [
		    {
			xtype:'button',
			text:'Call 1-800-555-1234',
			scope:this,
			handler: callSomeone
		    }
		],
		dockedItems:[
		    {
			xtype:'toolbar',
			dock:'top',
			title:'Phone Dialer Example'
		    }          
		]
	    }
	);
	
	function callSomeone(){
	    var msg = Ext.Msg.confirm('Please Confirm','Are you sure you want to make a phone call?',
		function(r){
		    if (r == 'yes'){
		        if (Ext.is.Android){
		            document.location.href = 'tel:+1-800-555-1234';
			} else { // we assume the device is running iOS
			    window.plugins.phoneDialer.dial('1-800-555-1234');
			}
		    }   
		}
	    );
	    msg.doComponentLayout();
	}

    }
});
...

The final product on a Samsung Galaxy S2:

Logo

All content © 2012-2017.