Submit app to Apple App Store

By: Ryan Wong at

There are lots of steps to submitting an app to the apple app storethat no one will tell you until you do it. Here’s a checklist of the submission process to save you time before submitting it.

Set your App ID

1.Open keychain access

2.Click keychain access -> Certificate assistant -> Request Certificate from a Certificate Authority

3.Set user email address, Common name(what you will call it), CA email is empty and select save to disk

4.Log into Apple developer account

5.Click Certificate Production -> create new app ID

6.Upload your certificate.certSigningRequest

7.Download your .cer file and double click it to import it into your keychain

Set up your Distribution Certificate

1.Login to apple developer account.

2.Click provisioning profile all.

3.Click add provision profile.

4.Check off App Store -> continue -> select app -> select production certificate

5.Name the profile

Building the app for production

1.Open xcode

2.Go to build setting

3.Go to code signing -> release -> select iphone distribution: name

4.Select provision profile -> Production provision

Submit to iTuneConnect

1.Log into iTuneConnect

2.Select my apps

3.Press the plus sign

4.Set name, version and sku

App Application Form

Before you start filling out this form make sure you have all the following:

Screenshots for 4-inch screen

You can take screenshots from your phone or resize it yourself. The sizes are below.Not optional.

1.640 x 1096 pixels for portrait (without status bar) minimum

2.640 x 1136 pixels for portrait (full screen) maximum

3.1136 x 600 pixels for landscape (without status bar) minimum

4.1136 x 640 pixels for landscape (full screen) minimum

Additional Screenshots for 4-inch screen

You can take screenshots from your phone or resize it yourself. The sizes are below. Not optional.

1.640 x 920 pixels for hi-res portrait (without status bar) minimum

2.640 x 960 pixels for hi-res portrait (full screen) maximum

3.960 x 600 pixels for hi-res landscape (without status bar) minimum

4.960 x 640 pixels for hi-res landscape (full screen) maximum

IPAD screenshot

If your app specifies iphone and ipad then you need these screenshots too.

1.1024 x 748 pixels for landscape (without status bar) minimum

2.1024 x 768 pixels for landscape (full screen) maximum

3.2048 x 1496 pixels for hi-res (without status bar) minimum

4.2048 x 1536 pixels for hi-res landscape (full screen) maximum

5.768 x 1004 pixels for portrait (without status bar) minimum

6.768 x 1024 pixels for portrait (full screen) maximum

7.1536 x 2008 pixels for hi-res portrait (without status bar) minimum

8.1536 x 2048 pixels for hi-res portrait (full screen) maximum

You can upload up to 5 screenshots per size.

Additional Fields

Here are the rest of the fields you need to fill in.Read through this list first before filling in the form to save time:

  • name

  • description

  • keyword (One or more keywords that describe your app. Keywords make App Store search results more accurate. Separate keywords with a comma.)

  • support url (A URL with support information for your app. This URL will be visible on the App Store.)

  • Marketing URL (A URL with marketing information about your app. This URL will be visible on the App Store.)

  • Privacy Policy URL (A URL that links to your organization’s privacy policy. Privacy policies are required for apps that are Made for Kids or offer auto-renewable In-App Purchases or free subscriptions. They are also required for apps with account registration, apps that access a user’s existing account, or as otherwise required by law. Privacy policies are recommended for apps that collect user- or device-related data.)

  • App Icon (This icon will be used on the App Store and must be in the JPG or PNG format, with a minimum resolution of at least 72 DPI, and in the RGB color space. It must not contain layers or rounded corners. 1024 x 1024 in size)

  • Version (The version number of the app you are adding. Numbering should follow software versioning conventions.)

  • Rating

  • Copyright (The name of the person or entity that owns the exclusive rights to your app, preceded by the year the rights were obtained (for example, “2008 Acme Inc.”). Do not provide a URL.)

  • Trade Representative Contact Information (You can provide additional information that will be displayed with your app on the Korean App Store. This information will only appear on the Korean App Store.Basically your address )

  • App Review Information (name, phone, email, notes)

  • Version Release (automatically upload or manual after approval)

Click Save.

Now go to xcode -> build -> archive -> submit to app store -> upload

Go back to App Application and select the build you uploaded. -> save for review

Custom Cordova Plugin

By: Ryan Wong at

I haved looked all over and theres no good complete guide to making a cordova plugin that’s not something trival like hello world or one liner android functions.

Step #1
1.Go to github and run the following:

1
git clone https://github.com/don/cordova-plugin-hello.git

2.Change the following in the plugin.xml

  • change plugin id to “com.company.pluginname”
  • change plugin name to your plugin name
  • change asset src to your javascript file in www
  • rename js-module hello src
  • in platform feature, rename Hello to the central java file where it delegates to other function
  • add source-file for each of your java files you will use. for source put “src/android/javafile.java” and target-dir=”src/com/companyname/plugin” even though it’s not physicaly there
  • if you are using external jar files in your project, here’s an example:
    1
    <source-file src="src/android/libs/mail.jar" target-dir="libs" />

3.Now I’ll explain how the www/hello.js file works.

1
2
3
greet: function (name, successCallback, errorCallback) {
cordova.exec(successCallback, errorCallback, "Hello", "greet", [name]);
},

The greet function is the name your javascript will call.

successCallback is the call back used if successful.

errorCallback is the call back used if unsuccessful.

name is a parameter that you pass in to this function. To be honest you can add more parameters. Then group all parameters into an array for cordova.

You can add more functions that one.

4.On to the Delegator java file you selected.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.company.plugin;

import org.apache.cordova.*;
import org.json.JSONArray;
import org.json.JSONException;
//add more imports if you want

public class Delegator extends CordovaPlugin {

@Override
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {

if (action.equals("greet")) {
String name = data.getString(0);
String message = "Hello, " + name;
callbackContext.success(message);
return true;
} else if (action.equals("retrieveEmails")){
String host = data.getString(0);
String port = data.getString(1);
String email = data.getString(2);
String password = data.getString(3);
//if (success){
// callbackContext.success(data);
//} else{
// callbackContext.error(some message);
//}
return true;
} else{
return false;
}
}
}

In this class you will use if statements to see which function was called in javascript and you can retrieve the parameters using the getString() function.

5.To test your plugin do the following:

1
2
3
4
cordova new  test com.company.test Test
cordova plugin add ../pluginfolder
cordova platform add android
cordova run android --device

Make sure you open the inspect devices tab in chrome to see how well it did.

#Notes about creating the plugin from the java side
When I was first given the task to make a phonegap plugin that doesn’t exist, I was freaking out that I had to learn android completely and then make this magical plugin but in reality it wasn’t too bad. From the many hours I wasted figuring out how to make the phonegap plugin, I found these tips useful especially if your a beginner at android.

1.Read up what android activity lifecycle is.
2.Read up what android intents are.
3.Create a new project in android with an existing activity.
4.When setting up android studio to work make sure you change gradle to the following settings to save you time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apply plugin: 'com.android.application'

android {
compileSdkVersion 19
buildToolsVersion "22.0.1"

defaultConfig {
applicationId "com.companyname.plugin"
minSdkVersion 19
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// compile 'com.android.support:appcompat-v7:21.0.3'
}

5.Then in the folder (com.companyname.plugin) create a Test.java and just put a basic java program below and run the code to make sure everything is setup:

1
2
3
4
5
6
7
package com.companyname.plugin;
//add import files you will use here
public class Test {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

6.Now just code what you want done in this class. If your not using any
device function then you don’t even need to run it in an emulator.
7.Once it’s correct copy the java files into the “src/android” folder along with libs you used.
8.At this point just add and remove the plugin to make adjustments and test on real phone
How this guide gives you a more complete picture.

Proper way to structure your node express application

By: Ryan Wong at

While researching the correct way to implement your api, no one gives a
precise way to structure your routes. So heres my take on this.

Say you have a route “/apiCall” and it returns a users profile.

Some changes I would make to this call:
1.I would rename it to “/api/v1/apiCall”. This way it’s very clear to the user that this is an api call and your using version 1 of this api. Also this allows you to depreciate old api calls safely allowing users to use a new version of api and the old.
2.call the api name something that has to do with their profile like “/api/v1/user/profile”.

#API Response
When you return an api response, you should always make use of the http status codes to make it clear to the user what went wrong. Doing this can make your api more robust and less code can be used.

Some common status codes I would use:

  • 200 (Successful call)
  • 304 (Not Modified) If the api response has not changed send cache version
  • 400 (Bad Request) If there was some validation error
  • 401 (Unauthorized Access) If user does not have authorization to make this call
  • 402 (Payment Required) payment is needed to make this call
  • 403 (Forbidden) Not allowed to access at all
  • 404 (Not Found) api route does not exist
  • 405 (Method not allowed)
  • 406 (Not Acceptable)
  • 407 (Proxy Authorization Required)
  • 408 (Request Timeout)
  • 410 (Gone)
  • 411 (Length Required) Content Type length required
  • 500 (Internal Server Error) Unknown error

#Structuring each Status error
To make use of all these status codes, I made an error object for each and return the error object with the next() callback.

##Error object

1
2
3
4
5
6
7
8
9
10
11
12
13
function UnauthorizedAccessError (code, error) {
Error.call(this, error.message);
Error.captureStackTrace(this, this.constructor);
this.name = "UnauthorizedAccessError";
this.message = error.message;
this.code = code;
this.status = 401;
this.inner = error;
}

UnauthorizedAccessError.prototype = Object.create(Error.prototype);
UnauthorizedAccessError.prototype.constructor = UnauthorizedAccessError;
module.exports = UnauthorizedAccessError;

##General Error handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
exports.errorHandleCode = function (err, req, res, next) {
var errorType = typeof err,
code = 500,
msg = { message: "Internal Server Error" };
console.log(err.name);
switch (err.name) {
case "UnauthorizedError":
code = err.status;
msg = undefined;
break;
case "BadRequestError":
case "UnauthorizedAccessError":
case "NotFoundError":
case "ValidationError":
//more error types ...
code = err.status;
msg = err.inner;
break;
default:
break;
}
return res.status(code).json(msg);
};

//In app.js file put this at end of file
app.use(errors.errorHandleCode);

#Api Method call
Now we put the whole api call together.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.post('/api/v1/user/profile', function(req, res, next){
//validation error
if (validationError){
return next(new ValidationError('400', validation.validationHandler()));
}
//find user
//var user = userFound;
if (!user){
return next(new UnauthorizedAccessError('401',{
message: "Unauthorized Access"
}));
}
//something serious went wrong
if (somethingSerious went wrong){
return next(new Error('Something serious went wrong'));
}
});

How to implement laravel cronjobs properly

By: Ryan Wong at

Here’s a full step by step guide to creating laravel cronjob.

1.Run the following in your terminal

1
p artisan command:make cronjobname

2.Find the cronjob file in app/command folder.
3.Change the description name in the file.

1
2
protected $name = 'command:name';
protected $description = 'Command description.';

4.Add your php code to perform in the run fire().
5.Open app/start/artisan.php file, and add one line as below:

1
Artisan::add(new cronjob);

6.Open your crontab with “env EDITOR=nano crontab -e”.
6.Type in the following:

1
*/5 * * * * /usr/bin/php /path/tp/app/artisan cronjobname

7.Press ctrl + x
8.Cron job is now enabled.

#Cron schedule dismysterified

  • anything */# means every # minutes
    1
    2
    3
    4
    5
    6
    7
    8
    * * * * * *
    | | | | | |
    | | | | | +-- Year (range: 1900-3000)
    | | | | +---- Day of the Week (range: 1-7, 1 standing for Monday)
    | | | +------ Month of the Year (range: 1-12)
    | | +-------- Day of the Month (range: 1-31)
    | +---------- Hour (range: 0-23)
    +------------ Minute (range: 0-59)

Hope this helps you out.

Posting forms with angular to laravel

By: Ryan Wong at

By default, angular submits form data as a serialized json to the server.

However, laravel is expecting form-url-encoded data.

To fix this add the following to angular:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
.config(function($httpProvider){
// Use x-www-form-urlencoded Content-Type
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';

/**
* The workhorse; converts an object to x-www-form-urlencoded serialization.
* @param {Object} obj
* @return {String}
*/

var param = function(obj) {
var query = '', name, value, fullSubName, subName, subValue, innerObj, i;

for(name in obj) {
value = obj[name];

if(value instanceof Array) {
for(i=0; i<value.length; ++i) {
subValue = value[i];
fullSubName = name + '[' + i + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
}
else if(value instanceof Object) {
for(subName in value) {
subValue = value[subName];
fullSubName = name + '[' + subName + ']';
innerObj = {};
innerObj[fullSubName] = subValue;
query += param(innerObj) + '&';
}
}
else if(value !== undefined && value !== null)
query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
}

return query.length ? query.substr(0, query.length - 1) : query;
};

// Override $http service's default transformRequest
$httpProvider.defaults.transformRequest = [function(data) {
return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
}];

});

Hope this helps you out. Wasted a lot of time figuring why it didn’t post properly.

ios swipe left back function shows a blank page in ionic

By: Ryan Wong at

When using the back function in android, everything is fine but it shows a blank screen in ios.

This blank screen shows up because you didn’t cache that page in your stateProvider. So if you did “cache:false” it will show a blank page.

To fix this, add the following code in your app.js file:

1
2
3
.config(function($ionicConfigProvider) {
$ionicConfigProvider.views.swipeBackEnabled(false);
})

Hope this helps you out. I lost alot of time trying to fix this. I tried to change how the back button worked in xcode. Nothing sticked.

cordovaOauth Could not find InAppBrowser plugin error

By: Ryan Wong at

If you get the error “cordovaOauth Could not find InAppBrowser plugin error” then your cordova package is wrong.

Do the following:

1
2
3
cordova plugin rm org.apache.cordova.inappbrowser

cordova plugin add cordova-plugin-inappbrowser

Then go into ng-cordova.js and replace all instances of “org.apache.cordova.inappbrowser” with “cordova-plugin-inappbrowser”.

Make sure you test on a real phone, simulator will fail since the livereload will mess it up.

Hope this helps you out.