Portage Bay
Solutions
Portage Bay

Updating FileMaker Files in iOS

The process of updating FileMaker files in iOS has always been sort of a question mark, with multiple ideas for how to best accomplish it, each with their own limitations. At PBS, we developed a process to update files in iOS a number of years ago, but we’ve updated the process with some of the newer FileMaker 18 features that make the process more seamless, while altogether giving it a more native look and feel.

In our approach, we decided to use the “Helper File” method, with a hosted file that handles the entire file upgrade process, but with one small change. Instead of calling the helper file to get the version number, we’re using a small html file that holds the version number.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Version</title>
</head>
<body>
    1.01
</body>
</html>

We put an html file like the one above on one of our web servers, then reference it in the iOS file during the startup script using “Insert from URL” to check the version number.

# Checks for a newer version from the specified URL
# 
Go to Layout [ “Preferences” ( PREFERENCES ) ; Animation: None ]
Set Variable [ $url ; Value: PREFERENCES::VERSION_URL ] 
Set Error Capture [ On ]
Insert from URL [ With dialog: Off ; Target: $v_response ; $url ] 
# 
If [ Get(LastError) > 0 ] 
    Set Variable [ $result ; Value: "error" ] 
Else
    Set Variable [ $BodyPosition ; Value: Position ( $v_response ; "<body>" ; 1 ; 1 ) ] 
    Set Variable [ $Version ; Value: GetAsNumber ( Middle ( $v_response ; $BodyPosition+ 7; 4 ) ) ] 
    Set Error Capture [ On ]
    Allow User Abort [ Off ]
    # 
    # New Version is Available!
    If [ $Version > T14_PREFERENCES::NT_Version or $$testing = 1 ] 
        Set Variable [ $result ; Value: $version ] 
    End If
End If
Go to Layout [ original layout ; Animation: None ]

#Exit with either the error or version number
Exit Script [ Text Result: $result ]

If the version number it returns is greater than the local version number, it compiles the data from the PREFERENCES table into a JSON object (more on that later). It then calls a script in the hosted helper file using the FMP URL to handle the update. By grabbing the version number from an html file, the “version check” process doesn’t require FM Go to open another data source, which keeps it lightweight while also checking for internet access in the same step.

# Compile preferences into JSON
Perform Script [ Specified: From list ; “PREFStoJSON” ; Parameter:    ]
Set Variable [ $JSON_PREFS ; Value: Get ( ScriptResult ) ] 

# Set Variables and the Update Script in the Helper File. The Helper file will take over from here.
Set Variable [ $IP ; Value: "OurDevServer.portagebay.com" ] 
Set Variable [ $FileName ; Value: "HelperFile.fmp12" ] 
Set Variable [ $UserName ; Value: "DevUser" ] 
Set Variable [ $password ; Value: "DevPassword" ] 
Set Variable [ $scriptname ; Value: "Update" ] 
Set Variable [ $url ; Value: "FMP://" & $UserName & ":" & $password & "@" & $IP &"/" & $FileName & "?script=" & $scriptName & "&param=" & $JSON_PREFS ] 
Open URL [ With dialog: Off ; $url ]

The helper file then manages the closing and deletion of the old file, exporting the new cloned file, and opening the new file to complete the upgrade process. Since FileMaker can now manage dynamic data sources, the helper file can reference the old file using a path as a variable. With this feature, we can then use the new file management syntax introduced in FileMaker 18 to delete the old file from the iOS device.

Initially, we tried using the new “Close File” feature, but found unexpected results. When calling a “Close File” at any point in iOS, the entire script stack is brought to a halt since the old file is still technically in use. So instead, we found that using the “Close Window” to close the old file before it’s deleted from the device keeps the stack running.

# Set data source
Set Variable [ $$url ; Value: Get(DesktopPath) & UpgradeHelper::FileName ] 
# 
# Close old mobile file by window name
Close Window [ Name: "MobileFile" ] 
# 
# Delete old mobile file
Delete File [ Target file: “$$url” ] 
# 
# Export NEW mobile file
Export Field Contents [ Helper::File ; “$exportPath” ; Automatically open ; Create folders: Off ] 
# 
# open file by data source
Open File [ Open hidden: Off ; “MobileFile.fmp12” ]
The user is shown a layout with an “Upgrading to version…” message during this whole process, displaying our handy  javascript spinner-indicator .

The user is shown a layout with an “Upgrading to version…” message during this whole process, displaying our handy javascript spinner-indicator.

Another significant change we made to this process was adding the ability to transfer data from the old file by compiling and passing a JSON object from the old file to the helper file, then from the helper file back into the new file. Since FM Go can only import .fmp12 files, but cannot export .fmp12 files, using JSON to pass the data seems to be the most logical approach.

# Compiles the PREFS to JSON to pass into Helper file during file update process
# 
Go to Layout [ “Preferences” (PREFERENCES) ; Animation: None ]
# Export logo images to temp locations. Use Documents folder in iOS instead of TempPath
Set Variable [ $DocsPath ; Value: Get ( DocumentsPath ) ] 
# 
Set Variable [ $LogoPrint_file ; Value: GetContainerAttribute ( PREFERENCES::Logo_Print ; "filename" ) ] 
Set Variable [ $LogoPrint_path ; Value: $DocsPath & $LogoPrint_file ] 
Export Field Contents [ PREFERENCES::Logo_Print ; “$LogoPrint_path” ; Create folders: Off ] 
# 
Set Variable [ $LogoSystem_file ; Value: GetContainerAttribute ( T14_PREFERENCES::Logo_System ; "filename" ) ] 
Set Variable [ $LogoSystem_path ; Value: $DocsPath & $LogoSystem_file ] 
Export Field Contents [ T14_PREFERENCES::Logo_System ; “$LogoSystem_path” ; Create folders: Off ] 
# 
Go to Layout [ “Preferences” (PREFERENCES) ; Animation: None ]
# Build JSON object
Set Variable [ $JSON_PREFS ; Value: JSONSetElement ( "{}" ; // [ GetFieldName ( FIELD ) ; VALUE ; JSONString ] ; //Logos  [ GetFieldName ( T14_PREFERENCES::Company_Logo_Report ) ; $LogoReport_path ; JSONString ] ;  [ GetFieldName ( T14_PREFERENCES::Company_Logo_System ) ; $LogoSystem_pa… ] 
Set Variable [ $JSON_PREFS ; Value: JSONFormatElements ( $JSON_PREFS ) ] 
# 
Exit Script [ Text Result: $JSON_PREFS ]

Using this process, we’re able to retain all sorts of “global” preferences and user-specific data. We’re even able to retain files in container fields by exporting out of the old file and including the exported file paths in the JSON object. This way, the new file can import the container files back into their respective containers after it’s opened.

# Get JSON Prefs from param
Set Variable [ $JSON_PREFS ; Value: JSONFormatElements ( Get ( ScriptParameter ) ) ] 
# 
Set Variable [ $fields_list ; Value: JSONListKeys ( $JSON_PREFS ; "" ) ] 
Set Variable [ $fields_total ; Value: ValueCount ( $fields_list ) ] 
Set Variable [ $counter ; Value: 0 ] 
Loop
    Set Variable [ $counter ; Value: $counter + 1 ] 
    Set Variable [ $fieldname ; Value: GetValue ( $fields_list ; $counter ) ] 
    # 
    # Insert logos
    If [ $fieldname = "PREFERENCES::Logo_Print" ] 
        Set Variable [ $LogoPrint_path ; Value: "image:" & JSONGetElement ( $JSON_PREFS ; $fieldname ) ] 
        Go to Field [ PREFERENCES::Logo_Print ] 
        Insert Picture [ “$LogoReport_Path” ] 
        # Delete the temp image file
        Delete File [ Target file: “$LogoReport_Path” ] 
    Else If [ $fieldname = "PREFERENCES::Logo_System" ] 
        Set Variable [ $LogoSystem_path ; Value: "image:" & JSONGetElement ( $JSON_PREFS ; $fieldname ) ] 
        Go to Field [ PREFERENCES::Logo_System ] 
        Insert Picture [ “$LogoSystem_path” ] 
        # Delete the temp image file
        Delete File [ Target file: “$LogoSystem_path” ] 
    Else
        # Set PREFS by fieldname
        Set Field By Name [ $fieldname ; JSONGetElement ( $JSON_PREFS ; $fieldname ) ] 
    End If
    # 
    Set Variable [ $error ; Value: Get ( LastError ) ] 
    # 
    Exit Loop If [ $counter ≥ $fields_total or $error ≠ 0 ] 
End Loop

Once that’s all complete (and free of errors), the script notifies the user that the upgrade is complete, and allows the user to log into the new file.

If [ $error = 0 ] 
    # Success, force re-login
    Re-Login [ With dialog: On ] 
    # 
    If [ Get ( LastError ) = 0 ] 
        Perform Script [ Specified: From list ; “Startup” ; Parameter:    ]
        Show Custom Dialog [ "The MobileFile has been updated to version " & PREFERENCES::Version ] 
    Else
        Close File [ Current File ]
    End If
Else

# Notify the user than an error occurred
    Show Custom Dialog [ "An error has occurred during the update. Please contact your system administrator." ] 

End If

All in all, we’ve found this to be a pretty solid technique for handling file upgrades in iOS. It’s pretty robust and can be implemented into other systems with little modifications. I’m sure we’ll make tweaks to it as we implement it in other client’s solutions. With the introduction of the data file features in FileMaker 18, we’re already looking into the possibility of making this process entirely modular by having the ability to have the helper file create the html file, so we’re sure there’s more to expand upon down the road. If you have any thoughts on this process or would like assistance implementing this into your current system, feel free to leave a comment below or contact us.