Thursday, March 30, 2017

ios cordova CDVLaunchScreen storyboard and splash screen plugin

When creating an ios cordova project with cordova (6.5.0), the xcode project generated by
cordova create
command will include a resource file "CDVLaunchScreen.storyboard". This storyboard has a image view in it, which refers to image resource "LaunchStoryboard", however by default, there is no image resource included in image.xcassets. Besides, the CDVLaunchScreen.storyboard is not referred or used in any places in the generated xcode project.

As a result, the cordova ios project still uses the old launchImage resource to display the app starting
splash screen as shown below


Usually, this does not matter for the application. However, if running the project on ipad pro device's UIWebView, it will shows the useragent is iphone instead of ipad. To demo this issue, update index.html and index.js with below changes and then run it on ipad pro device, the screen shot shows the user agent is IPHONE

Index.html
 <body>
        <div class="app">
            <h1>Apache Cordova</h1>
            <label id="useragent"></label>
            <div id="deviceready" class="blink">
                <p class="event listening">Connecting to Device</p>
                <p class="event received">Device is Ready</p>
            </div>
        </div>
        <script type="text/javascript" src="cordova.js"></script>
        <script type="text/javascript" src="js/index.js"></script>

    </body>

Index.js
   // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');
        document.getElementById('useragent').innerHTML = navigator.userAgent;
        console.log('Received Event: ' + id);
    }



One way to workaround the usage agent issue is from the xcode project's General setting page, set "Launch Screen File" to "CDVLaunchScreen.storyboard".  Even if no image resource is available, the user agent for ipad pro will show the correct value as below


Note in the xcode project, before the "Launch Screen File" is set to "CDVLaunchScreen.storyboard", if the application is already deployed to the ipad pro device, then after the configuration change, the useragent will still show as IPHONE in the testing. The reason is cordova stores the useragent into ios UserDefault, so it will load the saved value as webView's useragent. 

In order to update the user agent after changing the "Launch Screen File" setting, the bundle version needs to be updated. In the project's General settings screen, the Identitiy Build number (CFBundleVersion in info.plist file) needs to be set to a new version to reset the saved user agent value. After the version is updated, the project will show the right ipad user agent on ipad pro device.

Cordova Splash Screen plugin
Although manually updating the xcode project setting, and assign the launch image resource for LaunchStoryboard image view is fine, the same function is also available in cordova ios splash screen plugin:
https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-splashscreen/

Basically, in cordova config.xml, under platform ios section, you will need to add the below splash items
    <platform name="ios">
        <allow-intent href="itms:*" />
        <allow-intent href="itms-apps:*" />
        <splash src="res/screen/ios/Default@2x~universal~anyany.png" />
        <splash src="res/screen/ios/Default@2x~universal~comany.png" />
        <splash src="res/screen/ios/Default@2x~universal~comcom.png" />
        <splash src="res/screen/ios/Default@3x~universal~anyany.png" />
        <splash src="res/screen/ios/Default@3x~universal~anycom.png" />
        <splash src="res/screen/ios/Default@3x~universal~comany.png" />
    </platform>

And then add those image resource png files into projectRoot/res/screen/ios folder, the image size should follow the below table

filename size width height
Default@2x~universal~anyany.png 2732x2732 any any
Default@2x~universal~comany.png 1278x2732 com any
Default@2x~universal~comcom.png 1334x750 com com
Default@3x~universal~anyany.png 2208x2208 any any
Default@3x~universal~anycom.png 2208x1242 any com
Default@3x~universal~comany.png 1242x2208 com any

Once the resource is added and config.xml is updated, then add the splash screen plugin into the cordova project. You will also need to run 
cordova prepare 
to make the CDVLaunchScreen.storyboard set to the "Launch Screen File" field, you can verify this is set properly by checking the xcode project's genearl settings.

With this change, the ipad pro UIWebView should show its user agent properly as ipad.

For more information, please refer to cordova splash screen pluguin at

Build jar file using java sdk 1.7 on mac for android studio library

Currently by default, java SDK 1.8 will be installed on mac for compile java code. However, a jar file compiled using java sdk 1.8 cannot be used by Android studio (2.3) as library project. The android project will build without error, but it will fail when trying to debug the app on device or simulator. Set SourceCompatiblity Setting in app's build.grade file also does not help.

One way to solve the issue is building the jar file with java sdk 1.7.

To do so, first download Java SE sdk 1.7 from oracle web site, and then install it on your mac. You do not need to first uninstall Java 1.8 from your mac, the new java 1.7 will be installed on the mac, but it will not remove or change any settings used by the current installed java sdk 1.8.

After the installation is done, you can check there should have two sub folders under
/Library/Java/JavaVirtualMachines   
One for 1.8 and one for 1.7.

Now you need a way to switch the java sdk version from terminal app to build the java source code. The easy way to do so is add the below two lines in your .bash_profile
alias setJdk7='export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)'
alias setJdk8='export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)'

After saving the change in bash_profile, then you can switch the java sdk version by typing the below command from terminal app:

setJdk7
set java sdk to 1.7

sdkJdk8
set java sdk to 1.8

With the above change, you can rebuild the jar file using jdk 1.7 and then import the jar file into the android studio, it should be able to build and run on device and simulator without problem.

Note: when adding new jar file as a module in android studio project, the settings.gradle will be updated to include the jar file. you will need manually add the new module in app's build.gradle's dependency section.

Sunday, March 26, 2017

Upgrade to Android Studio 2.3 on mac may fail to open existing or create new android projects

Just upgrade Android studio 2.3 on mac, and after that, the android studio cannot open the old android project or create new android project. Basically it only recognizes those android mobile projects as java desktop projects

The reason is somehow, during the upgrading, the android Support was disabled in android studio Preferences's Plugin settings. You will need to manually enable it from Preferences' Plugins screen, and then restart the android studio, and follow the prompt.



Hopefully the other functions in Android Studio 2.3 are not as fragile as this.

Friday, March 17, 2017

Steps to localize ios app in xcode including storyboard, plist file, code

In xcode 8, the internationalization setting is in the project (not target)'s info settings, which is shown below.


Step 1: localize string in storyboard
1. Be sure the "Use Base Internationalization" checkbox is checked
2. Check the storyboard to delete all unused string, otherwise they will appear in the localized string files
3. click the language + button to add a new language, like Chinese, which will create new string file for the added language under the storyboard folder. This file contains all the strings used in the storyboard. Translate the string to the proper language.
4. Change the device language to the new language, and restart the app, the localized string should show in the storyboard UI elements


Step 2: localize strings in plist file
The second step is localized the application display name as well as other strings defined in plist file
1. from the xcode's File->New->File, select Resource tab, and create a string file, the file name is
InfoPlist, (the full file name is InfoPlist.strings)
2. select the InfoPlist file, and in xcode identity inspector window, click localization button, and then check the language you want to localize for the plist file
3. Open the localized InfoPlist file as property list, and add the key value pairs you want to localized, the key must match the keys defined in the original info.plist file, for example, to localize the display bundle name, add the below item
Note you only need to add the key/value pairs in localized InfoPlist file, and there is no need to make any change in InfoPlist file in base.lproj folder, as if the value cannot be found in there, it will use the value defined in the info.plist file.
Likely, only bundle display name and feature usage permission description (like NSCameraUsageDescription etc.) need to be localized in info plist file
After the display bundle name is changed, test it on device, the application name shown in the device home screen should have the localized name.

Step 3: localize hardcoded string in source file
1. go through the swift source files and identify all the strings that will appear in the UI, replace the hardcoded string by calling NSLocalizedString method with any comment  as below
self.navigationItem.title = "New Password"
to
self.navigationItem.title = NSLocalizedString("New Password", comment: "New Password screen title")

The comment parameter does not matter, and it will only show as comment in the string file generated in the last step. Actually, just set it to empty string "".

2. from terminal app, goto the application project folder, and run the below command:
find . -name \*.swift | xargs genstrings -o .
This will generate a file localizable.strings under this folder
3. add the generated file into xcode project, 
4. select the file from xcode identity inspector, and click localization button to localize it for different language
5. translate the localized files for all the keys
6. test the app under different language and verify the ui is updated to different language settings

Saturday, March 11, 2017

Steps to convert ios UITableViewController to UIViewController

UITableViewController is easy to create, but hard to change. It cannot add any additional view, such as toolbar or Google AdMob view. It is best practice to not use UITableViewController at all from the very beginning.

For existing UITableViewController, the below steps can be used to convert it to UIViewController with a UITableView

1. open storyboard, and add a new UIViewController,
2. select the old tableview object, and press option button and drag-drop it to the new viewcontroller. This will copy the tableview to the new UIViewController
3. Optionally, if the new UIViewController belongs to a navigation controller, then after creating the segua, then add a navigation item to the new UIViewController to customize the viewcontroller title, and then add bar button item to customize the left and right navigation bar button item
4. In the original uitableviewcontroller source file, in the class definition,
replace 
UITableViewController 
with
UIViewControllerUITableViewDataSource, UITableViewDelegate
5. go back to storyboard editor, and change the new UIViewController's class to your class name
6. copy the storyboard id and restoration id to the new UIViewController
7. drag drop the tableview to your viewcontroller source file to create a outlet for the tableview, and name the outlet to tableView, so it has the same name as UITableViewController.
8. From storyboard, select the UIViewController tableview, and then open connection inspector, delete the datasource and delegate outlets. And then create a new outlet to the new UIViewController
9. check other action and outlet from the old UITableViewController storyboard and create the same one on the new UIViewController
10. In the UIViewController source file, if UITableViewController only uses dynamic cell, then simply fix the compile error by deleting the override keyword in the related function.
If the original UITableViewController uses static cell in storyboard, then it is more complex. First changing the storyboard cell to dynamic cell, and then creating the derived table cell class for each table cell type. Then implementing the datasource delegate method to provide the row and section information, finally, when creating each tablecell row, associate the table cell with the existing outlet variable.
11. build and run the project to verify the function

Steps to replace apple iAd to google adMob in xcode project

Since apple stops support of iAd, so existing ios apps published in app store need to be updated to using google adMob to earn ad income.

The below steps are how to make the switch:
1. open xcode project remove iAd framework and any related code
2. install cocoapod on mac if not yet installed before
3. register to google adMob account at
https://apps.admob.com/#home
4. from adMob home screen, select Mobilize tab, and click the "mobilize new app" button. Then select the app store app you already published and create an adMob app.
5. In step 3, create a google firebase project.  it will give you a link to download the googleservice-info.plist file. The config file is also available in firebase web page at
https://firebase.google.com
6. cd into xcode project and create pod file as below and be sure to update the target to the right one
source 'https://github.com/CocoaPods/Specs.git
platform :ios, '7.0'

target 'YourProjectTarget' do

  use_frameworks!
  pod 'Firebase/Core'
  pod 'Firebase/AdMob'

end
7. run pod update to add the firebase framework into your xcode project
8. open the newly created xcode xcworkspace from xcode
9. add the previous step 5's plist file into the project
10. update xcode project storyboard to add ad view as described at
https://firebase.google.com/docs/admob/ios/quick-start
Basically, you will need to add UIView in the viewcontroller, and put the view at the bottom of the viewcontroller, and then update the viewcontroller code to made request to server to get the ads
11. To avoid the compile error, add the below import to the related source file

import GoogleMobileAds
import UIKit
12 In case you get a wired error of fail to run the app on real ios device, try http://jonathanblog2000.blogspot.ca/2017/03/ios-xcode-fix-framework-not-load-with.html
13. check appdelegate.swift to have the right admob appid, and all view controller to have right ad unit id.

After a while, when you build the ios project, you may get a warning of
"Please consider updating your SDK to the most recent SDK version to get the latest features and bug fixes."
In order to update the google sdk version, from mac terminal, goto the project folder and run
pod update
The above command will update google sdk to the latest version, and avoid the version update warning.

Wednesday, March 8, 2017

ios & xcode: Fix framework not load with error of "code signing blocked mmap()"

When running ios project on ios real device, the app may crash with an error of "code signing blocked mmap()". Although the same project can run properly on simulator.

After some testing, figure out the issue is related to the Apple Signing certificate. To fix the issue, first delete all Apple signing cert from keychain. Then open Xcode preference/Account, select the Apple ID, and the right team. Click view details, and reset button for ios development in signing identity section.

This is the only method that fixes the error in my test.

Tuesday, March 7, 2017

Avoid the annoying step to trust enterprise apple dev certificate each time after installing a new app

A recent change from Apple requires developer to trust the enterprise developer certificate each time when installing and run a new app on ios device.

Basically, when a new app is installed on device signed with enterprise dev cert, developer has to go to device settings\general\profile page to trust the cert first before able to debug or run the the app. Once the app is deleted and installed again, the dev has to repeat the same trust step again.

One way to avoid this is explicitly install the dev cert on device:
1. open xocde preference menu, select Account tab, and select the appid for enterprise dev account
2. from the team section select the enterprise team
3. click view detail button.
4. in the detailed dialog box signing identigty section, select "ios development" item. right click it and export
5. send an email with the exported dev certificate to your ios device, and install the cert on your ios device.
6. The enterprise dev cert will be trusted and no need to explicitly trust it again anymore.

Wonder why apple could not add a "DO NOT BOTHER" button on the cert trust page.

Friday, March 3, 2017

Set wifi proxy bypass list on android device

Android device does not have a good support on proxy auto config, so it is necessary to manually set the proxy settings.

Host:
proxy.phl.sap.corp

port:
8080

Bypass proxy for:
*.sap.corp

note the pass proxy list needs to have *, so that it will bypass the host of https://smpsdk-ciat-rel.mo.sap.corp:8081/, if * is missing, then the bypass will not work.


Steps to set proxy on android simulator
1. launch android simulator
2. open settings app from simulator
3. in Wireless & networks section, click "More..." and then select "Cellular networks" item
4. click Access Point Names item
5. click T-Mobile US item
6. set proxy server name in "proxy" field, for example: proxy.phl.sap.corp
7. set proxy port in "port" field, for example: 8080
8. click top right three point menu item and then select save
9. test with mobile chrome and you should be able to access www.yahoo.com
(not yet find a way to set bypass proxy server list on simulator)