Tag Archives: iOS

Loading an image from the iOS Library in PhoneGap

23 Aug 2016

While working through one of my personal projects I’ve figured out how to load an image from an iOS device’s Library. There are two steps – first use the Camera plugin to provide a UI for the user to select a file. The next is to take the file path the Camera plugin provides and use the File plugin to load it.

Requirements

This was tested via PhoneGap Build using the following setup:

  • CLI 6.3.0
  • iOS deploy target: 9.0.0
  • Camera plugin version 2.2.0
  • File plugin version 3.0.0

If you’re using PhoneGap Build this is what should be added to your config.xml





Using the Camera Plugin to Access the Library

It might seem counter intuitive to use the Camera plugin since it seems logical to first look at the File API to look for files… unlike the File API where you would need to write your own file browser and UI, the Camera Plugin uses native functionality and so makes it trivial to pick an image from a user’s Library. The Camera plugin will present a native UI to the end-user so that they can navigate their Library’s folder structure to locate the image they want to use and in the end provide a path to that image on the device.

This code will do what is described above:

   navigator.camera.getPicture(
      function (fileURI){
         console.log(fileURI);
         /* remove the comment below to use with
          * the rest of the code on this page
          */
         //convertPath(fileURI);
      },
      function (err){
         // fail
         console.log('fail',err);
      },
      {
         allowEdit: true,
         correctOrientation: true,
         destinationType: Camera.DestinationType.FILE_URI,
         sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
         targetHeight: window.innerHeight,
         targetWidth: window.innerWidth
      }
   );

Literally copy and paste the above, here are the things to note about the configuration object:

  • allowEdit – this is a flag that tells the native Library picker UI to allow scaling/positioning of the resource that the user selects.
  • correctOrientation – as it implies, use the image in the correct orientation relevant to how the device is being held
  • destinationType – this is the part that tells the plugin to return the path to the image
  • sourceType – tells the plugin to display UI to allow the user to select the image from the library
  • targetHeight – the desired height of the image – iOS creates a temporary image and passes that path back to you based on any edits and the Height and Width settings. Here I just assume that you would want an image that is the size of the viewport.
  • targetWidth – see above

That’s it. Dead simple. Now we need to load up the file using the path that the Camera plugin returns which requires the use of the File plugin.

Using the File plugin to Load an Image

This part is trickier and the source of much frustration among developers – during my search for documentation there was no single source that explained how this should work. I was left to putting the parts together from various sources as the “official” documentation didn’t directly explain how to do it. Anyway, I’ll do the explaining here within the code comments.

In short, these are the steps that result in a Base64 serialization of the image from which you can do whatever you like:

  1. Convert the image path to a file Entry Object
  2. Pass the FileEntry Object to a function that converts it to a File Object
  3. Pass the File Object to a FileReader to read the file
  4. Handle the response containing the image data

Here is all of the code:

   /**
    * This takes a file:// URI and creates a file entry object. The operation is asynch,
    * so the resulting fileEntry object is passed to the success callback.
    * @type {Function}
    * @name convertPath
    * @param {String} fileURI - the file:// path to the resource
    * @return {} Returns nothing
    */
    function convertPath(fileURI){
        window.resolveLocalFileSystemURL(
            fileURI,
            function(fileEntry){
                getFileSuccess(fileEntry);
            }
        );
    }

   /**
    * This starts the read process via the file entry object. This is asynch, so the file is passed to the success callback
    * @type {Function}
    * @name getFileSuccess
    * @param {Object} fileEntry - the file entry object
    * @return {} Returns nothing
    */
    function getFileSuccess(fileEntry){
        fileEntry.file(
            readFile, // success
            function(err){ // failure
                console.log('Failed to get file.',err);
            }
        );
    }

   /**
    * This creates a file reader using the file object that is passed to it.
    * Note how similar this is to programatically creating an image and loading data into it.
    * @type {Function}
    * @name readFile
    * @param {Object} file - file object
    * @return {} Returns nothing
    */
    function readFile(file){
    	console.log('got file...',file);
        var reader = new window.FileReader();
        reader.oneerror = function(e){
            console.log('FileReader Error: ',e.target.result);
        };
        reader.onloadend = function(fileObject) {
            console.log('we have the file:',fileObject);
            console.log('the image data is in fileObject.target._result');
        };
        reader.readAsDataURL(file);
    }

You can use the fileObject.target._result to populate the background of a div, for example:

$('#myDiv').css('background-image','url:(' + fileObject.target._result + ')');

Or insert it into a canvas:

   var image = new Image();
   var canvas = document.getElementById('canvas');
   var context = canvas.getContext('2d'); //retrieve context
   
   image.onload = function(){
      context.drawImage(this, 0, 0,_canvas.width, _canvas.height);
   }
   image.src = fileObject.target._result; // load the image data

It’s worth noting that of course you’ll need the appropriate styling for your DIVs if using the resulting image data as a background image. Also, if loading the data into a canvas your aspect ratio may be off – you’ll need to figure out how to scale the data to fit the canvas without distortion.

Create Mobile Provisions and P12 files without a Mac for Cloud Build Services

02 Aug 2016

There are many Windows developers who want to create iOS apps but do not want to make the investment into Apple hardware – one way around it is to virtualize OSX which works OK if your intent is to use Xcode. The other option is the situation where you are planning on using cloud services to build a hybrid app. The great thing about building in the cloud is that you don’t have to have Apple hardware to do so. A barrier that you’ll encounter very quickly is that you need two files to build in the cloud: 1) a Mobile Provision File and 2) a P12 file.

NOTE: Some cloud build services don’t submit the app for you (i.e., PhoneGap Build) while others do (i.e., Ionic’s app workflow). In the former case a Mac will be needed to upload your distribution IPA (your compiled app) to Apple. The instructions on this page will allow you to create files for development and production purposes without needing a Mac.

Being a Windows guy (and to be honest, an Android one as well) that didn’t help me much until I realized that I could do everything without a Mac using OpenSSL.

What follows are all the steps that anyone will need to be able to use a cloud build service to build a hybrid PhoneGap app for both development and distribution.

As a bonus this article wraps things up with instructions on how to use the Application Loader to upload and then submit your app to the App Store.

Install Open SSL

To begin, download and install these two pieces of software:

Install the Microsoft product first, then install OpenSSL.

Add OpenSSL to Your Environment Path

I tried to add the c:\OpenSSL-Win32\bin folder to my environment path but had some issues with it. In the end what I did was to execute everything within the bin folder (as described below) and created a naming convention so that all of the files that I created would stay grouped together at the top of the folder. Specifically, since I was creating everything for development purposes, I used the “_dev” prefix. When I was done it was easy to see all the files.

Well, that was my personal preference – you can come up with your own naming conventions…

Create a Certificate Signing Request

If you installed OpenSSL to its default location then navigate to:

  • C:\OpenSSL-Win32\bin

Once in that directory press SHIFT and RIGHT-CLICK in the Explorer window showing that directory. A menu will appear – click on “Open command window here”.

Your currently using the Windows command prompt, type the following and press “enter” to switch to the OpenSSL prompt:

  • openssl

You will get a warning message when you do this, below is an example of typing in the openssl command and the result:

C:\OpenSSL-Win32\bin> openssl
WARNING: can’t open config file: /usr/local/ssl/openssl.cnf
OpenSSL>

The warning is normal and can be ignored. Enter the following command to create a key substituting the name of the key for your desired key name.

  • genrsa -out mykey.key 2048

Next use this command to create your CSR (Certificate Signing Request), be sure to replace the email, name, etc with the proper values:

  • req -new -key mykey.key -out mycertificate.csr -subj “/emailAddress=your@address.com, CN=Joe Smith, C=US” -config “openssl.cfg”

All done, your CSR is in the bin directory. If you followed the above instructions verbatim then your file is called mycertificate.csr. Below is a screen capture of the above console steps.

openssl

Next you need to upload your CSR to the Apple Developer Portal.

Login, click “All” under the “Certificates” heading and then click the “+” (plus) button to begin the upload process. Click continue/next, etc until you see this screen:

cert_upload

Upload your CSR. If successful you will see the next image. If not then recreate your CSR and try again.

cert_ready

Click the “Download” button to download the “ios_development.cer” file <<< make note of this file as you will need it in a moment. Move this file to the c:\OpenSSL-Win32\bin directory if you are executing your OpenSSL commands from that location.

Add Devices

Before you create a Mobile Provision: In order to be able to install your app on to development devices you need to register them in the developer portal. Again, within the Developer portal click the “All” link under the “Devices” category and then enter the desired name of the device and its UDID.

Create a Mobile Provision file

This time in the Apple Developer Console click “All” under “Provisioning Profiles” then:

  1. Click the “+” icon
  2. Select the appropriate type of provision – in my case it is “iOS App Development”
  3. Click “Continue”
  4. Select the appropriate App ID
  5. Click “Continue”
  6. Select the appropriate Certificate
  7. Click “Continue”
  8. Select the development devices that you want to work with your profile
  9. Click “Continue”
  10. Give your profile a name
  11. Again, click “Continue”…

The provisioning profile has been created, download it and keep it in a safe place as you will need to upload it to your cloud build service (1 of 2 files that you need for that purpose).

P12 Certificate

Along with the Mobile Provision file the P12 (also known as a PFX) is provided to cloud build services to build your iOS app. A P12 is a combined format that holds both the private key and the certificate.

Before you can create the P12 file you have to convert the certificate that Apple provides you after you’ve uploaded the CSR to a PEM file.

To convert the Apple-provided “CER” file (previously referred to as the “ios_development.cer” file) to a PEM:

  • x509 -in ios_development.cer -inform DER -out developer_identity.pem -outform PEM

Where:

  • ios_development.cer is the CER file you downloaded from Apple
  • developer_identity.pem is the desired name of your PEM file

When you issue the above command you will have a “PEM” file in your bin folder. Next you’ll convert that to a P12/PFX using this command:

  • pkcs12 -export -inkey mykey.key -in developer_identity.pem -out my_p12.p12

Where:

  • mykey.key is your key file
  • developer_identity.pem is the PEM file created in the previous step
  • my_p12.p12 is the desired name of your P12 file

Note that:

  • You will be asked for the “Export Password” when creating the P12
  • You will be asked to verify the password

Look for your P12 in the bin folder. That file along with your Mobile Provision file are the files your cloud build service will need to compile your apps. Also, take this moment to write down the password you used for your P12!!


Creating an App Store Production Certificate & Provisioning Profile

PhoneGap Build does a great job of creating IPA’s that you can side-load via iTunes to your i-device. Eventually you’ll be ready to submit your app to the App Store. To do so you repeat the steps that you took to create all the needed files for development, just be sure to choose options relevant for submission to the App Store.

As an aside, Ionic’s workflow now includes direct submission of your apps to the Apple App Store – no Mac needed! In the scenario described in this blog post, however, you **do** need a Mac to submit your app. I recommend finding a friend with a Mac instead of shelling out $$$ to buy one. Or if you’re the adventurous type go ahead and check out Ionic.

Back to the topic at hand – you will *** REPEAT *** all the steps above and only change a couple of things. You will choose options specifically for distributing your app. These two differences are described in the next two sections.

Create App Store Distribution Certificate

As before, follow the same process for creating the Certificate. There is one key difference – you will choose the “App Store and Ad Hoc” option as noted in these steps:

  1. Log into the Apple Developer Portal
  2. Click “Certificates, Identifiers & Profiles”
  3. Click the “+” icon
  4. The “What Type of Certificate do you need?” screen appears. Under the “Production” heading choose “App Store and Ad Hoc
  5. The next screen is titled “About Creating a Certificate Signing Request (CSR)”. Click the “Continue” button
  6. The next screen is titled “Generate Your Certificate” – on this page you will upload the CSR that you created earlier. Go ahead and upload and the Distribution Certificate will be created. Download it and keep it in a safe place.

Create a Distribution Provisioning Profile

Again, you’ve already done this for your development files – repeat them here but choose “App Store” where appropriate:

  1. Log into the Apple Developer Portal
  2. In the left column under “Provisioning Profiles” click on “Distribution”
  3. Click the “+” icon
  4. This page is titled “What type of provisioning profile do you need?” – choose “App Store“, then click “Continue”
  5. The next screen is the “Select App ID” screen. Choose the bundle identifier relevant to your app then click “Continue”
  6. The next screen is titled “Select certificates” – choose the certificate that you created previously. Click the “Continue” button
  7. Next is the “Name this profile and generate” screen – give the profile a meaningful name then click “Continue” to generate your Distribution Provisioning Profile

Build the Production IPA in the Cloud

Log into PhoneGap Build, add your new files (select “Add a key” in the iOS “key” drop down list) and do a build against it. Since the files you are providing are distribution files you will automatically get a distribution IPA as a result. Download the production-ready distribution IPA to the Mac that you will use to submit your app to the App Store.

Upload Your Distribution-Ready App to the App Store

On the Mac make sure that Xcode is installed then start the Application Loader – in the screen shot below I typed “Application Loader” into the finder to locate the application.

submit_to_app_store_1

Enter your Apple ID and Password:

submit_to_app_store_2

Click “Choose” and browse for your Distribution-ready IPA file:

submit_to_app_store_3

Once your app is checked for conformance to submission rules you will see the “Deliver Your App” window. Click the “Next” button to submit the app.

submit_to_app_store_4

The “Deliver Your App” window appears where you can see the progress of your app while it is being submitted to the App Store. To view details of the process click the “Activity” button. Otherwise, just sit back and be patient.

submit_to_app_store_5

The deliver your app screen appears, click “next” to deliver your app. The process took about 2 or 3 minutes for my app to get through the submission process before I was finally greeted with this screen:

submit_to_app_store_6

Click the “Next” button to see the “Thank You” screen:

submit_to_app_store_7b

Submit Your App to the App Store

You must add a new app to the iTunes Connect Portal, including all the needed information, screen captures, icon art, etc before you can submit your app to the App Store. Once you have that done, then you can submit your app using the following steps.

  1. Log in to iTunes Connect.
  2. Click “My Apps”
  3. Click your app to view its details
  4. Click on “Prepare for Submission”
  5. Click “Build” to select your build
  6. Select the build from the list that appears
  7. Click the “Save” button
  8. Click “Submit for Review”

Preventing Webview Bounce and Keyboard Layout Shift

11 Apr 2015

Using libraries over frameworks means that there are few things that you have to do yourself. Preventing webview bounce – as can be seen in mobile Safari – is one of them. This is especially true since you are most likely using a scrolling library such as iScroll. Webview-bounce and iScroll don’t play well together. This issue turns out to be an easy one to solve. simply add this to your project:

document.ontouchmove = function(e){e.preventDefault()};

The next irritation for us web app developers is how the keyboard moves the screen around when it appears. It will move your layout to ensure that the focused input element is visible. The caveat is that iOS will not return the layout to its previous position if the user touches the layout to put away the keyboard instead of tapping the “hide keyboard” button. Even the “hide keyboard” button may not return the view to normal.

This is also easily solved, add this to your project:

var repositioner = null;
$('body').on('blur','input,area',function(){
  repositioner = setTimeout(function(){window.scrollTo(0,0;},200);
});
$('body').on('focus','input,area',function(){
  clearTimeout(repositioner);
});

Why the setTimeout? Well, lets say we only set a listener for the blur event. When an input element gains focus iOS will display the keyboard and shift the entire view up. With the keyboard still visible you could touch and thereby give focus to other input elements. But, giving focus to a different element blurs the previously focused element. This causes the view to bounce up on the blur event and then back down on the focus per what iOS feels like doing. Basically, the view bounces up and down as you touch form elements (this wont happen if you use the iOS keyboard’s built-in input tabbing).

By giving mobile Safari a 200 milisecond timeout we prevent the bouncing screen when touching input elements. Once the user puts away the keyboard the timer is quick and repositions your web app without a perceptible delay.

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!

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: