iOS hybrid applications using IBM Worklight
Hybrid application starter stores are accessed
as an app
directly installed on mobile devices. IBM Worklight
is a mobile application platform that you can use to develop cross-platform
hybrid applications for smart phone and tablet starter stores using
HTML, CSS and JavaScript. Worklight provides an application programming
interface (API) that enables users to create native User Interface
(UI) elements and access native device functionality using JavaScript,
in addition to extending the hybrid shell application natively.
In Feature Pack 6, support for iPhone hybrid applications is available. With Feature Pack 7, and the responsive web storefront, support for iPad hybrid applications is also available.
The iOS hybrid applications using IBM Worklight deliver a native experience by wrapping the mobile Web storefront with a native shell. The native shell elements are coded using the Apple iOS Software Development Kit (SDK), while the storefront is accessed using the mobile Web interface.
Hybrid iOS applications deliver design aspects and usability elements that function similar to a fully native iOS application.
Topic overview
This topic contains several sections of interest related to iOS hybrid applications for WebSphere Commerce using IBM Worklight.
- Environment setup
- Configuring the iOS hybrid application sample assets for WebSphere Commerce
- Enabling iOS hybrid application features in the mobile web store JSP files
- Updating Worklight-specific files in the mobile web storefront
- Configuring the Worklight Project for the iOS hybrid application
- #csmmobileappworklighti__section
- Setting up the iOS hybrid application in XCode
- Configuring the application after installation
- Integrating native device features into the Worklight iOS hybrid application
- Adding the native features using Worklight NativePage API
- Integrating the native address book
- Integrating the native map display
- Updates to storefront files to invoke native features
- Troubleshooting
Environment setup
The following tasks must be performed to setup your application development environment with WebSphere Commerce:
iOS development prerequisites
Ensure that you have registered with the Apple Developer Program, which is required to access the Xcode IDE and iOS SDK, and allows you to test your application using the iOS simulator.
In addition, you must be a member of the iOS Developer Program to deploy applications on an iOS device. For more information, see Apple Developers: Which Developer Program is for you.
The iOS application built on IBM Worklight 5.0.6 supports iOS versions 5 and 6.
The iOS application built on IBM Worklight 6.0.0 supports iOS 5, 6 and 7.
For more information about supported operating systems, see Detailed System Requirements for IBM Worklight and IBM Mobile Foundation.
Worklight iOS development prerequisites
- Install Worklight Studio on Apple Mac OSX.
- Complete the steps in Android hybrid applications using IBM Worklight: Importing project files. The iOS hybrid application is built on-top of the common resources shared with the Android hybrid application, and requires that the WCWorklight project be imported before configuring the iOS hybrid application.
- Download the sample code:
- Extract the sample code into a working directory. For example, extract_dir
WebSphere Commerce prerequisites
- Ensure that the AuroraMobile.sar file is published. For more information, see Publishing and deploying smart phone and tablet starter stores.
- Ensure that the responsive Aurora starter store (Aurora.sar) is published. It is displayed by default on all devices. There are no mobile or tablet-specific SAR files to publish.
- Determine your
storeId
for the SAR file you published. If you do not know yourstoreId
, run the following SQL query to find thestoreId
that corresponds to your store:select * from store;
Configuring the iOS hybrid application sample assets for WebSphere Commerce
- Update the Aurora mobile web storefront with the iOS Hybrid application
files.
- Merge the contents of extract_dir/WebSphereCommerce/AuroraMobile/mobile20
to:
- WC_eardir/Stores.war/storedir/mobile20
- WCDE_installdir/workspace/Stores/storedir/mobile20
- Merge the contents of extract_dir/WebSphereCommerce/Aurora
to:
- WC_eardir/Stores.war/storedir
- WCDE_installdir/workspace/Stores/storedir
- Merge the contents of extract_dir/WebSphereCommerce/AuroraMobile/mobile20
to:
- Add a new device-mapping for the Worklight iPhone Hybrid application
wc-devices.xml.
- Locate wc-devices.xml. Navigate to:
- WC_eardir/xml/config/com.ibm.commerce.foundation/wc-devices.xml
- WCDE_installdir/xml/config
- Create a new directory called com.ibm.commerce.foundation-ext,
if it does not already exist. Note: When creating an extensions folder, the folder name ends with -ext. This ensures the default file you are extending remains unchanged. By extending the configuration, you can override the properties of the existing wc-devices.xml file.
- In your new extensions folder, create a new file named wc-devices.xml.
- In your new wc-devices.xml file, define a device group for each
of the hybrid applications. Copy and paste the following code snippet
into your file:
<?xml version="1.0" encoding="UTF-8"?> <_config:Devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ibm.com/xmlns/prod/commerce/foundation/config ../xsd/wc-devices.xsd" xmlns:_config="http://www.ibm.com/xmlns/prod/commerce/foundation/config"> <_config:DeviceGroup internalID="-43" channelID="-6"> <_config:Device name="Worklight iPhone Hybrid" userAgentPattern=".*Worklight iPhone Hybrid.*"/> </_config:DeviceGroup> <!-- Override the existing device group definition for internal ID -42 --> <_config:DeviceGroup internalID="-42" channelID="-6"> <_config:Device name="Worklight Android Hybrid" userAgentPattern=".*Worklight Android Hybrid.*"/> </_config:DeviceGroup> </_config:Devices>
<?xml version="1.0" encoding="UTF-8"?> <_config:Devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ibm.com/xmlns/prod/commerce/foundation/config ../xsd/wc-devices.xsd" xmlns:_config="http://www.ibm.com/xmlns/prod/commerce/foundation/config"> <_config:DeviceGroup internalID="-45" channelID="-6"> <_config:Device name="Worklight iPad Hybrid" userAgentPattern=".*iPad.*Worklight/.*"/> </_config:DeviceGroup> <_config:DeviceGroup internalID="-43" channelID="-6"> <_config:Device name="Worklight iPhone Hybrid" userAgentPattern=".*iPhone.*Worklight/.*"/> </_config:DeviceGroup> </_config:Devices>
- Save the changes.
- Locate wc-devices.xml. Navigate to:
- Add the view mappings for the Worklight iOS Hybrid application
to the Struts configuration file.
- Open the extract_dir/Samples/SampleStrutsConfig.txt file.
- Open the struts-config-ext.xml file:
- WC_eardir/Stores.war/WEB-INF/struts-config-ext.xml
- WCDE_installdir/workspace/Stores/WebContent/WEB-INF/struts-config-ext.xml
- Copy and paste the following code snippet into the
<global-forwards>
section in the struts-config-ext.xml file, appending the snippet to the existing forward mappings. Update the sample store ID 10701 in the snippet with your store ID value.<!-- Worklight iOS Hybrid START --> <forward className="com.ibm.commerce.struts.ECActionForward" name="WishListDisplayView/10701/-43" path="/mobile30/UserArea/AccountSection/ServiceSection/InterestItemListSubsection/WishLists.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="AjaxOrderItemDisplayView/10701/-43" path="/mobile30/ShoppingArea/ShopcartSection/OrderItemDisplay.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="AjaxLogonForm/10701/-43" path="/mobile30/UserArea/AccountSection/MyAccountDisplay.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="AjaxStoreLocatorDisplayView/10701/-43" path="/mobile30/StoreLocatorArea/StoreLocator.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="SiteMapView/10701/-43" path="/mobile30/ShoppingArea/CatalogSection/CategorySubsection/TopCategoriesDisplay.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="ChangePassword/10701/-43" path="/mobile30/UserArea/AccountSection/PasswordSubsection/PasswordUpdateForm.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="ReLogonFormView/10701/-43" path="/mobile30/UserArea/AccountSection/LogonSubsection/UserTimeoutView.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="RememberMeLogonFormView/10701/-43" path="/mobile30/UserArea/AccountSection/LogonSubsection/logon.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="LogonForm/10701/-43" path="/mobile30/UserArea/AccountSection/LogonSubsection/logon.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="SearchLandingPage1/10701/-43" path="/ShoppingArea/CatalogSection/SearchSubsection/SearchBasedCategoryPage.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="OrderProcessErrorView/10701/-43" path="/GenericError.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="RLBadPartNumberErrorView/10701/-43" path="/GenericError.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="RLInvalidInputErrorView/10701/-43" path="/GenericError.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="iOSMorePageView/10701" path="/StoreInfoArea/iOSMore.jsp"/> <forward className="com.ibm.commerce.struts.ECActionForward" name="iOSLanguageCurrencyDisplayView/10701" path="/StoreInfoArea/iOSLanguageCurrencyDisplay.jsp"/> <!-- Worklight iOS Hybrid END -->
- Save the changes.
- Register the sample iOS more page in the Struts configuration
file. A sample JSP file is provided to simulate the functionality
of the item list when the more tab is tapped
in an iOS tab bar.
New JSP files (for example, iOSMore.jsp) must be registered in the Struts configuration file to be recognized by WebSphere Commerce. Add the action mapping:
- Open the struts-config-ext.xml file:
- WC_eardir/Stores.war/WEB-INF/struts-config-ext.xml
- WCDE_installdir/workspace/Stores/WebContent/WEB-INF/struts-config-ext.xml
- Add an
<action>
entry in the action mappings section, where 10701 should be replaced by your store ID. An example code snippet:<action path="/iOSMorePageView" type="com.ibm.commerce.struts.BaseAction"> <set-property property="credentialsAccepted" value="10701:P"/> </action> <action path="/iOSLanguageCurrencyDisplayView" type="com.ibm.commerce.struts.BaseAction"> <set-property property="credentialsAccepted" value="10701:P"/> </action>
Note: For extended sites, use the storeent_id of the Asset Store to which these JSP files are registered. Individual extended sites stores inherit these commands from the asset store because created stores do not have any JSP files of their own. - Save the changes
- Open the struts-config-ext.xml file:
- Create access control policies for the sample iOS more page JSP
file. By default, only site administrators can access new views. Create
access control policies for each new JSP file to allow general access.
- Stop WebSphere Commerce server if it is running.
- Go to the following directory:
- WC_installdir\xml\policies\xml
- WCDE_installdir/xml/policies/xml
- Create a new XML file called SampleiOSAccessPolicies.xml and copy
and paste the following code snippet into the file:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?> <!DOCTYPE Policies SYSTEM "../dtd/accesscontrolpolicies.dtd"> <Policies> <Action Name="iOSMorePageView" CommandName="iOSMorePageView"> </Action> <ActionGroup Name="AuroraStorefrontAssetStoreAllUsersViews" OwnerID="RootOrganization"> <ActionGroupAction Name="iOSMorePageView"/> </ActionGroup> <ActionGroup Name="AuroraAllUsersViews" OwnerID="RootOrganization"> <ActionGroupAction Name="iOSMorePageView"/> </ActionGroup> <Action Name="iOSLanguageCurrencyDisplayView" CommandName="iOSLanguageCurrencyDisplayView"> </Action> <ActionGroup Name="AuroraStorefrontAssetStoreAllUsersViews" OwnerID="RootOrganization"> <ActionGroupAction Name="iOSLanguageCurrencyDisplayView"/> </ActionGroup> <ActionGroup Name="AuroraAllUsersViews" OwnerID="RootOrganization"> <ActionGroupAction Name="iOSLanguageCurrencyDisplayView"/> </ActionGroup> </Policies>
- Save the file.
- Load the access control policies defined in SampleiOSAccessPolicies.xml. See Loading access control policy definitions and other policy-related elements for more information.
- Start WebSphere Commerce server.
Enabling iOS hybrid application features in the mobile web store JSP files
- Add the device ID detection logic to the environment setup JSPs
for the Worklight iOS hybrid applications to enable device-specific
rendering.
- Open the following file:
- WC_eardir/Stores.war/storedir/mobile20/include/JSTLEnvironmentSetup.jspf
- WCDE_installdir/workspace/Stores/storedir/mobile20/ include/JSTLEnvironmentSetup.jspf
- WC_eardir/Stores.war/storedir/Common/EnvironmentSetup.jspf
- WCDE_installdir/workspace/Stores/storedir/Common/EnvironmentSetup.jspf
- Locate the following code snippet:
<%-- Set variables for device specific rendering --%> <c:choose> <c:when test="${EC_deviceAdapter.deviceFormatId == -12}"> <c:set var="_iPhoneHybridApp" value="true"/> <c:set var="mobileBasePath" value="mobile20/iPhoneHybrid"/> <c:set var="pageMax1" value="20"/> <c:set var="pageMax2" value="20"/> </c:when> <c:when test="${EC_deviceAdapter.deviceFormatId == -22}"> <c:set var="_androidHybridApp" value="true" /> <c:set var="mobileBasePath" value="mobile20/AndroidHybrid"/> <c:set var="pageMax1" value="20"/> <c:set var="pageMax2" value="20"/> </c:when> <c:when test="${EC_deviceAdapter.deviceFormatId == -42}"> <c:set var="_worklightHybridApp" value="true"/> <c:set var="mobileBasePath" value="mobile20/WorklightHybrid"/> <c:set var="pageMax1" value="20"/> <c:set var="pageMax2" value="20"/> </c:when> <c:otherwise> <%-- Generic mobile device --%> </c:otherwise> </c:choose>
<%-- Set variables for device specific rendering --%> <c:if test="${EC_deviceAdapter.deviceFormatId == -44 || EC_deviceAdapter.deviceFormatId == -42}"> <c:set var="_worklightHybridApp" value="true" scope="request"/> <c:set var="mobileBasePath" value="WorklightHybrid/"/> <c:set var="pageMax1" value="20"/> <c:set var="pageMax2" value="20"/> </c:if>
- Replace with the following code snippet:
<c:choose> <c:when test="${EC_deviceAdapter.deviceFormatId == -12}"> <c:set var="_iPhoneHybridApp" value="true"/> <c:set var="mobileBasePath" value="mobile20/iPhoneHybrid"/> <c:set var="pageMax1" value="20"/> <c:set var="pageMax2" value="20"/> </c:when> <c:when test="${EC_deviceAdapter.deviceFormatId == -22}"> <c:set var="_androidHybridApp" value="true" /> <c:set var="mobileBasePath" value="mobile20/AndroidHybrid"/> <c:set var="pageMax1" value="20"/> <c:set var="pageMax2" value="20"/> </c:when> <c:when test="${EC_deviceAdapter.deviceFormatId == -42 || EC_deviceAdapter.deviceFormatId == -43}"> <c:set var="_worklightHybridApp" value="true"/> <c:set var="mobileBasePath" value="mobile20/WorklightHybrid"/> <c:set var="pageMax1" value="20"/> <c:set var="pageMax2" value="20"/> </c:when> <c:otherwise> <%-- Generic mobile device --%> </c:otherwise> </c:choose> <c:if test="${EC_deviceAdapter.deviceFormatId == -43}"> <c:set var="_worklightiPhoneHybridApp" value="true"/> </c:if>
<%-- Set variables for device specific rendering --%> <c:if test="${EC_deviceAdapter.deviceFormatId == -45 || EC_deviceAdapter.deviceFormatId == -44 || EC_deviceAdapter.deviceFormatId == -43 || EC_deviceAdapter.deviceFormatId == -42}"> <c:set var="_worklightHybridApp" value="true" scope="request"/> <c:set var="mobileBasePath" value="WorklightHybrid/"/> <c:set var="pageMax1" value="20"/> <c:set var="pageMax2" value="20"/> </c:if> <%-- Set variable for iPad and iPhone devices rendering --%> <c:if test="${EC_deviceAdapter.deviceFormatId == -45 || EC_deviceAdapter.deviceFormatId == -43}"> <c:set var="_worklightiOSHybridApp" value="true" scope="request"/> </c:if>
- Save the file.
- Open the following file:
- Update WorklightHybridSetup.jspf with new
URLs required for the iOS hybrid application.
- Open the following file:
- WC_eardir/Stores.war/storedir/WorklightHybrid/WorklightHybridSetup.jspf
- WCDE_installdir/workspace/Stores/storedir/WorklightHybrid/WorklightHybridSetup.jspf
- Locate the following code snippet:
<wcf:url var="HomePageURL" patternName="HomePageURLWLang" value="TopCategories"> <wcf:param name="langId" value="${langId}" /> <wcf:param name="storeId" value="${WCParam.storeId}" /> <wcf:param name="catalogId" value="${WCParam.catalogId}" /> </wcf:url>
- Replace with the following code snippet:
<wcf:url var="HomePageURL" patternName="HomePageURLWithLang" value="TopCategories1"> <wcf:param name="langId" value="${langId}" /> <wcf:param name="storeId" value="${WCParam.storeId}" /> <wcf:param name="catalogId" value="${WCParam.catalogId}" /> <wcf:param name="urlLangId" value="${urlLangId}"/> </wcf:url> <c:if test="${_worklightiOSHybridApp}"> <wcf:url var="iOSMorePageURL" value="iOSMorePageView"> <wcf:param name="langId" value="${langId}" /> <wcf:param name="storeId" value="${WCParam.storeId}" /> <wcf:param name="catalogId" value="${WCParam.catalogId}" /> </wcf:url> </c:if>
- Locate the following code snippet:
if (storedLangId == null || storedLangId != '${langId}') { ... }
- Update the code snippet with the following change:
if (storedLangId == null || storedLangId != '${langId}') { ... <c:if test="${_worklightiOSHybridApp}"> storage.setItem('iOSMorePageURL','${iOSMorePageURL}'); </c:if> }
- Locate the following code snippet:
<wcf:url var="ShoppingCartURL" value="AjaxOrderItemDisplayView"> <wcf:param name="langId" value="${langId}" /> <wcf:param name="storeId" value="${WCParam.storeId}" /> <wcf:param name="catalogId" value="${WCParam.catalogId}" /> </wcf:url>
- Replace with the following code snippet:
<wcf:url var="shopViewURL" value="AjaxOrderItemDisplayView"></wcf:url> <wcf:url var="ShoppingCartURL" value="OrderCalculate" type="Ajax"> <wcf:param name="langId" value="${langId}" /> <wcf:param name="storeId" value="${WCParam.storeId}" /> <wcf:param name="catalogId" value="${WCParam.catalogId}" /> <wcf:param name="URL" value="${fn:escapeXml(shopViewURL)}" /> <wcf:param name="errorViewName" value="AjaxOrderItemDisplayView" /> <wcf:param name="updatePrices" value="1" /> <wcf:param name="calculationUsageId" value="-1" /> <wcf:param name="orderId" value="." /> </wcf:url>
- Save the file.
- Open the following file:
- Update WorklightJSToInclude.jspf to load
iOS-specific resources on initialization.
- Open the following file:
- WC_eardir/Stores.war/storedir/WorklightHybrid/WorklightJSToInclude.jspf
- WCDE_installdir/workspace/Stores/storedir/WorklightHybrid/WorklightJSToInclude.jspf
- Locate the following code snippet:
<c:choose> <c:when test="${EC_deviceAdapter.deviceFormatId == -42 || EC_deviceAdapter.deviceFormatId == -44}"> <c:set var="platformType" value="android"/> </c:when> <c:otherwise> </c:otherwise> </c:choose>
- Replace with the following code snippet:
<c:choose> <c:when test="${EC_deviceAdapter.deviceFormatId == -42 || EC_deviceAdapter.deviceFormatId == -44}"> <c:set var="platformType" value="android"/> </c:when> <c:when test="${EC_deviceAdapter.deviceFormatId == -43 || EC_deviceAdapter.deviceFormatId == -45}"> <c:set var="platformType" value="ios"/> </c:when> <c:otherwise> </c:otherwise> </c:choose>
- Save the file.
- Open the following file:
- Update the search widget in order for the
Worklight iOS hybrid app to use the web search instead of the native
search by default.
- Open the following file:
- WC_eardir/Stores.war/storedir/Widgets/Search/Search_UI.jspf
- WCDE_installdir/workspace/Stores/storedir/Widgets/Search/Search_UI.jspf
- Locate the following code snippet:
<a id="searchButton" href="#" role="button" <c:choose><c:when test="${_worklightHybridApp}">onclick="javascript:NativeSearchJS.startSearchActivity();"</c:when><c:otherwise>data-toggle="searchBar"</c:otherwise></c:choose> aria-label="${SEARCH_CATALOG}" title="${SEARCH_CATALOG}" ><span id="searchButton_ACCE_Label" class="spanacce"><fmt:message bundle="${storeText}" key="SEARCH_CATALOG"/></span></a> <div id="searchBar" data-parent="header"> <c:if test="${_worklightHybridApp eq 'true'}"> <a id="worklightSearchButton" href="#" role="button" onclick="javascript:NativeSearchJS.startSearchActivity();"><span id="worklightSearchButtonLabel">${SEARCH_CATALOG}</span></a> </c:if>
- Update the conditional statements to check whether _worklightiOSHybridApp
is false. Replace with the following code snippet:
<a id="searchButton" href="#" role="button" <c:choose><c:when test="${_worklightHybridApp && !_worklightiOSHybridApp}">onclick="javascript:NativeSearchJS.startSearchActivity();"</c:when><c:otherwise>data-toggle="searchBar"</c:otherwise></c:choose> aria-label="${SEARCH_CATALOG}" title="${SEARCH_CATALOG}" ><span id="searchButton_ACCE_Label" class="spanacce"><fmt:message bundle="${storeText}" key="SEARCH_CATALOG"/></span></a> <div id="searchBar" data-parent="header"> <c:if test="${_worklightHybridApp eq 'true' && !_worklightiOSHybridApp}"> <a id="worklightSearchButton" href="#" role="button" onclick="javascript:NativeSearchJS.startSearchActivity();"><span id="worklightSearchButtonLabel">${SEARCH_CATALOG}</span></a> </c:if>
- Save the file.
- Open the following file:
- Enable the log out button on the My Account
page for the iPhone hybrid application.
- Open the following file:
- WC_eardir/Stores.war/storedir/mobile20/UserArea/AccountSection/MyAccountDisplay.jsp
- WCDE_installdir/workspace/Stores/storedir/mobile20/UserArea/AccountSection/MyAccountDisplay.jsp
- Locate the following code snippet:
<c:if test="${_iPhoneHybridApp == true}"> <!-- Home Page link: MobileIndexURL --> <wcf:url var="MobileIndexURL" value="m20Index"> <wcf:param name="langId" value="${langId}" /> <wcf:param name="storeId" value="${WCParam.storeId}" /> <wcf:param name="catalogId" value="${WCParam.catalogId}" /> </wcf:url>
- Add
worklightiPhoneHybridApp
to the list of OR conditions in the<c:if>
section. The updated line should resemble the following snippet:<c:if test="${_iPhoneHybridApp == true || _worklightiPhoneHybridApp == true }">
- Save the file.
- Open the following file:
- Update the Continue Shopping link to the
store home page at the order confirmation page so that it is an SEO
URL.
- Open the following file:
- WC_eardir/Stores.war/storedir/mobile20/ShoppingArea/OrderSection/OrderConfirmationDisplay.jsp
- WCDE_installdir/workspace/Stores/storedir/mobile20/ShoppingArea/OrderSection/OrderConfirmationDisplay.jsp
- Locate the following code snippet:
<wcf:url var="ContinueShopping" value="m20Index">
- Add the
m20HomeURLWLang
SEO pattern name to the list of OR conditions in the<wcf:url>
section. The updated line should resemble the following snippet:<wcf:url var="ContinueShopping" patternName="m20HomeURLWLang" value="m20Index">
- Save the file.
- Open the following file:
Updating Worklight-specific files in the mobile web storefront
- Update the Worklight hybrid header JSP file.
- Open the following file:
- WC_eardir/Stores.war/storedir/mobile20/WorklightHybrid/include/styles/style1/CachedHeaderDisplay.jsp
- WCDE_installdir/workspace/Stores/storedir/mobile20/WorklightHybrid/include/styles/style1/CachedHeaderDisplay.jsp
- Locate the following code snippet:
<div id="masthead"> ... </div>
- Add a
<c:choose>
tag and enclose the<div id="masthead">
with a<c:when>
tag and the following condition. The updated code should resemble the following snippet:<c:choose> <c:when test="${_worklightHybridApp && !_worklightiPhoneHybridApp}"> <div id="masthead"> ... </div> </c:when>
- Create the header to be used by the iPhone hybrid app. Copy the
following code snippet and paste it after the previously-added
<c:when>
tag:<c:when test="${_worklightiPhoneHybridApp}"> <div id="masthead" class="ios"> <a id="back_button" onclick="WCHybridApp.onBackKeyDown();"> <div id="back_button_container" class="back_button_spacer"> <div class="back_button_container left"> <div class="back_button_arrow left"></div> <div class="back_button_text left">Back</div> </div> </div> </a> <div id="header_pg_title"> <script>document.write(document.title);</script> </div> <a id="mobile_home_link" href="${fn:escapeXml(MobileIndexURL)}"> <div class="header_logo"></div> </a> <div class="clear_float"></div> </div> </c:when> <c:otherwise></c:otherwise> </c:choose>
- Replace the following code snippet:
With the following code snippet:<c:set var="mobileClient" value="${header['user-agent']}"/> <c:if test="${fn:contains(mobileClient, 'Android')}">
<c:if test="${_worklightHybridApp}">
- Save the file.
- Open the following file:
- Update the Worklight hybrid footer JSP file.
- Open the following file:
- WC_eardir/Stores.war/storedir/mobile20/WorklightHybrid/include/styles/style1/CachedFooterDisplay.jsp
- WCDE_installdir/workspace/Stores/storedir/mobile20/WorklightHybrid/include/styles/style1/CachedFooterDisplay.jsp
- Copy and paste the following
<wcf:url>
tag for the new page:<!-- iOS More Page link: iOSMorePageURL --> <wcf:url var="iOSMorePageURL" value="m20MorePageView"> <wcf:param name="langId" value="${langId}" /> <wcf:param name="storeId" value="${WCParam.storeId}" /> <wcf:param name="catalogId" value="${WCParam.catalogId}" /> </wcf:url>
- Replace the following code snippet:
With the following code snippet:<c:set var="mobileClient" value="${header['user-agent']}"/> <c:choose> <c:when test="${fn:contains(mobileClient, 'Android')}">
<c:choose> <c:when test="${_worklightHybridApp}">
- Add a
<c:when>
tag to test for the iPhone hybrid application:<c:when test="${_worklightiPhoneHybridApp}"> ... </c:when>
- Within the
<c:when>
tag for the iPhone hybrid application, copy and paste the entire set of script loading statements from the Android hybrid application. For each<script>
tag, inspect the src attribute and replace any instance of the wordandroid
withios
. This loads the scripts under the ios folder, and also loads thewlgap.ios.js
script rather than thewlgap.android.js
script. For more information about the scripts to be copied, see #csmmobileappworklighti__section. - Locate the following code snippet:
if (storedLangId == null || storedLangId != '${langId}') { ... }
- Add the following line to store the URL for the page into HTML5
local storage for access to the value of iOSMorePageURL within the
application:
storage.setItem('iOSMorePageURL','${iOSMorePageURL}'); //iOS more page
- Save the file.
- Open the following file:
- Update the CSS to import the iOS style sheet.
- Open the following file:
- WC_eardir/Stores.war/storedir/mobile20/WorklightHybrid/css/common1_1.css
- WCDE_installdir/workspace/Stores/storedir/mobile20/WorklightHybrid/css/common1_1.css
- Replace the following code snippet:
With the following code snippet:@import url("../android/css/WCHybrid.css");
@import url("../android/css/WCHybrid.css"); @import url("../ios/css/WCHybrid.css");
- Save the file.
- Open the following file:
- Add a container for the back button within
the header widget.
- Open the file:
- WC_eardir/Stores.war/storedir/Widgets/Header/Header_UI.jspf
- WCDE_installdir/workspace/Stores/storedir/Widgets/Header/Header_UI.jspf
- Replace the following code snippet:
<div id="header" role="banner">
With the following code snippet:<div id="header" <c:if test="${_worklightiOSHybridApp}">class="ios"</c:if> role="banner"> <c:if test="${(_worklightiOSHybridApp)}"> <div id="header_back_button_container"></div> </c:if>
- Save the file.
- Open the file:
- Update the common1_1.css and common1_1_rtl.css
files to include the iOS WCHybrid.css file.
- Open the common1_1.css file:
- WC_eardir/Stores.war/storedir/WorklightHybrid/css/common1_1.css
- WCDE_installdir/workspace/Stores/storedir/WorklightHybrid/css/common1_1.css
- Add the following line:
@import url("../ios/css/WCHybrid.css");
- Save the file.
- Open the common1_1_rtl.css file:
- WC_eardir/Stores.war/storedir/WorklightHybrid/css/common1_1_rtl.css
- WCDE_installdir/workspace/Stores/storedir/WorklightHybrid/css/common1_1_rtl.css
- Add the following line:
@import url("../ios/css/WCHybrid_rtl.css");
- Save the file.
- Open the common1_1.css file:
Configuring the Worklight Project for the iOS hybrid application
- In Worklight Studio, add the iPhone environment.
- In the Project Explorer view, right-click WCWorklight and select .
- Select iPhone and click Finish.
- Perform a build and deploy of the iPhone environment.
- Import the application sample source code into the development
environment. The files include Aurora sample images for the application's
user interface, such as icons for the tab bar and splash screens.
- Select .
- Select the extract_dir/WCWorklight directory and click OK.
- Click Select All files.
- Ensure your target folder is WCWorklight.
- Ensure that Overwrite existing resources without warning is selected.
- Click Finish.Note:
- If you encounter a dialog warning of read-only files encountered, select Yes to make the files writable.
- An issue (Bug 402263) with the file import tool in Eclipse 4.2.2 might prevent the importing of the sample source code into the WCWorklight project. To work around this issue, manually merge the contents of extract_dir/WCWorklight with the workspace_dir/WCWorklight directory.
- Update the WCHybrid.css to apply additional iPhone-specific styling.
- In Worklight Studio, under the Project Explorer view, open iphone/css/WCHybrid.css.
- Copy and paste the following code snippet
into the CSS file, after the existing file contents:
html#WCHybridHtmlContent { height: 100%; } /*Worklight container div */ #content { height: 100%; min-height: 100%; margin: 0 auto; width: 100%; }
- Add a CSS media query to resize the logo image displayed on the
splash screen for Apple retina display devices. In Worklight Studio,
open iphone/css/WCHybrid.css and copy and paste the following code
snippet into your CSS file, after the existing file contents:
@media only screen and (-webkit-device-pixel-ratio: 2.0), only screen and (min-resolution: 192dpi) { .splash_logo { background-image: url('../images/splash_xhdpi.png'); background-size: 45% auto; } }
- Save the changes.
- Add the onDeviceReady function to WCHybrid.js. This function is
called once the Cordova runtime has initialized, which is required
to bind certain Cordova event listeners.
- In Worklight Studio, under the Project Explorer view, go to and open WCHybrid.js.
- Below the
wlEnvInit()
function, append the following code snippet:var WCHybridJS = (function() { return { /** * Called when Cordova runtime has initialized */ onDeviceReady: function() { // Functions requiring Cordova runtime to be initialized can be added here } }; })();
- Save the changes.
- Add new strings to the Messages object.
This step covers the additional English language message. Translated
strings should be added as required based on this step.
- Open the common/js/messages.js file.
- In the Messages object, add a new key named
back
, using the existing keys as a reference. For example:back: null,
- Save the file.
- Open the common/js/messages_en_US.js file.
- In the setEn_US function, assign the value
back
to the Messages.back variable. For example:Messages.back = "Back";
- Save the file.
- Update the iPhone Cordova settings in the config.xml.
- Open .
- Change the value for the
OpenAllWhitelistURLsInWebView
preference to true. - Set the whitelist of hosts that Cordova is allowed to connect
to by adding an
<access>
tag, and set the origin attribute. If your site uses unsecure (HTTP) and secure (HTTPS) protocol, you must add an<access>
tag for both protocols.For example, if your WebSphere Commerce server hostname is www.example.com, add the following<access>
tags:
For more information, see Project Settings for iOS.<access origin="http://www.example.com/"/> <access origin="https://www.example.com/"/>
- Build and deploy the iPhone environment and launch XCode.
- Right-click . The XCode environment opens.
Setting up the iOS hybrid application in XCode
- Implement the development utilities into your main application
file.
- Open .
- Update the
didFinishWLNativeInit
method. This method retrieves the default values for the settings defined in the WCHybridSettings property list, enables the Settings option to clear the local HTML5 storage and ignoring of bad SSL certificates. In addition, the user-agent string of the web view is updated with the string defined in the WebSphere Commerce wc-devices.xml file for the Worklight iPhone Hybrid application.Note: Certain functions, such as clearing the local storage and ignoring bad SSL certificates are intended for development use only and must be removed prior to deploying the application to production.The getDefaultsFromSettingsBundle method registers the values specified in the WCHybridSettings property list within the Settings.bundle on application initialization. removeTargetFileAtPath method removes files, and is used to remove the application cache files. clearLocalStorage method clears application settings, such as the WebSphere Commerce host name, are stored using the HTML5 LocalStorage for the web view. During development it might be useful to provide a way to clear these files and reset the application settings.
Copy and paste the following code snippet into AuroraWLHybrid.m, replacing the existing didFinishWLNativeInit method:-(void) didFinishWLNativeInit:(NSNotification *)notification { /* * If you need to do any extra app-specific initialization, you can do it here. * Note: At this point webview is available. **/ // Set navigation bar tint color for native pages [[UINavigationBar appearance] setTintColor:[UIColor orangeColor]]; NSUserDefaults *stdUserDefaults = [NSUserDefaults standardUserDefaults]; // Load the app settings from the WCHybridSettings property list file NSMutableDictionary *wcHybridAppSettings = [self getDefaultsFromSettingsBundle]; if (wcHybridAppSettings != nil) { // Override app variables with user settings for (NSString *key in wcHybridAppSettings.allKeys) { NSString *value = [stdUserDefaults stringForKey:key]; if (value != nil && [value length] != 0) { [wcHybridAppSettings setValue:value forKey:key]; } } #if DEBUG // Development use only - Clear the HTML5 LocalStorage if corresponding user setting is enabled // MUST BE REMOVED FROM PRODUCTION CODE BOOL clearLocalStorageEnabled = [[wcHybridAppSettings valueForKey:@"clearLocalStorage"] boolValue]; if (clearLocalStorageEnabled) { [self clearLocalStorage]; } // Development use only - Ignore bad SSL certificate if correpsonding user setting is enabled // MUST BE REMOVED FROM PRODUCTION CODE BOOL ignoreBadSslCert = [[wcHybridAppSettings valueForKey:@"ignoreBadSslCert"] boolValue]; if (ignoreBadSslCert) { // Gets the server hostname from the corresponding user settings NSString *wcServerHost = [wcHybridAppSettings valueForKey:@"wcServerHostname"]; if (wcServerHost == nil || [wcServerHost length] == 0) { wcServerHost = @""; //No WebSphere Commerce Server hostname set. Go to Settings and provide the hostname (e.g. demo.ibm.com) of the server with the invalid SSL certificate. Terminate and relaunch the app for the hostname to take effect. } else { [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:wcServerHost]; } } #endif // Append the Worklight Hybrid User-Agent to the default value // Create a UIWebView to retrieve the user-agent via JavaScript /* NSString *wlUserAgentSuffix = @" Worklight iPhone Hybrid"; // leading space is required UIWebView *tempWebView = [[UIWebView alloc] initWithFrame:CGRectZero]; NSString *defaultUserAgent = [tempWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; NSString *wlUserAgent = [defaultUserAgent stringByAppendingString:wlUserAgentSuffix]; [wcHybridAppSettings setObject:wlUserAgent forKey:@"UserAgent"]; */ // Register the settings [stdUserDefaults registerDefaults:wcHybridAppSettings]; } } - (NSMutableDictionary *)getDefaultsFromSettingsBundle { NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; // Get the pathname for Settings.bundle NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"]; // Get the contents of WCHybridSettings.plist NSDictionary *wcHybridSettings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"WCHybridSettings.plist"]]; if (wcHybridSettings != nil) { // Get the preferences under the PreferenceSpecifiers key NSArray *preferences = [wcHybridSettings objectForKey:@"PreferenceSpecifiers"]; if (preferences != nil) { NSMutableDictionary *defaults = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]]; // Store default value (if set in the property list) for each key for (NSDictionary *pref in preferences) { NSString *key = [pref objectForKey:@"Key"]; if (key != nil) { NSString *defVal = [pref objectForKey:@"DefaultValue"]; if (defVal != nil) { [defaults setObject:defVal forKey:key]; } } } dict = defaults; } } return dict; } - (void)removeTargetFileAtPath:(NSString *)path :(NSString *)fileExtension { for (NSString *string in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil]) { if ([[string pathExtension] isEqualToString:fileExtension]) { [[NSFileManager defaultManager] removeItemAtPath:[path stringByAppendingPathComponent:string] error:nil]; } } } - (void)clearLocalStorage { NSString *LOCAL_STORAGE_EXT = @"localstorage"; // Remove cookies NSHTTPCookieStorage *WCHybridCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; for (NSHTTPCookie *cookie in [WCHybridCookieStorage cookies]) { [WCHybridCookieStorage deleteCookie:cookie]; } // Remove the HTML5 local storage db NSString *appStoragePath = [[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"Backups"] stringByAppendingPathComponent:@"localstorage.appdata.db"]; [[NSFileManager defaultManager] removeItemAtPath:appStoragePath error:nil]; // Library/Caches folder is used for local web storage for iOS versions 5.1-6.0 if(IsAtLeastiOSVersion(@"5.1") && !IsAtLeastiOSVersion(@"6.0")) { NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; [self removeTargetFileAtPath:cachePath :LOCAL_STORAGE_EXT]; } // Check Library/WebKit folder for iOS 6 NSString *webKitPath = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"WebKit/LocalStorage"]; [self removeTargetFileAtPath:webKitPath :LOCAL_STORAGE_EXT]; // Reset the Clear LocalStorage toggle in Settings [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"clearLocalStorage"]; }
- The web view in Worklight 5 does not provide
a user-agent that identifies itself as a Worklight application. In
order for WebSphere Commerce to detect and serve the platform-specific
content, uncomment the following code snippet in didFinishWLNativeInit:
/* NSString *wlUserAgentSuffix = @" Worklight iPhone Hybrid"; // leading space is required UIWebView *tempWebView = [[UIWebView alloc] initWithFrame:CGRectZero]; NSString *defaultUserAgent = [tempWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; NSString *wlUserAgent = [defaultUserAgent stringByAppendingString:wlUserAgentSuffix]; [wcHybridAppSettings setObject:wlUserAgent forKey:@"UserAgent"]; */
- Add the following interface for the
NSURLRequest
.Note: This function is intended for development use only and must be removed prior to deploying the application to production.This code should be placed between theimport
statements and the@implementation
directive:// Development use only - Ignore bad SSL certificate if corresponding user setting is enabled // MUST BE REMOVED FROM PRODUCTION CODE @interface NSURLRequest (Dummy) + (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host; + (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString *)host; @end
- Values specified in the WCHybridSettings property list within
the Settings.bundle must be registered by the application to apply.
The following code excerpt shows an implementation of
getDefaultsFromSettingsBundle
. Add the following method to AuroraWLHybrid.m:- (NSMutableDictionary *)getDefaultsFromSettingsBundle { NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; // Get the pathname for Settings.bundle NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"]; // Get the contents of WCHybridSettings.plist NSDictionary *wcHybridSettings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"WCHybridSettings.plist"]]; if (wcHybridSettings != nil) { // Get the preferences under the PreferenceSpecifiers key NSArray *preferences = [wcHybridSettings objectForKey:@"PreferenceSpecifiers"]; if (preferences != nil) { NSMutableDictionary *defaults = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]]; // Store default value (if set in the property list) for each key for (NSDictionary *pref in preferences) { NSString *key = [pref objectForKey:@"Key"]; if (key != nil) { NSString *defVal = [pref objectForKey:@"DefaultValue"]; if (defVal != nil) { [defaults setObject:defVal forKey:key]; } } } dict = defaults; } } return dict; }
- The
removeTargetFileAtPath
method is used to remove files, such as the application cache files. Add the following method to AuroraWLHybrid.m:- (void)removeTargetFileAtPath:(NSString *)path :(NSString *)fileExtension { for (NSString *string in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil]) { if ([[string pathExtension] isEqualToString:fileExtension]) { [[NSFileManager defaultManager] removeItemAtPath:[path stringByAppendingPathComponent:string] error:nil]; } } }
- Application settings, such as the WebSphere Commerce host name,
are stored using the HTML5 LocalStorage for the web view. During development
it might be useful to provide a way to clear these files and reset
the application settings. Add the following implementation of the
clearLocalStorage
method to AuroraWLHybrid.m:- (void)clearLocalStorage { NSString *LOCAL_STORAGE_EXT = @"localstorage"; // Remove cookies NSHTTPCookieStorage *WCHybridCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; for (NSHTTPCookie *cookie in [WCHybridCookieStorage cookies]) { [WCHybridCookieStorage deleteCookie:cookie]; } // Remove the HTML5 local storage db NSString *appStoragePath = [[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"Backups"] stringByAppendingPathComponent:@"localstorage.appdata.db"]; [[NSFileManager defaultManager] removeItemAtPath:appStoragePath error:nil]; // Library/Caches folder is used for local web storage for iOS versions 5.1-6.0 if(IsAtLeastiOSVersion(@"5.1") && !IsAtLeastiOSVersion(@"6.0")) { NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; [self removeTargetFileAtPath:cachePath :LOCAL_STORAGE_EXT]; } // Check Library/WebKit folder for iOS 6 NSString *webKitPath = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"WebKit/LocalStorage"]; [self removeTargetFileAtPath:webKitPath :LOCAL_STORAGE_EXT]; // Reset the Clear LocalStorage toggle in Settings [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"clearLocalStorage"]; }
- Add the Development preferences user interface controls to the Settings menu.
For more information, see Implementing an iOS Settings Bundle.
- Open .
- Create a child panel item in the Root.plist. This will create
a button in the top-level application settings page that shows the
child settings page. Control-click the editor pane and select Add
Row. Use the following values for this key:
Settings Field Type Value Type String Child Pane Title String Settings Filename String WCHybridSettings - Create a new property list named WCHybridSettings.plist to the Settings bundle. For more information, see Creating Additional Settings Page Files.
- Add a text field for the WebSphere Commerce Server hostname. Control-click
the editor pane and select Add Row. Assign
the following values for this key:
Server Hostname Field Type Value Type String PSTextFieldSpecifier Title String Server Hostname Identifier String wcServerHostname Default value String No default value - Add a toggle switch to enable ignoring of bad SSL certificates.
Control-click the editor pane and select Add Row.
Use the following values for this key:
Ignore Bad SSL Cert Field Type Value Type String Toggle Switch Title String Ignore Bad SSL Cert Identifier String ignoreBadSslCert Default Value Boolean YES - Add a toggle switch to clear the local storage. Control-click
the editor pane and select Add Row. Use the
following values for this key:
Clear local storage Field Type Value Type String Toggle Switch Title String Clear Local Storage Identifier String clearLocalStorage Default Value Boolean NO - Add a new group for Development items. This will visually group
the following items in the Settings menu. Control-click
the editor pane and select Add Row. Use the
following values for this key:
Development Field Type Value Type String Group Title String Development - Save and close.
- Build the XCode project.
- Perform a build in XCode and deploy to the iOS Simulator. Select the iPhone Simulator when prompted.
Configuring the application after installation
The application settings are accessible through the iOS Settings application, under the Aurora WLHybrid app. These settings are intended for development use only, and should be removed before deploying the application to production. For example, end-users must not be able to clear local storage, enable ignoring of bad SSL certificates, or other settings that can negatively alter the behavior of the application.
- Clear LocalStorage
- Toggles whether the application should clear the application's HTML5 LocalStorage, allowing you to update the in-app configuration (host name, store ID, etc).
- Ignore Bad SSL Cert
- Toggles whether the application should ignore a bad SSL certificate.
- Server Hostname
- When ignoring bad SSL certificates, the host name of your WebSphere Commerce server must also be specified.
- Go to your simulator or device application settings and open the Settings application. Scroll through the list of applications and select .
- Update the following settings:
- Ensure Ignore Bad SSL Cert is set to On.
- Ensure that your WebSphere Commerce Server host name is specified in the Server Hostname field.
- Exit and restart the application
Integrating native device features into the Worklight iOS hybrid application
Worklight provides options to access native functionality from web applications: implementing a custom Apache Cordova plug-in, or integrating native pages with web pages. The NativePage API enables the application to switch between a web view and a native view. Similar to the Android hybrid application, the iPhone hybrid application takes the approach of integrating native pages with the web pages using the Worklight NativePage API.
- Address book integration for importing contacts into the account address book.
- Native Maps application integration under the stores tab.
Barcode scanning requires integration with third-party libraries such as ZBar or ZXing barcode scanners. Barcode scanning can be implemented as an Apache Cordova plug-in. For more information, see Using Cordova plugins in Worklight: Native barcode scanning for iOS.
Adding the native features using Worklight NativePage API
- WC_eardir/Stores.war/storedir/mobile20/WorklightHybrid/common/js
- WCDE_installdir/Stores/storedir/mobile20/WorklightHybrid/common/js
- WC_eardir/Stores.war/storedir/WorklightHybrid/common/js
- WCDE_installdir/Stores/storedir/WorklightHybrid/common/js
Class names for the Worklight iOS hybrid application can be declared in the WCHybridAppProperties.js file. A sample implementation has been provided as part of the sample source ZIP file (see Worklight iOS development prerequisites).
For example, in WCHybridAppProperties.js, the native map class name is MapViewController. Following this convention, in your XCode project, you would create a new Objective-C class named MapViewController containing your implementation of the native page for the map view.
Integrating the native address book
- In XCode, right-click . Select . Click Next.
- Following the definition in WCHybridAppProperties.js, in the Class field, name the class ContactsViewController. Ensure that Subclass of is set to UIViewController. Select With XIB for user interface. Click Next.
- In the following window, navigate to and select the Classes folder. Click Create.
- In the ContactsViewController.h file, update
the interface definition to include the
ABPeoplePickerNavigationControllerDelegate
. For more information, see ABPeoplePickerNavigationControllerDelegate Protocol Reference.- Add import statements for the AddressBook frameworks:
<AddressBook/AddressBook.h> <AddressBookUI/AddressBookUI.h>
- Locate the following line:
Update the line to include the delegate:@interface ContactsViewController : UIViewController
@interface ContactsViewController : UIViewController <ABPeoplePickerNavigationControllerDelegate>
- Save the changes.
- Add import statements for the AddressBook frameworks:
- In the ContactsViewController.m file, add
the methods required to implement the ABPeoplePickerNavigationControllerDelegate.
The
peoplePickerNavigationControllerDidCancel
method dismisses the people picker when the user taps the cancel button in the user interface and returns to the web view. ThepeoplePickerNavigationController:shouldContinueAfterSelectingPerson
method displays the contact, when one is selected by the user and the picker is dismissed. ThepeoplePickerNavigationController:shouldContinueAfterSelectingPerson:property:identifier
method returns data to the web view when the user selects a contact's properties, and dismisses the view.Copy and paste the following code snippet:-(void)setDataFromWebView:(NSDictionary *)data { } #pragma mark - ABPeoplePickerNavigationControllerDelegate - (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker { [self dismissViewControllerAnimated:NO completion:nil]; [NativePage showWebView:nil]; } - (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person { return YES; } - (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier { NSMutableDictionary *address = [[NSMutableDictionary alloc] init]; NSString *nickName = (NSString *)ABRecordCopyValue(person, kABPersonNicknameProperty); NSString *firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty); NSString *lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty); ABMultiValueRef addressMVR = ABRecordCopyValue(person, property); CFIndex index = ABMultiValueGetIndexForIdentifier(addressMVR, identifier); CFDictionaryRef addressDR = ABMultiValueCopyValueAtIndex(addressMVR, index); NSString *street = (NSString *)CFDictionaryGetValue(addressDR, kABPersonAddressStreetKey); NSString *city = (NSString *)CFDictionaryGetValue(addressDR, kABPersonAddressCityKey); NSString *state = (NSString *)CFDictionaryGetValue(addressDR, kABPersonAddressStateKey); NSString *country = (NSString *)CFDictionaryGetValue(addressDR, kABPersonAddressCountryKey); NSString *zipcode = (NSString *)CFDictionaryGetValue(addressDR, kABPersonAddressZIPKey); ABMultiValueRef phoneMVR = ABRecordCopyValue(person, kABPersonPhoneProperty); NSArray *phones = (NSArray *)ABMultiValueCopyArrayOfAllValues(phoneMVR); ABMultiValueRef emailMVR = ABRecordCopyValue(person, kABPersonEmailProperty); NSArray *emails = (NSArray *)ABMultiValueCopyArrayOfAllValues(emailMVR); [address setObject:(nickName != nil ? nickName : @"") forKey:@"nickName"]; [address setObject:(firstName != nil ? firstName : @"") forKey:@"firstName"]; [address setObject:(lastName != nil ? lastName : @"") forKey:@"lastName"]; [address setObject:(street != nil ? street : @"") forKey:@"street"]; [address setObject:(city != nil ? city : @"") forKey:@"city"]; [address setObject:(state != nil ? state : @"") forKey:@"state"]; [address setObject:(country != nil ? country : @"") forKey:@"country"]; [address setObject:(zipcode != nil ? zipcode : @"") forKey:@"postCode"]; [address setObject:(phones != nil ? phones : [NSNull null]) forKey:@"phone"]; [address setObject:(emails != nil ? emails :[NSNull null]) forKey:@"email"]; CFRelease(emailMVR); CFRelease(phoneMVR); CFRelease(addressDR); CFRelease(addressMVR); [self dismissViewControllerAnimated:NO completion:nil]; [NativePage showWebView:address]; return NO; }
- In the ContactsViewController.m file, update
activity lifecycle methods to call and dismiss the people picker.
- Add import statements for the following files:
- AuroraWLHybrid.h
- CDVAppDelegate.h
- NativePage.h
- Create the following instance variables:
ABPeoplePickerNavigationController *peoplePicker; MyAppDelegate *myAppDelegate;
- Add the init, onBeforeShow and onAfterShow methods. Copy and paste
the following code snippet:
- (id)init { peoplePicker = [[ABPeoplePickerNavigationController alloc] init]; myAppDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; return [super init]; } - (void)onBeforeShow { } - (void)onAfterShow { [self presentViewController:peoplePicker animated:NO completion:nil]; }
- In the
viewDidLoad
method, configure theABPeoplePickerNavigationController
and add the view to the delegate. Add the following lines toviewDidLoad
:[peoplePicker setDisplayedProperties:[[NSArray alloc] initWithObjects:[[NSNumber alloc] initWithInt:kABPersonAddressProperty], nil]]; [peoplePicker setPeoplePickerDelegate:self]; [self.view setAlpha:0.0]; [myAppDelegate.window addSubview:self.view];
- Add import statements for the following files:
- Save the changes to the ContactsViewController.m file.
Integrating the native map display
- In XCode, create a new
MapViewController
class. Ensure that With XIB for user interface is selected when creating the class. This will create the header file, application class and Interface Builder file (.xib). - Add the MapKit framework to the XCode project.
- In MapViewController.h, add import statements
for the MapKit framework and update the interface to include the
MapViewDelegate
.- Add an import statement for
<MapKit/MapKit.h>
. - Update the interface definition to include the
MKMapViewDelegate
. Locate the following line:
Update it to include the delegate:@interface MapViewController : UIViewController
@interface MapViewController : UIViewController <MKMapViewDelegate>
- Define the following list of properties between the @interface
and @end lines:
@property (nonatomic, retain) IBOutlet MKMapView *mapView; @property (nonatomic, retain) IBOutlet UINavigationBar *navBar; @property (nonatomic, retain) IBOutlet UIBarButtonItem *backButton; @property (nonatomic, copy) NSString *mapCenterLat; @property (nonatomic, copy) NSString *mapCenterLong; @property (nonatomic, copy) NSString *storeName; @property (nonatomic, copy) NSString *storeLat; @property (nonatomic, copy) NSString *storeLong; @property (nonatomic, copy) NSString *storeCity; @property (nonatomic, copy) NSString *storeState; @property (nonatomic, copy) NSString *storeAddr1; @property (nonatomic, copy) NSString *storeAddr2; @property (nonatomic, copy) NSString *storeAddr3; @property (nonatomic, copy) NSString *headerTitle; @property (nonatomic, copy) MKPointAnnotation *physicalStore;
- Save the changes.
- Add an import statement for
- In the MapViewController.m, update and add
activity lifecycle methods to call and dismiss the native map.
- Add import statements for:
- NativePage.h
- CDVAppDelegate.h
- <MapKit/MapKit.h>
- Add synthesize statements for each of the properties defined in
the MapViewController.h file. For example, after
the
@implementation MapViewController
statement, add@synthesize mapView;
. - In
viewDidLoad
, add the following code after the super call:[mapView setDelegate:self]; if (self.headerTitle != nil) { [[navBar topItem] setTitle:self.headerTitle]; [[navBar topItem] setIsAccessibilityElement:YES]; [[navBar topItem] setAccessibilityLabel:self.headerTitle]; } NSString *backButtonLabel = @"Back"; [backButton setTitle:backButtonLabel]; [backButton setIsAccessibilityElement:YES]; [backButton setAccessibilityLabel:backButtonLabel];
- Implement the view lifecycle and data methods in
viewDidUnload
by adding the following methods to the class: TheviewDidAppear
method is called when the map view is displayed, and refreshes the annotations (pin icons that represent the physical store) and centers the map centered over the selected physical store. ThesetDataFromWebView
method sets the physical store properties from the data sent from the web view. ThecreateSubtitle
method displays the map annotations when clicked. ThehideMapView
method action is called when the map view is dismissed.Copy and paste the following code snippet into MapViewController.m:- (void)viewDidUnload { [super viewDidUnload]; self.mapView = nil; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // Remove previous physical store annotations from the MapView NSArray *annotations = [self.mapView annotations]; for (id annotation in annotations) { if ([annotation isKindOfClass:[MKPointAnnotation class]]) { [self.mapView removeAnnotation:annotation]; } } CLLocationCoordinate2D userLocation = {[self.mapCenterLat floatValue], [self.mapCenterLong floatValue]}; MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation, 2000, 2000); [self.mapView setRegion:region animated:NO]; physicalStore = [[MKPointAnnotation alloc] init]; [physicalStore setCoordinate:CLLocationCoordinate2DMake([self.storeLat floatValue], [self.storeLong floatValue])]; [physicalStore setTitle:self.storeName]; [physicalStore setSubtitle:[self createSubtitle]]; [physicalStore setIsAccessibilityElement:YES]; [physicalStore setAccessibilityLabel:physicalStore.subtitle]; [self.mapView setCenterCoordinate:physicalStore.coordinate animated:YES]; [self.mapView addAnnotation:physicalStore]; // Selecting the annotation to automatically open the callout does not have an effect on iOS 5.1 [self.mapView selectAnnotation:physicalStore animated:YES]; } - (void)setDataFromWebView:(NSDictionary *) data { self.mapCenterLat = (NSString *) [data valueForKey:@"mapCenterLatitude"]; self.mapCenterLong = (NSString *) [data valueForKey:@"mapCenterLongitude"]; self.storeName = (NSString *) [data valueForKey:@"storeName"]; self.storeLat = (NSString *) [data valueForKey:@"storeLatitude"]; self.storeLong = (NSString *) [data valueForKey:@"storeLongitude"]; self.storeCity = (NSString *) [data valueForKey:@"storeCity"]; self.storeState = (NSString *) [data valueForKey:@"storeState"]; self.storeAddr1 = (NSString *) [data valueForKey:@"storeAddress1"]; self.storeAddr2 = (NSString *) [data valueForKey:@"storeAddress2"]; self.storeAddr3 = (NSString *) [data valueForKey:@"storeAddress3"]; self.headerTitle = (NSString *) [data valueForKey:@"storeLocatorTitle"]; } - (NSString *)createSubtitle { NSMutableString *subtitle = [[NSMutableString alloc] init]; [subtitle appendString:self.storeAddr1]; [subtitle appendString:@", "]; [subtitle appendString:self.storeCity]; [subtitle appendString:@", "]; [subtitle appendString:self.storeState]; return subtitle; } - (IBAction)hideMapView:(id)sender { [NativePage showWebView:nil]; }
- Save the changes.
- Add import statements for:
- Use XCode's Interface Builder to add a navigation bar and map
view to the MapViewController.xib file. For more
information, see Creating View Objects from Interface Builder.
- Add a Navigation Bar to the top of the view.
- Add a Bar Button Item to the Navigation Bar.
- Add a MapView to the view and size it so that it occupies the remaining area below the Navigation Bar.
- From File's Owner, assign the outlets for the user interface objects:
- Assign the backButton outlet to the Bar Button Item
- Assign the mapView outlet to the Map View.
- Assign the navBar outlet to the Navigation Bar.
- Assign the Received Actions for hideMapView to the Bar Button Item.
- Save the changes.
Updates to storefront files to invoke native features
- WC_eardir/Stores.war/storedir/mobile20/WorklightHybrid/common/js
- WCDE_installdir/Stores/storedir/mobile20/WorklightHybrid/common/js
- WC_eardir/Stores.war/storedir/WorklightHybrid/common/js
- WCDE_installdir/Stores/storedir/WorklightHybrid/common/js
- Update the iOS hybrid application properties files with the native
class names corresponding to your classes.
- Open the following file:
- WC_eardir/Stores.war/storedir/mobile20/WorklightHybrid/ios/js/WCHybridAppProperties.js
- WCDE_installdir/Stores/storedir/mobile20/WorklightHybrid/ios/js/WCHybridAppProperties.js
- WC_eardir/Stores.war/storedir/WorklightHybrid/ios/js/WCHybridAppProperties.js
- WCDE_installdir/Stores/storedir/WorklightHybrid/ios/js/WCHybridAppProperties.js
- In the default WCHybridAppProperties.js file,
the native map class name is defined as
MapViewController
. This assumes that in your XCode project has an Objective-C class namedMapViewController
with your implementation of the native page. Add or change the properties in WCHybridAppProperties.js as appropriate.
- Open the following file:
- Update the DisplayMap.js file to pass an
additional store locator title parameter from the web view to the
native view.
- Open the following file:
- WC_eardir/Stores.war/storedir/mobile20/WorklightHybrid/common/js/DisplayMap.js
- WCDE_installdir/Stores/storedir/mobile20/WorklightHybrid/common/js/DisplayMap.js
- WC_eardir/Stores.war/storedir/WorklightHybrid/common/js/DisplayMap.js
- WCDE_installdir/Stores/storedir/WorklightHybrid/common/js/DisplayMap.js
- In the invokeNativeMap function, locate the localInfo variable
and add an additional parameter to pass in the store title using the
JSON string:
var localInfo = { ... storeAddress3: address3 };
- Add
storeLocatorTitle: document.title
so that the resulting code resembles the following:var localInfo = { ... storeAddress3: address3, storeLocatorTitle: document.title };
- Save the file.
- Open the following file:
Troubleshooting
- When updating files in Worklight Studio under the
- Exit XCode.
- Return to Worklight Studio, perform a build of the iPhone environment.
- Right-click the iphone environment, select to re-launch XCode.
path, followed by a , files in the XCode
project might not be refreshed until the project is reloaded. Do the
following: - 302 redirects are not handled correctly
in the Worklight iPhone hybrid app and the
webViewDidFinishLoad
method might never be called. This is a known issue In Apache Cordova version 2.6 which is packaged with Worklight 6.0. For more information about this known issue, see Device ready is not fired in IOS after receiving a 302 temporary redirect from remoteserver.One workaround is to update affected JSP files so that the redirect is triggered earlier in the page load:- Open the following file:
- WC_eardir/Stores.war/storedir/mobile30/ShoppingArea/CheckoutSection/OrderBillingAddressSelection.jsp
- WCDE_installdir/Stores/storedir/mobile30/ShoppingArea/CheckoutSection/OrderBillingAddressSelection.jsp
- Locate the following line:
<html xmlns="http://www.w3.org/1999/xhtml" lang="${shortLocale}" xml:lang="${shortLocale}">
- Add the JavaScript redirect to the order billing details page,
when there are no valid addresses associated with the current user
and the user is not in the My Account flow. Copy and paste the following
code snippet above the line in the preceding step:
<c:set var="hasValidAddresses" value="false"/> <c:forEach var="payment" items="${usablePayments.usablePaymentInformation}"> <c:if test="${fn:length(payment.usableBillingAddress) > 0 && !hasValidAddresses}"> <c:set var="hasValidAddresses" value="true"/> </c:if> </c:forEach> <c:choose> <c:when test="${_worklightiOSHybridApp && !hasValidAddresses && fromPage != 'MyAccount'}"> <wcf:url var="OrderBillingDetailsURL" value="m30OrderBillingDetails"> <wcf:param name="langId" value="${langId}" /> <wcf:param name="storeId" value="${WCParam.storeId}" /> <wcf:param name="catalogId" value="${WCParam.catalogId}" /> <wcf:param name="orderId" value="${WCParam.orderId}" /> <wcf:param name="fromPage" value="${WCParam.fromPage}" /> </wcf:url> <html xmlns="http://www.w3.org/1999/xhtml" lang="${shortLocale}" xml:lang="${shortLocale}"> <head> <meta http-equiv="refresh" content="0;url=${OrderBillingDetailsURL}"/> </head> <body> </body> </html> </c:when> <c:otherwise>
- Add the closing
<c:otherwise>
and<c:choose>
tags to the newly added code snippet. The closing tag should be placed at the end of the existing</html>
block. - Save the file.
- Open the following file: