Submitting to the iOS App Store

From NSB App Studio
Jump to: navigation, search

Note: Until these steps are performed, PhoneGap Build will return 'error' for iOS.

Preparing your App

In this TechNote, we'll cover what you need to do to submit your app to Apple's iOS App Store.

Apple is concerned that apps in its store work as described and do not cause any security risk. They also care that the apps provide some unique functionality and follow their Human Interface Guidelines. The results have been good: Apps in the iOS App Store are usually pretty good, and the store is now one of the largest software resellers on the planet.

The method used will be to take an AppStudio app, use PhoneGap Build to produce a .ipa file, then submit that to Apple.

You can use this procedure for any AppStudio project. It will also work if your project uses the PhoneGap API. It will not work if you are using third party PhoneGap plug ins which are not supported by PhoneGap Build: for those, you will need to use the PhoneGap CLI method.

Start by testing your app as much as possible while it is still a web app. It will be much easier to make changes, fixes and improvements at this stage.

Also, have a look at this list of common reasons apps are rejected: https://developer.apple.com/app-store/review/rejections/

Get Signing Certificates

For security purposes, Apple has a strong signing process. This involves certificates which identify the developer, which need to be included in the build process. Apple issues these certificates as part of its developer program.

Apple has good instructions on its website about its policies, procedures and how to get the certificates. This is probably the most complex and confusing part of submitting an app, so it's good to pay attention carefully. Don't be discouraged: hundreds of thousands of developers have gone through it successfully.

You will need a Mac to do this and have XCode installed. You can download XCode from the iOS Developer Center.

1. Sign up for an iOS Developer Account

Enroll as enroll as an iOS Developer to submit apps to the App Store. It costs $99.00 USD per year.

2. Set up your information in the iOS Provisioning Portal

Sign on using your Apple ID and go into the Certificates, Identifiers & Profiles. You'll see tabs under iOS. Your account needs to have admin privileges.

These are the important ones for AppStudio developers:

Certificates

The Development certificate identifies you and your development computer. Start by clicking on the Plus (+) button to add a new certificate.

Choose iOS App Development Certificate.

Instructions will then display telling you how to create a CSR file. Go ahead and do it. The next screen uploads the CSR file - do that as well and click Generate. It will take a few minutes to generate the file. You can do a refresh of your browser to see the status of the request.

Once the file is generated, Apple's website will instruct you to download the certificate. Double click on it to install it in your keychain.

App ID

Next, choose App IDs in the Identifiers section. Click on the Plus (+) to add a new App ID.

For App ID Description, enter the short name of your app. You will want to use this consistently.

Don't check anything in App Services for now.

For App ID Suffix, choose Explicit App ID. The Bundle ID should be your domain name (in reverse) followed by your app name.

 com.nsbasic.sampleapp

If everything is correct, Submit it.

Devices

Next, set up the list of devices you will be using. This list will be built into the Provisioning Profile. Your test build will only run on the devices listed. You can list up to 100 devices. They can be iPod, iPads or iPhones.

It will ask you to Assign a new Apple Device. This is the device you will use for your testing. You will need to enter a description on the device ("Bill's iPhone 5") and the Device ID. Get the Device ID (UDID) from iTunes with the device plugged in. In the Summary screen, click on "Serial Number" and it will change to "Identifier (UDID)". Right click on it to copy it and paste it into the Devices screen.

Provisioning Profiles

A Provisioning Profile is a file which tells iTunes which devices your app may be installed on. A Distribution Provisioning File is used for the App Store. Use a Development Provisioning File for testing.

To create one, go into Provisioning Profiles and click on the plus ("+") button. Choose iOS App Development for testing. Your App ID should show up on the next screen - click on Continue. Next, select your Certificate, then the devices to be included (there is a handy Select All button). Finally, give the profile a name like sampleapp.development. The Provisioning Profile is then created and you can download it.

PhoneGap Build

PhoneGap Build is a web service which takes your project and returns a file which can be uploaded to the App Store.

To use it, you will need to create your own account with them. They have both a free and a paid plan. For a single project, PhoneGap Build is free.

Once you have the account, enter its name and password into Preferences.

Config.xml

One of the project properties is named configxml. This is used to create the config.xml file which is passed to PhoneGap Build. It contains much of the information that PhoneGap Build needs to create your app. The default configxml file will work, though you may wish to do some customization. It's worth consulting PhoneGap's config.xml documentation to see all the options.

Here is a sample config.xml (subject to change):

<?xml version="1.0" encoding="UTF-8"?>
<widget 
xmlns = "https://www.w3.org/ns/widgets"
xmlns:gap = "http://phonegap.com/ns/1.0"
id = "com.nsbasic.{id}"
versionCode = "1"
version = "{version}">

<name>{title}</name>
<description>{description}</description>
<preference name="phonegap-version" value="{phoneGapVersion}" />

<!-- Icons -->
<icon src='{icon}' />

<platform name = 'android'>
    <icon src='icons/android/ldpi.png' platform='android' qualifier='ldpi' />
    <icon src='icons/android/mdpi.png' platform='android' qualifier='mdpi' />
    <icon src='icons/android/hdpi.png' platform='android' qualifier='hdpi' />
    <icon src='icons/android/xhdpi.png' platform='android' qualifier='xhdpi' />
    <icon src='icons/android/xxhdpi.png' platform='android' qualifier='xxhdpi' />
</platform>

<platform name = 'ios'>
    <icon src='icons/ios/icon-40.png' platform='ios' width='40' height='40' />
    <icon src='icons/ios/icon-40@2x.png' platform='ios' width='80' height='80' />
    <icon src='icons/ios/icon-50.png' platform='ios' width='50' height='50' />
    <icon src='icons/ios/icon-50@2x.png' platform='ios' width='100' height='100' />
    <icon src='icons/ios/icon-60.png' platform='ios' width='60' height='60' />
    <icon src='icons/ios/icon-60@2x.png' platform='ios' width='120' height='120' />
    <icon src='icons/ios/icon-60@3x.png' platform='ios' width='180' height='180' />
    <icon src='icons/ios/icon-72@.png' platform='ios' width='72' height='72' />
    <icon src='icons/ios/icon-72@2x.png' platform='ios' width='144' height='144' />
    <icon src='icons/ios/icon-72@3x.png' platform='ios' width='216' height='216' />
    <icon src='icons/ios/icon-76.png' platform='ios' width='76' height='76' />
    <icon src='icons/ios/icon-76@2x.png' platform='ios' width='152' height='152' />
    <icon src='icons/ios/icon-small.png' platform='ios' width='29' height='29' />
    <icon src='icons/ios/icon-small@2x.png' platform='ios' width='58' height='58' />
    <icon src='icons/ios/icon.png' platform='ios' width='57' height='57' />
    <icon src='icons/ios/icon@2x.png' platform='ios' width='114' height='114' />
</platform>

<!-- Splash Screens -->
<preference name='SplashScreenDelay' value='2000' />
<preference name='AutoHideSplashScreen' value='true' />
<gap:splash src='{splashscreen}'/>
<gap:plugin name='cordova-plugin-splashscreen' source='npm' />

<platform name='android'>
    <splash src='splash/android/res-long-land-hdpi/splash.png' qualifier='long-land-hdpi' />
    <splash src='splash/android/res-long-land-ldpi/splash.png' qualifier='long-land-ldpi' />
    <splash src='splash/android/res-long-land-mdpi/splash.png' qualifier='long-land-mdpi' />
    <splash src='splash/android/res-long-land-xhdpi/splash.png' qualifier='long-land-xhdpi' />
    <splash src='splash/android/res-long-land-xxhdpi/splash.png' qualifier='long-land-xxhdpi' />
    <splash src='splash/android/res-long-land-xxxhdpi/splash.png' qualifier='long-land-xxxhdpi' />
    <splash src='splash/android/res-long-port-hdpi/splash.png' qualifier='long-port-hdpi' />
    <splash src='splash/android/res-long-port-ldpi/splash.png' qualifier='long-port-ldpi' />
    <splash src='splash/android/res-long-port-mdpi/splash.png' qualifier='long-port-mdpi' />
    <splash src='splash/android/res-long-port-xhdpi/splash.png' qualifier='long-port-xhdpi' />
    <splash src='splash/android/res-long-port-xxhdpi/splash.png' qualifier='long-port-xxhdpi' />
    <splash src='splash/android/res-long-port-xxxhdpi/splash.png' qualifier='long-port-xxxhdpi' />
    <splash src='splash/android/res-notlong-land-hdpi/splash.png' qualifier='notlong-land-hdpi' />
    <splash src='splash/android/res-notlong-land-ldpi/splash.png' qualifier='notlong-land-ldpi' />
    <splash src='splash/android/res-notlong-land-mdpi/splash.png' qualifier='notlong-land-mdpi' />
    <splash src='splash/android/res-notlong-land-xhdpi/splash.png' qualifier='notlong-land-xhdpi' />
    <splash src='splash/android/res-notlong-land-xxhdpi/splash.png' qualifier='notlong-land-xxhdpi' />
    <splash src='splash/android/res-notlong-land-xxxhdpi/splash.png' qualifier='notlong-land-xxxhdpi' />
    <splash src='splash/android/res-notlong-port-hdpi/splash.png' qualifier='notlong-port-hdpi' />
    <splash src='splash/android/res-notlong-port-ldpi/splash.png' qualifier='notlong-port-ldpi' />
    <splash src='splash/android/res-notlong-port-mdpi/splash.png' qualifier='notlong-port-mdpi' />
    <splash src='splash/android/res-notlong-port-xhdpi/splash.png' qualifier='notlong-port-xhdpi' />
    <splash src='splash/android/res-notlong-port-xxhdpi/splash.png' qualifier='notlong-port-xxhdpi' />
    <splash src='splash/android/res-notlong-port-xxxhdpi/splash.png' qualifier='notlong-port-xxxhdpi' />
</platform>

<platform name = 'ios'>
  <splash src='splash/ios/Default.png' width='320' height='480' />
  <splash src='splash/ios/Default@2x.png' width='640' height='960' />
  <splash src='splash/ios/Default-568h@2x.png' width='640' height='1136' />
  <splash src='splash/ios/Default-667h@2x.png' width='750' height='1334' />
  <splash src='splash/ios/Default-Portrait.png' width='768' height='1024' />
  <splash src='splash/ios/Default-Landscape.png' width='1024' height='768' />
  <splash src='splash/ios/Default-Portrait@2x.png' width='1536' height='2048' />
  <splash src='splash/ios/Default-Landscape@2x.png' width='2048' height='1536' />
  <splash src='splash/ios/Default-Portrait-736h@3x.png' width='1242' height='2208' />
  <splash src='splash/ios/Default-Landscape-736h@3x.png' width='2208' height='1242' />
</platform>

<preference name="permissions" value="none"/>
<!-- sample preference specifications -->
<!-- <preference name="autorotate" value="false" readonly="true"/> -->
<!-- <preference name="orientation" value="default" /> -->
<!-- <preference name="fullscreen" value="true" /> -->

<!-- Platforms: Customize as needed. -->
<gap:platforms>
   <gap:platform name="android" />
   <gap:platform name="ios" />
   <gap:platform name="winphone" />
</gap:platforms>

<plugin name="cordova-plugin-camera" source="npm"  />
<plugin name="cordova-plugin-device" source="npm" />
<plugin name="cordova-plugin-contacts" source="npm" />
<plugin name="phonegap-plugin-barcodescanner" source="npm" />

<!-- Requires AppStudio 6. -->
<plugin name="cordova-plugin-statusbar" source="npm" />
  <preference name="StatusBarOverlaysWebView" value="{phoneGapStatusBarOverlay}" />
  <preference name="StatusBarBackgroundColor" value="{phoneGapStatusBarColor}" />
  <preference name="StatusBarStyle" value="{phoneGapStatusBarStyle}" />

<plugin name="cordova-plugin-whitelist" source="npm" />
  <allow-navigation href="*" />
  <access origin="*" />
</widget>

Fields which are surrounded by brackets, like {version}, are automatically filled in by AppStudio from the other information in the project.

==== iOS Icons ====

A full set of icons will include the following square images, in png or jpg format. For example,
29.png is a 29x29 png format image. The default icon file must be called icon.png and the default splash
screen file must be called splash.png.

<pre>
29.png
48.png
57.png
58.png
64.png
72.png
76.png
114.png
120.png
144.png
152.png
320.png
1024.png

Here is which ones need to be included in your build. This goes into config.xml: Note how the icons are organized in an icon folder.

<icon src="icons/ios/57.png" gap:platform="ios" width="57" height="57" />
<icon src="icons/ios/72.png" gap:platform="ios" width="72" height="72" />
<icon src="icons/ios/114.png" gap:platform="ios" width="114" height="114" />
<icon src="icons/ios/144.png" gap:platform="ios" width="144" height="144" />

Set these files up in an folder, structured like this:

icons
    ios
        57.png
        72.png
        114.png
        144.png

Add the folder name, icons, to manifest in Project Properties.

If you do not supply an icon, the default AppStudio icon will be shown.

iOS Splash Screens

See SplashScreens for information on setting up splash screens for your app.

Add the folder name, splash, to manifest in Project Properties.

According to Apple's application guidelines, a tablet (iPad) application should not hide the status bar while a handheld (iPhone) application should hide status bar.

The above dimensions are assuming that you have chosen to follow this recommendation. If you choose a different path, then you should increase or decrease the image height based on the size of the status bar.

An iPhone app should include one launch image in portrait orientation; an iPad app should include one launch image in portrait orientation and one launch image in landscape orientation.

If you do not supply a splash screen, the default PhoneGap splash screen will be shown.

Signing Keys

To properly build for the App Store, PhoneGap Build needs the Certificate and Provisioning file. To set them up, sign into PhoneGap Build using your ID, then go into Edit Settings. Choose the Signing Keys tab.

Signingkey.png

For Title, enter the name of your app.

For Certificate, select your .p12 file. To get the .p12 file, go into Keychain Access on your Mac and do the following:

Keychain-export.png

Before it exports the .p12 file, it will ask you to provide a password. Remember this: PhoneGap will need the password to unlock the certificate.

For Provisioning Profile, select your .mobileprovision file.

The settings screen will display a lock icon beside your newly created key. Click on the lock, and it will ask you for the certificate password. This unlock will expire, so you will need to unlock it again from time to time.

Doing an Adhoc Build

An AdHoc Build can only be run on designated devices. It is designed for testing, not for release.

  1. In AppStudio, choose "Build Native App with PhoneGap". This will upload your app to PhoneGap's servers so it can be packaged.
  2. Log into PhoneGap Build and choose your app. It should have an error box beside the iOS build status. The problem is that the signing key hasn't been chosen yet. Do so from the picker that appears.
  3. You may see a 'locked' icon as well. If so, click on it. It will request your certificate password and unlock it for one hour.
  4. Choose 'Rebuild' and wait a minute or two. The ipa file should now be generated: you can download it.
  5. To run the ipa file on your device, drag it into iTunes and sync. It should be installed and appear on the Home screen.
  6. If the app is already installed on the device, you will have to delete it from the device before syncing the app.

Missing info.plist key

contributed by gonzomoco

If you get this message:

Missing Info.plist key - This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSContactsUsageDescription key with a string value explaining to the user how the app uses this data.

Inside the config.xml on the AppStudio project you need to modify the PhoneGap configxml:

<plugin name="phonegap-plugin-barcodescanner" source="npm" />

Should be changed to:

<gap:plugin name="phonegap-plugin-barcodescanner" source="npm">
  <variable name="CAMERA_USAGE_DESCRIPTION" value="Scanning Bar Codes" />
</gap:plugin>

I think that the complete list of permissions that you might need depending the added phonegap plugins are described in the following page: https://iosdevcenters.blogspot.com/2016/09/infoplist-privacy-settings-in-ios-10.html

Submitting your App

Set up your app in iTunes Connect

iTunes Connect manages your apps in the App Store. There are a number of steps that need to be done in it before an app can be submitted.

Contracts, Tax and Banking

There are a number of contracts that need to be agreed to before you can submit. If you want to sell your app, you also need to supply banking information so payments can be direct deposited. Tax documents may also need to be filed.

Some of this paperwork cannot be done electronically. Expect to spend up to a month completing all of this if you are selling your app.

Other than that, the procedure is straight forward: follow the process on the website.

Manage Your Apps

Use Manage Your Apps to add, view, and manage your App Store apps.

Before adding an app, collect the information you will need for it. Some of the fields you need to enter can only be set up when adding a new app: they cannot be changed later. Other fields can be changed later, but will trigger a review of your app. There are helpful prompt icons beside each field to help you with the input.

For more information on setting up fields, read "Adding New Apps".

The Bundle ID Suffix should contain your company name and the app name:

com.nsbasic.Project1

This needs to match the id field in the configxml project property. If the project's save file name is Project1, it will look like this:

id = "com.nsbasic.{id}"

Get Ready for Submission

  1. From your Version Details page, click the Ready to Upload Binary button.
  2. Follow the instruction on the screen.
  3. When complete, you will be able to upload the binary using the Application Loader.
  4. If you want to upload again, you will need to repeat these steps.

Upload to the App Store

Get the Application Loader

Before you can upload, you need the Application Loader. You will only need to do this the first time.

  1. Log in to http://itunesconnect.apple.com
  2. Go to "Manage Your Applications"
  3. Use "Download Application Loader" link at the bottom of the page to download, then install.
  4. Alternatively, download from https://itunesconnect.apple.com/apploader/ApplicationLoader_3.0.dmg

Upload your app

  1. Start the Application Loader
  2. Log on with your Apple ID.
  3. Click on Deliver Your App. The Application Loader will check with iTunes Connect to see if any apps are "Waiting for Upload", and display a list. Select the correct one.
  4. Select the ipa file on your system when it asks you for it.
  5. Application Loader with then perform a series of tests on the file. If it passes, the file will get uploaded to iTunes Connect.
  6. The status in iTunes Connect will change to "Waiting for Review".
  7. A week or so later, expect an email from Apple either saying the app is approved or with a list of issues to correct. The exact time varies: you can check the current wait times here: http://appreviewtimes.com