Installation and version management of mobile applications without any MDM solution

From my SCN blog post:

http://scn.sap.com/community/mobile/blog/2014/08/24/installation-and-version-management-of-mobile-applications-without-any-mdm-solution

While testing applications, we used the below strategies and it took us some time to work these out. So I sum these up as reference – I trust people will find it useful. These are based on my experience in dealing with iOS apps.

Problem 1: I was working on an enterprise mobile application project but we couldn’t use the organisation’s pre-existing mobile device management ( MDM ) solution. This can happen due to any number of factors – in our case, the app was simply too big to be supported by the current version of MDM solution (it’s not a SAP MDM solution ).

Solution :

We used the below alternatives and they can be employed to work.

1. DropBox. A simple html page can be created in DropBox which points to the app’s plist file ( property list file ) and the plist file in turn will point to the relative location of the .app file. I kept the app in the Public folder of dropbox.

Let me draw a picture to make it clearer.

Screen Shot 2014-08-23 at 11.07.08 pm.png

2. TestFlight: I wasn’t aware of this site when we were first working on testing release I but found it extremely useful for subsequent releases. You can set up a project team, assign users to test and check the devices registered.

See the sample screenshot below.

Screen Shot 2014-08-23 at 6.png

3. Through a public site. You’ll need infrastructure team to create a publicly accessible domain ( if you want them to access via normal internet ).

Add the below MIME types to your web server ( for IIS ).

Mime type for plist -> application/xml

Mimet type for ipa -> application/octet-stream

4. The most obvious one : If the users can come to your desk, of course just use Xcode to install by making the device a developer device.

Hints on creating the HTML page:

a) Create the HTML page – this page will point to the plist file.

e.g.

<a href=”itms-services://?action=download-manifest&url=http://dl.dropbox.com/u/xxxxxxxxx/app_live.plist“>Download App</a>

This will create a quick and dirty page for users to install the app.

Screen Shot 2014-08-23 at 5.58.09 pm.png

Of course, you can make it better by adding icons etc.

b) The plist file will in turn point to the .ipa file. You’ll need to edit it manually from the one generated by Xcode.

<?xml version=”1.0″ encoding=”UTF-8″?>

<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd“>

<plist version=”1.0″>

<dict>

<key>items</key>

<array>

<dict>

  <key>assets</key>

  <array>

   <dict>

    <key>kind</key>

    <string>software-package</string>

    <key>url</key>

    <string>http://www.domain.com/app/Application.ipa</string>

   </dict>

  </array>

  <key>metadata</key>

  <dict>

   <key>bundle-identifier</key>

   <string>com.domain.ApplicationBundle</string>

   <key>bundle-version</key>

   <string>1.0.0</string>

   <key>kind</key>

   <string>software</string>

   <key>title</key>

   <string>Name Of Application</string>

  </dict>

</dict>

</array>

</dict>

</plist>

Problem 2: While testing we realised that sometimes the user’s app version was outdated and they’ll report issues or have inconsistencies due to a lower version. This can cause a lot of confusion.

Solution :

 

– Store the supported version of the app on a text file in an external URL .

– Get the version of the app and compare the version from an external URL.

– If the version in the app is lower than the supported version , display a message asking the user to upgrade and quit.

The supported version may not be the current version of the app – for minor UI tweaks, we may not change the supported version but if there are changes to the underlying object structure, the supported version will need to be changed.

Sample Objective-C code:  We can put it in the Main View Controller.

Main view controller :

    [self checkifObsoleteAppVersion];

….

– (void) checkifObsoleteAppVersion{

 

//Get version from external site

    NSString *versionURLString = @”http://URL/version.txt;  //<<– Version information stored in a simple text file

    NSURL *versionURL = [NSURL URLWithString:[versionURLStringstringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

    NSString *versionOnExternalSite = [[NSString stringWithContentsOfURL:versionURLencoding:NSUTF8StringEncoding error:nilsubstringToIndex:6];

 

//Get version from App

    NSString *versionFromApp = [self.syncManager.appVersion substringToIndex:6];

 

//Format the versions to numbers

    NSNumberFormatter *versionFromAppNo = [[[NSNumberFormatter alloc]init]autorelease];

    NSNumber *appNo = [ versionFromAppNo numberFromString:versionFromApp];

 

    NSNumberFormatter *versionFromAppNoExt = [[[NSNumberFormatter alloc]init]autorelease];

    NSNumber *appNoExt = [ versionFromAppNoExt numberFromString:versionOnExternalSite];

       

//App version shouldn’t be lower than the one maintained in version file

       

    if ([appNo floatValue] < [appNoExt floatValue] )

    {

 

// Show a message

        UIAlertView *appObsoleteView = [[[UIAlertView alloc]initWithTitle:@”App version obsolete”message:@”A new version of App has been released and this version is obsolete.Please upgrade App to the new version.” delegate:self cancelButtonTitle:@”Exit” otherButtonTitles:nil, nil]autorelease];

        appObsoleteView.tag = 666;

        [appObsoleteView show];

 

    }

 

}

 

@end

 

Problem 3: Just putting as a reference to consolidate the information in a single place. Users were required to be manually registered and there were issues caused sometimes due to wrong information being set up due to manual process.

 

Solution:

I created a solution using SUP’s Java APIs and shared the experience in an earlier blog.

 

http://scn.sap.com/community/mobile/blog/2013/05/30/automating-user-registration-in-sybase-control-center

 


Retain selection screen values on program restart for transactions with chosen selections

From my blog on SCN

http://scn.sap.com/community/abap/blog/2014/08/20/retain-selection-screen-values-on-program-restart-for-transactions-with-chosen-selections

I had an interesting last week and what looked easy at the outset did my head in for quite a while. I tried various methods and the below approach was able to give me a satisfactory solution.

Let’s have a look at the high level requirement.

– Copy a standard SAP transaction and customise it . Below picture describes the requirement

Screen Shot 2014-08-16 at 7.28.17 pm.png

The transaction is started with only few fields of the complete selection screen as the selection screen has too many fields and may confuse the users.

2.PNG

The users need to navigate back and forth between different modes of output screen. At the first display of output screen ( let’s call them as levels – so the output screen is at level 0 when initially displayed and if called again goes to level 1 and so on . Similarly when the user comes back from a higher level of screen, the level will decrease : from 1 to 0 ). And of course when the program navigates back from level 0 of selection screen, it should display the selection screen.

I prototyped using a simple program using flight model.

– Selection Screen : Contains all fields.

All fields.PNG

However, the transaction is always run with a variant which hides the last field.

Selection Screen.PNG

– Let’s test with some data.

Selection Test.PNG

We get the level 0 output screen.

Level 0.PNG

Now click on filter ( search icon on top right )

Filter.PNG

and we get level 1 screen.

Level 1.PNG

Looks good so far. Now, let’s try going back – going back to level 0 gives the screen as anticipated. However, when we go back and see that the selection screen parameters have gone back. The selection screen has gone blank !

Selection Screen.PNG

Let’s see what’s going on.

As we need to keep track of different levels of screen, if the level of screen is greater than 0.

…….

ELSEIF syucomm = ‘EXIT’.

    IF gv_list_level > 0.

      gv_list_level = gv_list_level 1.

      gt_flight[] = gt_master_flight[].

      CALL SCREEN 100.

    ENDIF.

When we want to go back to selection screen from screen at level 0, we use below:

SUBMIT zsubmit_flight

            WITH report EQ ‘ZTESTFLIGHT’

            WITH variant = ‘ZFLIGHT_VAR’

            WITH SELECTION-TABLE gt_seltab .

zsubmit_flight is a standard SAP report used by the report and can’t be changed by us.

SUBMIT (REPORT) WITH VARIANT  = VARIANT

                    USING SELECTIONSET VARIANT

                    VIA SELECTION-SCREEN

Workaround:

1) Store selected values by call of RS_REFRESH_FROM_SELECTOPTIONS 

2) Export the selection table before doing a program restart.

EXPORT gt_seltab TO MEMORY ID gc_sel_mem.

3)  Retrieve the selection table AT SELECTION-SCREEN OUTPUT.

RS_VARIANT_CONTENTS gives the parameters and select-options actually visible in the variant.

IMPORT gt_seltab FROM MEMORY ID gc_sel_mem.

  IF NOT gt_seltab[] IS INITIAL.

    CALL FUNCTION ‘RS_VARIANT_CONTENTS’

      EXPORTING

        report              = ‘ZTESTFLIGHT’

        variant              = ‘ZFLIGHT_VAR’

      TABLES

        l_params            = lt_params

        l_selop              = lt_selops

        valutab              = lt_value

      EXCEPTIONS

        variant_non_existent = 1

        variant_obsolete    = 2

        OTHERS              = 3.

    IF sysubrc <> 0.

clear: lt_value,

          lt_selops,

          lt_value.

    ENDIF.

* Update parameters values

    LOOP AT lt_params INTO lw_param.

      READ TABLE gt_seltab REFERENCE INTO lo_values WITH KEY selname = lw_paramname.

      IF sysubrc = 0.

        lv_attr = lo_values->selname.

        TRANSLATE lv_attr TO UPPER CASE.

        ASSIGN (lv_attr) TO <fs_attr_val>.

        <fs_attr_val> = lo_values->low.

      ENDIF.

    ENDLOOP.

* Update select-option values

    LOOP AT lt_selops INTO lw_param.

      READ TABLE gt_seltab REFERENCE INTO lo_values WITH KEY selname = lw_paramname.

      IF sysubrc = 0.

        CONCATENATE lo_values->selname ‘SIGN’ INTO lv_attr SEPARATED BY ‘-‘.

        TRANSLATE lv_attr TO UPPER CASE.

        ASSIGN (lv_attr) TO <fs_attr_sign>.

        <fs_attr_sign> = lo_values->sign.

        CONCATENATE lo_values->selname ‘OPTION’ INTO lv_attr SEPARATED BY ‘-‘.

        TRANSLATE lv_attr TO UPPER CASE.

        ASSIGN (lv_attr) TO <fs_attr_option>.

        <fs_attr_option> = lo_values->option.

        CONCATENATE lo_values->selname ‘LOW’ INTO lv_attr SEPARATED BY ‘-‘.

        TRANSLATE lv_attr TO UPPER CASE.

        ASSIGN (lv_attr) TO <fs_attr_low>.

        <fs_attr_low> = lo_values->low.

        CONCATENATE lo_values->selname ‘HIGH’ INTO lv_attr SEPARATED BY ‘-‘.

        TRANSLATE lv_attr TO UPPER CASE.

        ASSIGN (lv_attr) TO <fs_attr_high>.

        <fs_attr_high> = lo_values->high.

        lv_attr = lo_values->selname.

        TRANSLATE lv_attr TO UPPER CASE.

        ASSIGN (lv_attr) TO <fs_attr_main>.

        CONCATENATE lv_attr ‘[]’ INTO lv_attr_tab.

        ASSIGN (lv_attr_tab) TO <fs_attr_tab>.

        IF lo_values->low IS NOT INITIAL OR lo_values->high IS NOT INITIAL.

          REFRESH <fs_attr_tab>.

          APPEND <fs_attr_main> TO <fs_attr_tab>.

        ENDIF.

      ENDIF.

    ENDLOOP.

  ENDIF.

– Create a transaction ‘ZFLIGHT’ with program ZTESTFLIGHT , variant ZFLIGHT_VAR.

The code can be referred here:

The code can be referred here:

ZSUBMIT_FLIGHT

https://github.com/viksingh/ABAP_Demo/blob/master/ZSUBMIT_FLIGHT

Program ZTESTFLIGHT with issues:

https://github.com/viksingh/ABAP_Demo/blob/master/ZTESTFLIGHT_1

Program  ZTESTFLIGHT with corrections:

ABAP_Demo/ZTESTFLIGHT_2 at master · viksingh/ABAP_Demo · GitHub

The key here is function module RS_VARIANT_CONTENTS and the dynamic update of selection screen after restart of a transaction.

ZSUBMIT_FLIGHT

https://github.com/viksingh/ABAP_Demo/blob/master/ZSUBMIT_FLIGHT

Program ZTESTFLIGHT with issues:

https://github.com/viksingh/ABAP_Demo/blob/master/ZTESTFLIGHT_1

Program  ZTESTFLIGHT with corrections:

ABAP_Demo/ZTESTFLIGHT_2 at master · viksingh/ABAP_Demo · GitHub

The key here is function module RS_VARIANT_CONTENTS and the dynamic update of selection screen after restart of a transaction.


ABAP dynamic internal table sort saves the day

Requirement to refresh the ALV to display only display unique fields. Thankfully the sort fields of an itab can be specified dynamically now.

TRY.

SORT gt_output BY (lt_sorttab).
DELETE ADJACENT DUPLICATES FROM gt_output COMPARING ALL FIELDS.
CATCH cx_sy_dyn_table_ill_comp_val.
gt_output[] = lt_output[].
ENDTRY.