Skip navigation
All Places > AppStudio for ArcGIS > Blog
1 2 3 Previous Next

AppStudio for ArcGIS

90 posts

We are excited to announce the release of a new sample app - Nearby - available today in AppStudio for ArcGIS! This sample showcases how to find the nearest places of interest worldwide, filter them by category, and provide step-by-step directions from your current location. Give this new app a spin using the AppStudio Player (readily available to run in the samples tab) on your favorite mobile or tablet.

 

The source code for this app is available for you to learn and customize further in AppStudio Desktop (4.0 and above) along with all the other great sample apps. Just open Desktop, click on the New App button, and either browse or type Nearby in the search box to find the sample and click Create.

 

Please check this blog for feature highlights

 

Technical Highlights                          

 

Geocoding

 

We accomplish geocoding process by using Esri’s World Geocoding Service which allows you to search for places using high-level concepts such as a category level 1 descriptor (e.g “POI” for places of interest), a category level 2 descriptor (eg. “Food”) or a category level 3 descriptor which is more specific (eg. “Japanese food”). The category filters and other search criteria such as the maximum number of results, output fields, and search area are defined using the ArcGIS Runtime SDK’s GeocodeParameters.

 

Geocoding in this sample is implemented using the LocatorTask class. Note that we are using an online locator URL in the sample, but the URL can be replaced with your own custom online/offline locator. Inside the project folder, you will find the LocatorTask in the Views/Geocodeview.qml file (line 333).

 

 

LocatorTask {
   id: locatorTask
   url: "http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer"
   .
   .
   .
}

 

Routing 

 

In order to provide routing, we utilize Esri’s routing service which allows us to generate directions and display them to the user both as a route drawn on the map and as a list of turn-by-turn directions. 

 

We accomplish this using ArcGISRuntime's RouteTask class. Note that Esri’s online routing service requires authentication and thus all your routing requests will have to be authenticated with your ArcGIS online clientId  and clientSecret. Remember to add both of these keys inside the project in file Views/RoutingView.qml on lines 286 and 287 as shown below in the graphic below. 

 

OAuthClientInfo {        
   id: clientInfo
   clientId: "<<YOU CLIENT ID GOES HERE>>"
   clientSecret: "<<YOUR CLIENT SECRET GOES HERE>>"
   oAuthMode: Enums.OAuthModeApp


Credential {
   id: cred
   oAuthClientInfo: clientInfo
}
RouteTask {
   id: routeTask
   url: "http://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World"
   credential: cred
   .
   .
   .
}

 

The sample also showcases how to get directions for two transport modes namely walking and driving. Switching between walking and driving directions is accomplished by simply setting three values of the RouteTask’s RouteParameters property as shown in the snippet below.  

 

function setToWalkMode() {
   routeParameters.travelMode.type = "WALK";
   routeParameters.travelMode.impedanceAttributeName = "WalkTime";
   routeParameters.travelMode.timeAttributeName = "WalkTime";
}

function setToDriveMode() {
   routeParameters.travelMode.type = "AUTOMOBILE";
   routeParameters.travelMode.impedanceAttributeName = "TravelTime";
   routeParameters.travelMode.timeAttributeName = "TravelTime";
}

 

Project Structure 

 

image showing nearby sample app project structure

 

When you open the sample files you will see that the project is divided into four folders as follows:

  • Assets folder contains all localized assets such as images, text strings, and colors that are used in the app.
  • Controls and Widgets contain components that are used to complete and manage the user interface.
  • Views contain files that implement the main functionalities of the app.
    • MapView.qml adds a map and provides all other functions for interacting with the map such as displaying graphics for found places and drawing routes.
    • RouteView.qml implements routing functionalities.
    • GeocodeView.qml, SearchView.qml, PlaceListView.qml and FiltersView.qml implement geocoding functionalities. We put these views in separated files in order to avoid having overly large code files.

 

We enjoyed creating this sample for you and hope you find it helpful as you create amazing native apps in AppStudio with nearby functionalities. Stay tuned for more samples rolling out soon. 

 

Resources:

 

 

  • New to AppStudio? Check out this website to learn more about what AppStudio for ArcGIS can do to you and your organization. 
  • Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial. 
  • Follow us on Twitter @AppStudioArcGIS    to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase.
  • The AppStudio team periodically hosts workshops and webinars; please sign up for the AppStudio newsletter if you are interested in information regarding AppStudio events.

 AppStudio for ArcGIS is a cross-platform application development tool which enables our customers to create apps that work on a range of devices and platforms. Be it mobile devices running Android or iOS, or desktops with Windows, macOS or Linux, we strive to provide a seamless experience to our customers. 

 

In order to ensure that your apps continue to work on all your devices, we proactively test each update of our supported platforms to capture new issues and attempt to resolve them before you can find them! While testing our apps with iOS 13, we found a couple of issues which need your attention.  

 

 

Accessing the photo gallery on iOS 13 

 

We found that on iOS 13, any app that tries to open the iOS photo gallery freezes while accessing it. On further investigation, we found that this was caused due to an underlying bug in the Qt Framework, the details of which can be found in the Qt bug reports website  . This bug has been resolved and the fix will be available in Qt 5.12.6. However, since AppStudio for ArcGIS 4.0 is built on Qt 5.12.3, we have been working with the Qt Company to obtain a patch for this bug and we have already applied the patch to our cloud Make build servers.  

 

 

How does this affect you and what do you need to do? 

 

This bug only affects apps running on iOS 13 and that allow users to access the photo gallery to browse photos saved on their iOS device. If you have recently built such apps using Cloud Make from AppStudio for ArcGIS 4.0, a simple re-build of your app should fix this issue.  

 

If you are using Player to test or deploy your apps, we will patch the player and resubmit to the app store. So a simple update to player from app store should fix this issue. 

 

If you are planning to build your iOS app using Local Make, please contact us at appstudiofeedback@esri.com. Since the fix for this issue needs to be applied to the Qt installation, our team can provide guidance and support to you to implement the fix. 

 

 

New requirement while publishing iOS apps to Apple App Store 

 

Recently Apple has updated the requirements that apps need to fulfil before they can be published on the App Store. Starting with iOS 13, apps that need to access Bluetooth need to include a new Product List key – NSBluetoothAlwaysUsageDescription. The details of this new key are available in Apple’s developer documentation   .  With this hotfix, we have updated our cloud Make build servers to include this key in iOS apps that are built using cloud Make. If you intend to use local Make to build your iOS app and publish it to App Store, our team can guide you to ensure that your app meets this requirement. 

 

 

I hope this has been helpful and we look forward to your feedback at appstudiofeedback@esri.com.  

 

 

  • New to AppStudio? Check out this website    to learn more about what AppStudio for ArcGIS can do to you and your organization. 
  • Become an AppStudio for ArcGIS developer! Watch this video    on how to sign up for a free trial. 
  • Follow us on Twitter @AppStudioArcGIS    to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase   .
  • The AppStudio team periodically hosts workshops and webinars; please sign up    for the AppStudio newsletter if you are interested in information regarding AppStudio events. 

 

The AppStudio for ArcGIS 4.0 release recently rolled out with many exciting new features and enhancements. In this blog, let’s look at the new features and improvements added to the  AppStudio Player for ArcGIS. The Player allows you to instantly view AppStudio apps on a real device (desktop or mobile) without requiring a full build and deployment cycle.

 

You can download the latest Player from the Apple App store or Google Play store.

 

1. New setting to change languages on the fly 

AppStudio Player is available in over thirty languages, and by default, it will be set to the language of your device. However, if you want to test an app in a different language, this required that you change the language setting for the entire device. 

 

We are excited to bring a brand-new feature in this release that allows you to change the app’s language even without changing the device’s language. This new feature brings huge benefits, especially when testing app localization. (this could save you a whole lot of time). If your AppStudio app includes translation files, you can easily test your app on different languages by changing the Player’s language in the Settings page.   

 

         

                                                                               Player Language setting

 

2. New setting to clear local apps  

A recent request from our customers was to have the ability to clear or remove all the locally downloaded apps at once. We are excited to bring this handy functionality into AppStudio 4.0, which is available in the Settings page.   

 

          

                                                                           Player Clear Local Apps setting

 

3. AppStudio Player Enterprise template now supports opening web apps 

The AppStudio Player template is widely used as an enterprise distribution solution, and our users love to share and distribute their organization’s apps through this custom Player with their custom branding and configurations. One popular request we’ve heard from our users is the ability to open web apps along with the AppStudio native apps. 

We are excited to announce that with the 4.0 release, we have added a new Web Apps page in Player’s enterprise template to open web apps such as web maps, web scenes, Web AppBuilder apps, Story Maps, and Survey123 web forms available in your organization.  

Note: that this feature is only available if you are deploying your own version of the AppStudio Player enterprise template. Please let us know if you want us to support this feature also in the released app store version of AppStudio Player. 

          

  

                                                                       Open web apps in Player

 

Follow the following quick steps to configure an enterprise Player template to include your own web apps (feel free to go to step 2 if you already have a custom Player).  

 

Please make sure you have the latest version of AppStudio Desktop (4.0), and it is licensed at the Developer Edition.  

 

Step 1: 

  • Open AppStudio for ArcGIS 
  • Sign in to your ArcGIS Online or Enterprise account with the AppStudio for ArcGIS Developer Edition license  
  • Click on the purple New App button, navigate to the Enterprise tab, select Player (4.0 Template), and click on the Create button.  
  • Upload the Player you created 
  • Click on the Settings tool on the side panel  
  • Go to Licensing section and Register an ArcGIS Client ID to allow users to sign into the Player with OAuth authentication, then click Apply 

 

Step 2: 

  • Create a new group in your organization and add your web apps in it. The group can be private or public. If the group is private, you will need to sign into Player to access the group and the web apps within the group. 
  • Copy the group ID   

 

Step 3: 

  • Open the Settings tool while your custom Player is selected in AppStudio Desktop 
  • Go to Properties > General tab 
  • Provide the group ID that you have created from step 2 and enter it in the Web Apps Group ID field 
  • Click on Apply and run your Player app to test 

 

Create a new enterprise Player template

 

 

If you wish to learn more about customizing AppStudio Player, please check this tutorial.  

 

4. Bug fixes and other enhancements:  

  • Support for iOS 13  
  • Fixed the bug where local apps were not showing in emulators and simulators  
  • Fixed the bug where the Reset button is not showing when Sample page is hidden (enterprise template) 
  • Fixed the bug where Remote console option was still showing on the app loader side menu when disabling this property in settings (enterprise template) 
  • Fixed a few texts overflows issues in the Player item cards  
  • Fixed a bug where the wrong IP address was displayed in Remote Console while connecting to the desktop Console  
  • Few enchantments made to launching Player via the command line (desktop) 
  • Player enterprise template now supports showing the branding icon in both Home and About page instead of the default icon.  
  • New 4.0 samples and templates are now available in the Player Samples page 
  • Runtime for ArcGIS included in the Player updated to the latest version 100.6  

  

We hope you like the new features and enhancements added to AppStudio Player and its Enterprise template. We would love to hear feedback or any missing feature request from you to make your app development experience easier and faster. Please reach us at appstudiofeedback@esri.com or contact Esri support.  

 

 

 

 

  • New to AppStudio? Check out this website to learn more about what AppStudio for ArcGIS can do to you and your organization.
  • Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial.
  • Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase.
  • The AppStudio team periodically hosts workshops and webinars; please sign up for the AppStudio newsletter if you are interested in information regarding AppStudio events.

The App Templates in AppStudio Desktop have been updated to work with the AppStudio 4.0. All four templates now support Runtime 100.6. Here is a list of other updates to the templates in this release: 

 

3D Scene viewer

 

  • In the AppStudio 4.0 beta release we introduced a new 3D Scene Viewer template - to discover, browse and interact with ArcGIS 3D maps (web scenes). Based on your feedback we have now added support for the offline scenes aka mobile scene packages in this release. Similar to other AppStudio template apps, you configure the template to consume your desired global web scenes and mobile scene packages by providing their item ids hosted within your organization or publicly across ArcGIS Platform.
  • This template now allows you to do sign-in to your organization across ArcGIS Platform to display the secured content (web scenes & mobile scene packages) and to download mobile scene packages. Once downloaded onto your device, the mobile scene package will be available to operate without any internet connection. Below is a quick video on how to display a mobile scene package. 

 

Note: To allow users to download a mobile scene package without signing in to an ArcGIS organization, Enable Anonymous Use can be used while creating, if you have licensed ArcGIS Pro with the ArcGIS Publisher extension.

 

Let us know what do you want to see in the future release in the comment below or by email.

 

 

Map Viewer

 

  • BUG-000122909 - Resolved issue to avoid mobile device screen locking while a mobile map package is being downloaded. If a user is downloading one or multiple mobile map packages, the device wouldn't go in sleep mode until the download is complete or user manually turns off the screen. 
  • The template now checks if a client ID is available before the user continues with a sign-in operation. If the client ID is not provided in the app, it will prompt the user.
  • Online and offline gallery sections now have individual limits to show the number of items in the gallery. For example, if 10 is set as the limit, the gallery will load up to 10 items each for the two categories. Previously the limit was shared between the two.  
  • AppFramework.Controls library reference has been removed from the Map Viewer template as the AppStudio Framework has deprecated this library.
  • Map Viewer template has been updated to use ECMA 7.0 javascript promises.
  • Other bug fixes and UI enhancements.

 

Quick Report

 

  • BUG-000124162- Resolved issue with submitting a report with audio or video attachments when submitting report through saved drafts.
  • AppFramework.Controls library reference has been removed from the Quick Report template as the AppStudio Framework has deprecated this library
  • Other bug fixes and UI enhancements

 

Map Tour

 

  • AppFramework.Controls library reference has been removed from the Map Tour template as the AppStudio Framework has deprecated this library
  • Other bug fixes and UI enhancements

 

 

Please feel free to test and provide your feedback through the GeoNet or emailing the team at appstudiofeedback@esri.com.

 

 

Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial.

 

Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase.

 

The AppStudio team periodically hosts workshops and webinars; please click on this link to leave your email if you are interested in information regarding AppStudio events.

Have you made a great app for outdoor use, that would benefit from high accuracy location? Do your users want to improve the location that they capture or report in your app, by using an external receiver? Do you want to report a location in your app that is being fed from a receiver that is remote to you but available over a network? In each of these cases, you will want to add the ability to search for, and connect to, an external GNSS receiver in your app.

 

We have recently added this ability to a number of our apps – Trek2There, Survey123, QuickCapture – and now we have made that work available to you, as a small collection of custom components, so you can drop them into your own app.

 

There are a few considerations you need to evaluate before adding GNSS device discovery to your app – what hardware are you intending your app to be used on, what receivers you want to support – jump to the end of this post to explore those.

 

For now, I want to show you the mechanics of making this work. The code that you can add to your app is available as an AppStudio sample called GNSS Discover. You can download this directly to your computer and copy and paste contents of it to your app.

 

The following steps will demonstrate how to add GNSS device discovery to the Hello World (Runtime) starter app. I’ve deliberately chosen this lightweight app so you can see the few steps that it takes to add this complex functionality.

 

        1. Launch and sign in to AppStudio Desktop Edition.
        2. Search for and download, the GNSS Discover sample app.
        3. Click New App, choose Hello World (Runtime) and click Create.
        4. Select the app you just created in the AppStudio gallery, and from the side menu choose Files. This will launch a file browser showing you the files created for your new app.
        5. In the AppStudio gallery, select the GNSS Discover app that you downloaded already, and from the side menu choose Files. This will launch another file browser showing you the files of the sample app.
        6. Copy the subfolder called GNSSPlugin, from the sample app to the folder of your new app.
        7. Close both file browsers.
        8. In the AppStudio gallery, select your new app (created from the Hello World starter app) and from the side menu select Edit. Your app will now launch in your default code editor. You will make edits to the MyApp.qml file only.
        9. After the existing import statements at the top of the file, add the following reference for your app to be able to use the contents of the folder that you just copied to your project:

          import "./GNSSPlugin"

        10. Scroll to the bottom of the MyApp.qml file. Before the last curly brace, you need to add the following code. This code is also in the GNSSDiscover.qml of the GNSS Discover sample app. You can open this file and copy and paste from there if you prefer.

          // Manage connections to GNSS providers

          GNSSManager {

              id: gnssManager

              gnssSettingsPages: gnssSettingsPages

          }

          // GNSS settings UI

          GNSSSettingsPages {

              id: gnssSettingsPages

              gnssManager: gnssManager

          }

          // GNSS status UI

          GNSSStatusPages {

              id: gnssStatusPages

              gnssManager: gnssManager

              gnssSettingsPages: gnssSettingsPages

          }

        11. The GNSSManager that you have just added provides a new position source that will communicate with both your internal position source and any configured external position source. To use this, change the positionSource of the existing locationDisplay component, to be gnssManager. It should look like this:

          locationDisplay {

              positionSource: gnssManager

          }

          Save your work. If you choose to run your app at this stage (Alt+Shift+R), you will see the map launch and it will have two buttons, a home and location  button.

          Click the location button and see that the map will center on your current location. By default, the app is using your devices internal position source. To be able to use an external receiver, you need to be able to select one. We will next add a settings button , so you have somewhere to select a new receiver.

           

        12. Copy the existing locationButton component and paste a copy of it immediately below the original one.
        13. Change the id of the copied component to be settingsButton.
        14. Change the source to be

          "./GNSSPlugin/images/round_settings_white_24dp.png"

        15. Change the name of the parameters in the onHoveredChanged event of this button to be

          settingsButton

        16. Remove the contents of the onClicked event and replace with

          gnssSettingsPages.showLocationSettings()

          Run your app. See that there is now an additional button with the settings icon. Click on this button and see the settings page. On this page you can choose to connect to a GNSS receiver that is connected to your device.

           

        17. As an optional bonus – would you like to make the color of the settings button match the others? You can do this with a color overlay. Start by giving your settings button image an id, for example
        18. Add the following code immediately after your button image component:

          ColorOverlay {

          anchors.fill: settingsButtonImage

          source: settingsButtonImage

          color: "black"

          }

          Ensure that the name for anchors.fill and source is the same as the name that your gave your settings button image.

        19. For the color overlay component to work, you will need to add one more import statement. This component is part of the Qt Graphic Effects plugin in, so at the top of the file where the other import statements are, add the following:

          import QtGraphicalEffects 1.12

          Save your work and run your app. See that the settings button is now the same color as the other buttons.

           


          Instead of cloning and modifying an existing button, there is a settings button component in the GNSS Discover sample that you also could have used.  Steps 12-17 show how you add new functionality to your app whilst maintaining the existing presentation of tools.

           

          Also included in the GNSS Discover sample is a status button that indicates when the position source is connected, and when clicked, displays information coming from that position source. In the next step, we will add this button as is.

           

        20. After the last button in your app, paste the following (this code is also in the GNSS Discover sample)

          GNSSStatusButton{

          gnssStatusPages: gnssStatusPages

          }

Save your work and run your app. The status button is only visible when the location is active, so first click the location button for the status button to appear.  See that the status button has different styling to your other buttons. You can choose to style it as you wish. Click on the status button to see information coming from the position source.

 

 

You can add the components from the GNSS Discover sample to any other app in a similar way. Just include the GNSSPlugin folder in your app and reference the GNSS Manager, GNSS Settings and GNSS Status pages in your app. You can choose how to launch the settings and status pages to suit your needs. For alternative examples on how to launch those pages, take a look at the code for the GNSS Discover sample itself. This sample shows buttons for the status and settings pages, that appear on the toolbar of the sample app.

 

 

Things to consider

 

An app that needs to communicate with external GNSS hardware needs the high accuracy location and Bluetooth capabilities to be set.

Also, to be able to work on iOS devices, the receiver must be part of the MFi program as well as support the output of NMEA sentences.

 

Whilst building your app, we recommend you test in AppStudio Player. Player already has many GNSS receivers whitelisted so you can test your app on iOS before whitelisting your own app.

 

To learn more about using GNSS receivers with your apps see https://doc.arcgis.com/en/appstudio/api-guide/apihighaccuracygps.htm

 

We are excited to announce the release of AppStudio for ArcGIS version 4.0. This release is packed with exciting new features, enhancements, and bug fixes to help you create cross-platform native apps easier and faster. Read this blog post to learn about all of the updates.

 

Bug Fixes: 

 

Bug ID

Synopsis

BUG-000103403

Fixed a translation bug with AppStudio for ArcGIS for the 'Close' button in German.

BUG-000119230

Fixed the bug due to which Android builds were fails from AppStudio for ArcGIS when a valid Java KeyStore (JKS) is supplied with Cloud Make.

BUG-000122909

Fixed that bug due to which downloading a mobile map package in an iOS was failing when the iPhone screen is locked, returning the error message "Socket is not connected."

BUG-000122721

Fixed the bug in audiorecorder.pause() method which was breaking the recording file path.

BUG-000121926

Updated the documentation to have consistency in mentioning the Android Native Development Kit (NDK) version in the "Install development tools for local Make" page.

BUG-000124162
Fixed a bug due to which sending video attachments from drafts in Quick Report from an Android device was failing.

 

Deprecation:

 

 

Known issue:

  • Apps built with AppStudio 4.0 cannot be rotated to landscape mode on specific tablet devices when landscape mode is enabled on tablet and disabled on the phone.

Please reach out to us if you encounter this issue.

1. Introduction

 

Many, many, many years ago, in my local bookstore, I had my first experiences with computers. I typed in my first program and always thought what it did was cool:

 

10 PRINT "HELLO"
20 GOTO 10

 

 

Today, I'm going to look into iterators and generators, and, yes, make a connection to how these latest features in ECMAScript 7 update of AppStudio 3.3 and Qt 5.12.1 aren't too dissimilar from old school concepts.

 

 

2. For Of Iterators

 

 

In my previous blog, I talked about "For Of" and showed that it works with Javascript iterators and Javascript generators.

 

function *cities() {
    yield { name: "Hawaii", distance: 4221.73 }
    yield { name: "Los Angeles", distance: 96.65 }
    yield { name: "New York City", distance: 3853.10 }
    yield { name: "Redlands", distance: 1.12 }
    yield { name: "Seattle", distance: 1566.70 }
}

for ( let city of cities() )
    console.log( JSON.stringify( city ) )

View the full ForIter.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly.

 

 

In the above example, we declare a Javascript generator which produces cities with their name and their distance from ESRI. We turn that Javascript generator into a Javascript iterator when we "call" it, i.e. cities(). As a Javascript iterator, we use a "for of" loop to process the results.

 

So far, nothing special. You can glean this sort of thing from any blog about Javascript iterators.

 

If we replace the "for of" syntax with a somewhat traditional looking "while" loop, we get to see some inner workings of the Javascript iterator:

 

function *cities() {
    yield { name: "Hawaii", distance: 4221.73 }
    yield { name: "Los Angeles", distance: 96.65 }
    yield { name: "New York City", distance: 3853.10 }
    yield { name: "Redlands", distance: 1.12 }
    yield { name: "Seattle", distance: 1566.70 }
}

const iter = cities()
let city = iter.next()
while ( !city.done ) {
    console.log( JSON.stringify( city.value ) )
    city = iter.next()
}

View the full WhileIter.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly.

 

 

The first impression is this "while" version looks a bit hard to swallow. The "for of" shorten version looks cleaner. When you think about it, this version actually gives you more insight into the "yield" keyword. When "yield" is used, a value is returned from the Javascript generator, and the Javascript generator pauses execution until the next time we call "iter.next()".

 

At first, this may seem like an alien concept, but, if you look harder, you'll see this pattern in a lot of places, some quite old.

 

3. Unix head and tails

 

On Unix, we learn that in shell programming, we can join Unix commands together using Unix pipes. A pipe is a form of redirection that is used in Unix to send output from one program to another for further processing. The first process is not completed before the second is started. They are executed concurrently.

 

$ cat cities.txt

 

 

$ cat cities.txt | head -3

 

 

$ cat cities.txt | head -3 | tail -2


 

Whilst cat can produce 5 lines of text, in the above pipeline, it doesn't. The 3 Unix commands, cat, head and tail are executing concurrently. The cat command only needs to generate output, if the subsequent head and tail commands require it. In this example, we only require cat to generate 3 lines of text to fullfill the requirements of the subsequent head command.

 

We can mirror the above Unix statement in QML.

 

Button {
    text: qsTr("LA and NYC cities")

    onClicked: {
        function *cities() {
            yield { name: "Hawaii", distance: 4221.73 }
            yield { name: "Los Angeles", distance: 96.65 }
            yield { name: "New York City", distance: 3853.10 }
            yield { name: "Redlands", distance: 1.12 }
            yield { name: "Seattle", distance: 1566.70 }
        }

        function* cat( iter ) {
            yield* iter
        }

        function* head( iter, n ) {
            if (n <= 0) return
            for (let item of iter) {
                yield item
                if (--n <= 0) break
            }
       }

       function* tail( iter, n ) {
           let arr = [ ]
           for ( let item of iter ) {
               arr.push(item)
               if (arr.length > n) arr.shift()
           }
           yield* arr
       }

       for ( let city of tail( head( cat( cities() ), 3), 2) )
           console.log( JSON.stringify( city ) )
    }
}

View the full HeadAndTails.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly.

 

 

Similar to the Unix head, tail, cat commands, the above head, tail, cat Javascript generator functions start executing concurrently. The data pipeline occurs when callers consume data and the Javascript generator functions yield data.

 

Because of the requirements of the head generator call, we only require the cat generator and the cities generator to generate 3 lines of output.

 

 

4. Infinite loops no longer taboo

 

 

Traditionally, we consider CPU intensive and/or infinite loops to be taboo in Javascript.

 

function helloWorldTaboo() {
    while (true)
        console.log( "Hello World" )
}

 

They block the UI, they make the program unresponsive, they may eventually lead to a crash. We consider such code taboo and we have been trained to not write in that style. We require the developer to rewrite their applications to use signals and slots or callback functions that help unblock the UI.

 

Rules changes when it comes to Javascript generators and Javascript iterators. Apparent infinite loops are no longer taboo:

 

function* helloWorldGenerator() {
    while (true)
        yield "Hello World"
}

 

The following demonstrates the Javascript generator working with a Timer object. The program is not CPU intensive and not quite an infinite loop in that one can stop the invoking the Javascript generator by stopping the Timer.

 

 

Button {
    text: qsTr("Run!")

    onClicked: {
        function* helloWorldGenerator() {
            while (true)
                yield "Hello World"
        }

        helloWorldTimer.iter = helloWorldGenerator()
        helloWorldTimer.start()
    }
}

Timer {
    id: helloWorldTimer
    property var iter
    running: false
    repeat: true
    interval: 100
    onTriggered: {
        let item = iter.next()
        if (item.done) {
            stop()
            return
        }
        console.log( item.value )
    }
}

View the full InfiniteHello.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly.

 

 

 

5. Generators are bidirectional

 

 

In Unix, the data flows in Unix pipes in one direction. With Javascript generators, the data flow can be bidirectional. We can take a value from the Javascript generator modify and send the result back to the Javascript generator.

 

The following shows the generator yielding 1, 2 and 3 respectively. For each value, we square the result and send the square back to the generator.

 

function* gen() {
  console.log( yield 1 )
  console.log( yield 2 )
  console.log( yield 3 )
}

let iter = gen()
let v = iter.next()
console.log( JSON.stringify(v) )
v = iter.next( v.value * v.value )
console.log( JSON.stringify(v) )
v = iter.next( v.value * v.value )
console.log( JSON.stringify(v) )
v = iter.next( v.value * v.value )
console.log( JSON.stringify(v) )

View the full Bidirectional.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly.

 

 

 

6. Mixing Javascript generators with Promises

 

 

In a previous blog on Promises, I talked about async / await missing in AppStudio 3.3 and Qt 5.12.1 and that you can vote to have it included it https://bugreports.qt.io/browse/QTBUG-58620.

 

I've read in other blogs that async / await is considered by some as syntactic sugar on Javascript generators yielding Promises.

 

I gave the following example demonstrating what this looks like with _asyncToGenerator() a function that works with Promises inside Javascript generators:

 

Button {
    text: qsTr( "Test Promise async/await (Babel)" )

    onClicked: {
        _asyncToGenerator( function*() {
            try {
                showTitle( yield download( "GET", "https://appstudio.arcgis.com" ) )
                showTitle( yield download( "GET", "https://community.esri.com/groups/appstudio" ) )
                showTitle( yield download( "GET",
"https://community.esri.com/groups/survey123" ) )
            } catch ( err ) {
                console.log( "Caught Error: ", err.message )
                throw err
            }
        } )()
    }
}

View the full PromiseBabel.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly.

 

 

 

6. Using ArcGIS Online Search API

 

The following uses Promises to invoke the ArcGIS Online Search API iteratively to retrieve titles of over 200+ AppStudio samples:

 

Button {
    text: qsTr("Search")

    onClicked: {
        _asyncToGenerator( function*() {
            networkRequest.url = "https://www.arcgis.com/sharing/rest/search"
            networkRequest.responseType = "json"
            try {
                let f = "pjson"
                let q = "type:Native Application owner:appstudio_samples"
                let start = 1
                let num = 10
                while (start !== -1 ) {
                    let resp = yield networkRequest.sendWithPromise( { f, q, start, num } )
                    let { nextStart, total, results } = resp.response
                    for ( let result of results )
                        console.log( result.title )
                    start = nextStart
                }
            } catch (err) {
                console.log("Caught exception: ", err.message)
                console.log(err.stack)
                throw err
            }
        } )()
    }
}

View the full ArcGISOnlineSearch.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly.

 

 

 

7. Maze Solving Algorithm

 

 

The following demonstrates how you can use Promises in a recursive manner to solve a maze:

 

Button {
    text: qsTr("Solve Maze")

    onClicked: {
        _asyncToGenerator( function*() {
            function solve(x, y) {
                return _asyncToGenerator( function*() {
                    maze[y][x] = someDude
                    printDaMaze()
                    yield sleep(100)
                    if (x === endingPoint[0] && y === endingPoint[1]) return true
                    if (x > 0 && maze[y][x - 1] === free && (yield solve(x - 1, y))) return true
                    if (x < mazeWidth && maze[y][x + 1] === free && (yield solve(x + 1, y))) return true
                    if (y > 0 && maze[y - 1][x] === free && (yield solve(x, y - 1))) return true
                    if (y < mazeHeight && maze[y + 1][x] === free && (yield solve(x, y + 1))) return true
                    maze[y][x] = free
                    printDaMaze()
                    yield sleep(100)
                    return false
                } )()
            }
            if (yield solve( startingPoint[0], startingPoint[1]) ) {
                console.log( "Solved!" )
                printDaMaze()
            } else {
                console.log( "Cannot solve. :-(" )
            }
        } )()
    }
}

View the full MazeSolver.qml on GitHub. Try it now on AppStudioBlogIterators | WebAssembly.

 

 

 

8. Experimental WebAssembly

 

 

Qt now offers a WebAssembly target.

 

This means it is possible to deploy QML applications in a web browser. WebAssembly is an experimental technology meaning it's an evolving platform. Expect a lot of things to not work. With the disclaimer out of the way, you can test all of the code snippets in this blog in a Web Assembly application here: https://stephenquan.github.io/AppStudio/Apps/AppStudioBlogIterators/

 

 

Send us your feedback to appstudiofeedback@esri.com

 

Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial.

 

Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase.

 

The AppStudio team periodically hosts workshops and webinars; please click on this link to leave your email if you are interested in information regarding AppStudio events.

1. Introduction

 

 

This blog is about the Javascript "for of" loop and the Javascript destructuring assignment.

 

These are part of ECMAScript 6 features that were included in the ECMAScript 7 update of AppStudio 3.3 and Qt 5.12.1.

 

We explore how these features may help with your AppStudio apps.



2. The "for of" loop

 

 

Traditionally, to loop through an array we need to use an index variable (e.g. i):

 

var census = [
    { country: "France", population: 66.99 },
    { country: "Australia", population: 24.6 },
    { country: "United States", population: 327.2 }
]
for (var i = 0; i < census.length; i++) {
    var item = census[i]
    console.log( item.country, item.population )
}

 

 

In ECMAScript 6, we can use "for of" loop to make this easier:

 

let census = [
    { country: "France", population: 66.99 },
    { country: "Australia", population: 24.6 },
    { country: "United States", population: 327.2 }
]
for ( let item of census ) {
    console.log( item.country, item.population )
}

 

 

The "for of" loop is new in ECMAScript 6 and created specifically for looping through arrays and, in general, Javascript iterators.

 


3. Unpacking object values

 

 

Since we know each item have country and population fields, we can use the new ECMAScript destructuring assignment operator to extract it:

 

let census = [
    { country: "France", population: 66.99 },
    { country: "Australia", population: 24.6 },
    { country: "United States", population: 327.2 }
]
for ( let item of census ) {
    let { country, population } = item
    console.log( country, population )
}

 

 

We can even mix destructuring assignment with "for of":

 

let census = [
    { country: "France", population: 66.99 },
    { country: "Australia", population: 24.6 },
    { country: "United States", population: 327.2 }
]
for ( let { country, population } of census ) {
    console.log( country, population )
}

 

 

You can get creative by combining this with arrow functions:

 

let census = [
    { country: "France", population: 66.99 },
    { country: "Australia", population: 24.6 },
    { country: "United States", population: 327.2 }
]
let orderByPopulationDesc = (a, b) => -(a.population - b.population)
for ( let { country, population } of census.sort( orderByPopulationDesc ) ) {
    console.log( country, population )
}



4. Unpacking arrays values

 

 

Similarly, you can unpack arrays using the following destructuring assignment syntax:

 

let [ year, month, day ] = [ "2019", "11", "07" ]
console.log(year) // 2019
console.log(month) // 11
console.log(day) // 07

 

 

 

5. Unpacking regular expression capture groups

 

 

When you use "str.match" with regular expression you get an array of strings. You get additional strings in the array for each capture group. You need to process the array to extract each capture group, e.g.

 

let str = "2019-11-07"
let m = str.match( /(\d{4})-(\d{2})-(\d{2})/ )
console.log( m.length ) // 4
console.log( m[0] ) // 2019-11-07
console.log( m[1] ) // 2019
console.log( m[2] ) // 11
console.log( m[3] ) // 07

 

 

The Javascript destructuring assignment here helps eliminate the need for having an intermediate array variable. The code becomes much shorter and clearer:

 

let str = "2019-11-07"
let [ , year, month, day ] = str.match(/(\d{4})-(\d{2})-(\d{2})/)
console.log( year ) // 2019
console.log( month ) // 11
console.log( day ) // 07

 

 

 

6. Using destructuring assignment with SQL

 

 

In the following example, we see that the destructuring assignment is useful in unpacking results from SQL queries:

 

db.open()
db.query( "CREATE TABLE IF NOT EXISTS countries (country TEXT, population NUMBER)" )
db.query( "INSERT INTO countries VALUES ( 'France', 66.99 )" )
db.query( "INSERT INTO countries VALUES ( 'Australia', 24.6 )" )
db.query( "INSERT INTO countries VALUES ( 'USA', 327.2 )" )
let q = db.query( "SELECT * FROM countries" )
for ( let ok = q.first() ; ok ; ok = q.next() ) {
    let { country, population } = q.values
    console.log( country, population )
}
q.finish()
db.close()

 

 

We can use "for of" syntax here if we make use of Javascript iterator and Javascript generator syntax:

 

function* queryIterator(q) {
    for ( let ok = q.first() ; ok ; ok = q.next() )
        yield q.values
}

db.open()
db.query( "CREATE TABLE IF NOT EXISTS countries (country TEXT, population NUMBER)" )
db.query( "INSERT INTO countries VALUES ( 'France', 66.99 )" )
db.query( "INSERT INTO countries VALUES ( 'Australia', 24.6 )" )
db.query( "INSERT INTO countries VALUES ( 'USA', 327.2 )" )
let q = db.query( "SELECT * FROM countries" )
for ( let { country, population } of queryIterator( q ) ) {
    console.log( country, population )
}
q.finish()
db.close()

 

 

We will talk more about Javascript iterators and Javascript generators in a future blog.

 

 

7. Conclusion

 

 

This blog introduces Javascript "for of" loop and Javascript destructuring assignments.

 

These are some of the new Javascript ECMAScript 7 syntax improvements in AppStudio 3.3 and Qt 5.12.1 that can help you improve your code readability, clarity by helping you write shorter, easier to maintain code.

 

These syntax improvements complement other Javascript improvements, such as Javascript iterators, Javascript generators and Javascript promises.

 

We will introduce Javascript iterators and Javascript generators in a future blog.

 

We will revisit Javascript promises and how they fit with all these Javascript syntax improvements.

Introduction

 

 

If you're a parent to daughters you may know what I mean when I talk about obsessions with Unicorns. When we walk pass any book or toy featuring unicorns the our girls' eyes light up with that mandatory question, "can we buy that?". My response is "No!". Please, no more, no, N-O, No!

 

This blog is really about TextInput validators:

 

  • InputMask
  • IntValidator
  • DoubleValidator
  • RegExpValidator
  • InputValidator (in this example, we will show you how to say "No" to unicorns!)

 

The first 4 is from Qt's runtime. The last is from AppStudio's AppFramework.

 

 

InputMask

 

 

TextField {
    inputMask: "999-AAA"
    placeholderText: qsTr("Type in a license plate of the pattern 999-AAA")
    text: "123-XYZ"
    color: acceptableInput ? "green" : "red"
    selectByMouse: true
}

View full InputMaskTest.qml on GitHub Gist.

 

 

InputMask is a property on TextInput components such as TextField. It allows us to quickly define acceptable text input. In the above example we allow 6 character license plates that begin with 3 digits, followed by a dash and finishing with 3 characters. When acceptable input is entered the text field changes from "red" to "green" and the user can submit the input.

 

The InputMask has the following features:

  • When the field is "empty" you'll see placeholders for symbols like hyphen ('-'), you will not see the placeholder text
  • When you type, you do not need to type the fixed symbols, e.g. hyphen ('-')
  • The code is very short

 

 

https://doc.qt.io/qt-5/qml-qtquick-textinput.html#inputMask-prop

https://doc.qt.io/qt-5/qlineedit.html#inputMask-prop

 

 

IntValidator

 

 

TextField {
    validator: IntValidator {
        bottom: 25
        top: 75
    }
    placeholderText: qsTr("Type in an integer between 25 and 75")
    text: "34"
    color: acceptableInput ? "green" : "red"
    selectByMouse: true
}

View full IntValidatorTest.qml on GitHub Gist.

 

 

IntValidator is a QML component that can be assigned as a TextInput validator. It allows us to quickly define acceptable numerical input. In above example we allow any integer between 25 and 75. When acceptable input is entered the text field changes from "red" to "green" and the user can submit the input.

 

The IntValidator has the following features:

  • When the field is empty you can see the placeholder text
  • You cannot type more than the number of characters hinted by the IntValidator (e.g. in the above example you cannot type more than two digits)

 

https://doc.qt.io/qt-5/qml-qtquick-intvalidator.html

 

 

DoubleValidator

 

 

TextField {
    validator: DoubleValidator {
        bottom: 25.0
        top: 75.0
    }
    placeholderText: qsTr("Type in a number between 25.0 and 75.0")
    text: "34.5"
    color: acceptableInput ? "green" : "red"
    selectByMouse: true
}

View full DoubleValidatorTest.qml on GitHub Gist.

 

 

DoubleValidator is a QML component that can be assigned as a TextInput validator. It allows us to quickly define acceptable numerical input. In above example we allow any number between 25.0 and 75.0. When acceptable input is entered the text field changes from "red" to "green" and the user can submit the input.

 

The DoubleValidator has the following features:

  • When the field is empty you'll see the placeholder text
  • You can type in any length (because decimal points are allowed)

 

https://doc.qt.io/qt-5/qml-qtquick-doublevalidator.html

 

 

RegExpValidator

 

 

TextField {
    validator: RegExpValidator {
        regExp: /[0-9]{3}-[A-Za-z]{3}/
    }
    placeholderText: qsTr("Type in a license plate of the pattern 999-AAA")
    text: "123-XYZ"
    color: acceptableInput ? "green" : "red"
    selectByMouse: true
}

View full RegExpValidatorTest.qml on GitHub Gist.

 

 

RegExpValidator is a QML component that can be assigned as a TextInput validator. It allows us to quickly define acceptable input. In the above example, we allow for license plates that begin with a 3 digit number, followed by a dash and ending with 3 letters.

 

RegExpValidator has the following features:

  • When the field is empty you'll see the placeholder text (different than InputMask)
  • You must type the hyphen, it won't be automatically typed for you (different than InputMask)
  • The regular expression is significantly more complex to implement and maintain

 

 

https://doc.qt.io/qt-5/qml-qtquick-regexpvalidator.html

 

 

InputValidator

 

 

TextField {
    validator: InputValidator {
        validate: function (input, position) {
            if (input.match(/unicorn/)) {
                return InputValidator.Invalid
            }

            while (input.match(/canine/)) {
                input = input.replace(/canine/, 'dog')
                position -= 6
                position += 3
            }

            let state = InputValidator.Intermediate
            if (input.endsWith('.')) {
                state = InputValidator.Acceptable
            }

            return { input, position, state }
        }
    }
    placeholderText: qsTr("Type in a sentence ending with a fullstop. Avoid using 'unicorn' and 'canine'!")
    text: "A dog is man's best friend."
    selectByMouse: true
}

View full InputValidatorTest.qml on GitHub Gist.

 

 

InputValidator is a QML component that can be assigned as a TextInput validator. It allows us to supply a Javascript function to be the validator. The Javascript function receives both the original string and position as input. It can return an input state (InputValidator.Invalid, InputValidator.Intermediate or InputValidator.Acceptable). It can also return an object that contains an updated version of the string, position and state. Returning an updated string allows us to implement auto correction. In the above example we forbid the user from typing in "unicorn". It's not allowed! We also forbid the user from typing "canine". If they attempt to type "canine" it gets auto corrected as "dog". The sentence will never be full accepted until the user finishes it with a full stop ".".

 

InputValidator has the following features:

  • When the field is empty you'll see the placeholder text
  • You can prohibit certain input (e.g. we can come up with a word blacklist, e.g. censor offensive words)
  • You can correct / fix input (i.e. implement auto correct)
  • You have full control over the experience
  • Takes more effort to implement your rules

 

 

https://doc.arcgis.com/en/appstudio/api/reference/framework/qml-arcgis-appframework-inputvalidator/

 

 

References

 

 

 

 

Send us your feedback to appstudiofeedback@esri.com

 

Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial.

 

Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase.

 

The AppStudio team periodically hosts workshops and webinars; please click on this link to leave your email if you are interested in information regarding AppStudio events.

The AppStudio team is hard at work on the next version of AppStudio for ArcGIS (version 4.0). As always there will be technical updates to the software, but with this new version there will also come some significant changes to how we license the software.

 

More details about the new technical features and capabilities coming with AppStudio 4.0 are detailed in this blog post, but here is an overview of the highlights.

  • New 3D Scene Viewer template
  • AppStudio extension for Visual Studio Code (open VS Code from AppStudio, write QML)
  • Support for opening a permission dialog while app is running
  • Preview file before sharing
  • Change language while app is running
  • Updates to JavaScript environment (ECMAScript 7)

If you want access to the AppStudio 4.0 beta visit the AppStudio for ArcGIS Early Adopter Community.

 

Licensing Changes

 

AppStudio is a powerful suite of tools that provides our users an easy to use, yet extremely robust cross-platform app development experience. We want more ArcGIS users to experience the full power of AppStudio, and have access to more templates, layouts, samples, and tools. To give access to more of what AppStudio has to offer, we have decided to make some changes to the license levels.

 

Name Change

First things first, we are changing the name of the licensing levels

 

Original NameNew Name
AppStudio for ArcGIS BasicAppStudio for ArcGIS
AppStudio for ArcGIS StandardAppStudio for ArcGIS Developer Edition

 

Just as before, every ArcGIS organization member with a user type of Creator or GIS Professional will have access to AppStudio for ArcGIS (we’ve dropped “Basic” from the name). If you have an AppStudio for ArcGIS Standard license it will be renamed to AppStudio for ArcGIS Developer Edition. We are also working on making an easier experience to purchase and provision an AppStudio Developer Edition license to the ArcGIS organization of your choice.

 

 

What comes with an AppStudio for ArcGIS license?

 

Previously, the AppStudio Basic license lets you create a native app from one of three templates on appstudio.arcgis.com. This online template experience offered minimal configuration options and required that maps and data be public. At version 4.0 all AppStudio for ArcGIS licenses now includes the AppStudio Desktop application. AppStudio Desktop lets you configure an app from a template, build an app from a layout, or add functionality to an app using code from one of our 70+ live samples. Speaking of code... also included is the ability to write custom code for your app using Qt Creator (installed with desktop) or Microsoft Visual Studio Code (using a new AppStudio extension).

 

After you have created an app using AppStudio Desktop you can upload it to your ArcGIS organization and share it with others in your org using AppStudio Player. An AppStudio app stored in ArcGIS Online is just another item in your organization. And just as you share data, maps, and web apps with others, you can share your AppStudio apps and then run the app using AppStudio Player (more about AppStudio Player below).

 

Note: The app building experience on appstudio.arcgis.com will be retired on August 15th, 2019, see the Q&A below for more information.

 

What comes with an AppStudio for ArcGIS Developer Edition license?

 

The Developer Edition license adds a few key components for more advanced developers or those who want to distribute their apps as a standalone installed app via a public app store or an enterprise deployment. The Developer Edition includes a set of Enterprise templates such as Survey123, AuGeo, and AppStudio Player. Access to the AppStudio cloud Make service is also included with the Developer Edition so you can generate app installation files for iOS, Android, Windows, Mac, and Linux.

 

This table highlights the capabilities of AppStudio editions at version 4.0:

AppStudio
for ArcGIS
AppStudio
Developer Edition
Download and use AppStudio DesktopXX
Convert your ArcGIS web maps into mobile appsXX
Build apps with no coding, using configurable app templatesXX
Extend apps built using the configurable app templatesXX
Create custom apps using your developer skillsXX
Share apps within your ArcGIS organization using AppStudio PlayerXX
Use Cloud Make to build install executables
(Android, iOS, Windows, OS X Linux)
X
Publish to the Apple and Google Play app storesX
Use Mobile Device Management (MDM) systems to share in an EnterpriseX
Access to AppStudio enterprise app templates
(Survey123, QuickCapture, AuGeo, more...)
X
In the future - Create add-ins for Survey123 for ArcGISX

 

We are excited about these new changes to AppStudio and the opportunity to share more of the great tools in AppStudio with a larger audience. We understand that these changes may be disruptive for some of our existing users who have been using the web experience to build apps. Please reach out to us (appstudiofeedback@esri.com) so we can better help you through this license transition.

 

Below is a Q&A to help with some of the common questions we think people might have. If you have other questions, please leave them in the comments section below.

 

Thank You – The AppStudio team


Q & A


What will happen to the app building experience on appstudio.arcgis.com website?

The template based app building tools on appstudio.arcgis.com will be shutdown August 15th, 2019 with the release of AppStudio 4.0. Going forward you will use the tools in AppStudio Desktop to configure an app based on one of the standard templates (Map Viewer, Map Tour, Quick Report). AppStudio Desktop offers many more settings for these templates, so you have more control configuring the look and feel of your app... without writing any code. Some of the settings you can configure in AppStudio desktop include the app color, text color, custom fonts, feedback email, offline map package, and more.

If you have already created apps using the website tools those apps will not go away, you can download and further customize those apps using AppStudio Desktop.


What AppStudio license level has access to generate a build? (access to cloud Make)

A significant change in the licensing is that to generate a standalone install build (suitable for submitting to the app stores) for an app will require an AppStudio Developer Edition license.
Those users who have created apps from the website experience using an AppStudio Basic license will no longer have access to generate a standalone build of their app after August 15th, 2019.


How do I share my app with others using AppStudio Player for ArcGIS?

AppStudio for ArcGIS Player is an amazing tool that can transform how you distribute apps in your organization. Using AppStudio Player is like having a private app store, which you control. You can determine who has access to what apps in your organization and when they get updated.

Here how it works:
1. Create an app using AppStudio Desktop
2. Using AppStudio Desktop upload your app to ArcGIS Online or Enterprise
3. In your portal set the sharing property to share your app with others in your ArcGIS organization. Share it with a group of users, the entire organization, or even the public.
4. The end user of the app downloads AppStudio Player onto their device from the app stores or from the AppStudio website.
5. They login using their username/password for their ArcGIS organization.
6. Next, they download the shared app to the device
7. Now they can run the app as a native application right within AppStudio Player

We are excited to announce the release of AppStudio for ArcGIS 4.0 Beta.  Downloads are available through the Early Adopter Community. We encourage you to participate in the beta program to try out new features, enhancements and report bugs. Your feedback will greatly help us improve AppStudio and deliver a stable, high-performance product to everyone.

 

 

New licensing model

We want more ArcGIS users to experience the full power of AppStudio, and have access to more templates, layouts, samples, and tools. To give access to more of what AppStudio has to offer, we have decided to make some changes to the license levels with the AppStudio 4.0 release.  The changes include:

  • License level name change
    • AppStudio for ArcGIS
    • AppStudio for ArcGIS Developer Edition
  • AppStudio by default now includes the ability to make changes to any app or sample or template and upload to the Player (Previously this required a full standard license).
  • Retiring web-based app building tools on appstudio.arcgis.com
  • Cloud Make service will only be included with AppStudio Developer Edition

 

We are excited about these new changes to AppStudio and the opportunity to share more of the great tools in AppStudio with a larger audience.  We understand that these changes will impact the workflow of some of our existing AppStudio users. Please read this blog post for full details on the new licensing changes:
BLOG: Licensing changes coming in AppStudio for ArcGIS 4.0

 

New 3D Scene Viewer template

We released the 3D Scene Viewer as a sample app in the last release – thank you for all of the great feedback and suggestions. With this release, the 3D Scene Viewer has received many new features, enhancements, UI updates, and more customization options. We are excited to publish it as a configurable template app. Please try out this new template app and let us know what do you want to see in the future release in the comment below or email appstudiofeedback@esri.com.  

 

Notice that Support for Mobile Scene Packages coming soon

 

 

   3D Scene Viewer template

 

AppStudio for ArcGIS extension for Visual Studio Code

Visual Studio Code, an alternative code editor to Qt Creator, has been supported since the last release. We have continued our work on Visual Studio Code integration and have simplified the process of opening the AppStudio project in Visual Studio Code.  A new Editor session has been added in AppStudio Preferences, allowing you to choose Qt Creator or Visual Studio Code as your default code editor. Once your default is set, clicking on the Edit button in the side panel will open your AppStudio in the desired code editor. Please refer to this documentation for using the AppStudio for ArcGIS extension for Visual Studio Code.

 

 

 

Support for opening permission dialog prompt

We are excited to introduce the new Permission singleton and PermissionDialog QML component in the AppFramework Platforms plugin. The Permission singleton can query the permission status. The PermissionDialog component provides the ability to open a dialog prompt to ask for the camera, microphone, storage (Android), location, or background location permission on mobile devices. It can also open the device settings if the user declines the permission prompt.

Note: API references of these new features and enhancements are available in the Qt Creator Help documentation included with the AppStudio Desktop 4.0 (Beta) install.    

 

                                                                           Permission Dialog Sample

 

Notice that a Permission Dialog sample app is available in AppStudio Desktop. You can download the sample app here: AppStudio Desktop 4.0 beta > New App > search for Permission Dialog.

 

Support for previewing and sharing files

We have been actively listening to feedback and suggestions from the community and are happy to announce support for one of the most requested features - previewing and sharing files. We have enhanced the AppFramework clipboard.share(fileURL) method to open an appropriate user interface for a specific file (PDF, image, audio, video, and more), allowing you to preview and share the file.

 

 

                                                                      Share text, URL, or file sample

 

 

Support for changing languages when the app is running

Previously, the language of a localized AppStudio app would be based on the device language. With this release, you can now change app language inside of the app without changing the device language. This is huge as now you can also test your apps without changing phone OS locale and also have the ability to provide the option in UI to switch between languages.

 

Internationalization sample

 

 

Others:

  • Upgraded Qt to version 5.12.3
  • We have ended support for creating Android x86 apps.
  • AppStudio 4.0 will be the last version to support Android 4.4.

 

Getting Started

The beta program is open to anyone – join the Early Adopter Community today to gain access to the latest versions of AppStudio 4.0 Beta.

 

  • AppStudio Desktop- You can download the beta version of AppStudio Desktop 4.0 here.
  • AppStudio Player (iOS)- AppStudio Player 4.0 (Beta) is available via Apple's TestFlight. Please click on this link to download.
  • AppStudio Player (Android) - AppStudio Player 4.0 (Beta) is available via the Android Beta Program. Please go to this link to download.

 

You are now all set to start testing the AppStudio 4.0 Beta!

 

Feedback

Your feedback is much appreciated and will help us to improve the final release of AppStudio for ArcGIS 4.0. For bugs or other issues you may find, please submit a bug report or join the beta forum to provide feedback. You can also send us an email at appstudiofeedback@esri.com.

 

The AppStudio for ArcGIS 4.0 final release is scheduled for late August.  We want to thank everyone who contributes to this release in advance, and we wish you happy testing!

 

Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial.

 

Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your apps built using AppStudio to be featured in the AppStudio Showcase.

 

The AppStudio team periodically hosts workshops and webinars; please click on this link to leave your email if you are interested in information regarding AppStudio events.

SQuan-esristaff

Sorting QML ListModels

Posted by SQuan-esristaff Employee Jun 19, 2019

1. Introduction

 

 

This blog post discusses sorting the QML ListModel. It's one of my favourite problems to solve. The blog also talks about a Sorting Cities sample app. That sample app implements and demonstrates the QML ListModel sort. The rest of the blog explains how the function works.

 

 

2. Sorting Cities sample app

 

 

 

 

The Sorting Cities sample app is an AppStudio 3.3 app. It comes with five U.S. cities. The user can sort them by city name or by distance to ESRI. The sort order can be either ascending or descending.

 

Item {
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 10 * AppFramework.displayScaleFactor

        spacing: 10 * AppFramework.displayScaleFactor

        ListView {
            id: listView
            Layout.fillWidth: true
            Layout.fillHeight: true
            model: ListModel {
                id: listModel
                Component.onCompleted: {
                    add( "Hawaii", 21.289373, -157.917480 )
                    add( "Los Angeles", 34.052235, -118.243683 )
                    add( "New York City", 40.730610, -73.935242 )
                    add( "Redlands", 34.0555700, -117.1825400 )
                    add( "Seattle", 47.608013, -122.335167 )
                }
                function add(city, lon, lat) {
                    const esri = QtPositioning.coordinate( 34.0572522, -117.1945814 )
                    const coord = QtPositioning.coordinate( lon, lat )
                    const distance = coord.distanceTo( esri )
                    append( { city, coord, distance } )
                }
            }

            delegate: ItemDelegate {
                text: `${city} ${(distance / 1000).toFixed(2)} km`
            }
        }

        Button {
            text: qsTr("  City  ")
            onClicked: listModelSort( listModel,
                                      (a, b) => a.city.localeCompare(b.city) )
        }

        Button {
            text: qsTr("  City (Desc)  ")
            onClicked: listModelSort( listModel,
                                      (a, b) => - a.city.localeCompare(b.city) )
        }

        Button {
            text: qsTr("  Distance to Esri  ")
            onClicked: listModelSort( listModel,
                                      (a, b) => (a.distance - b.distance) )
        }

        Button {
            text: qsTr("  Distance to Esri (Desc)  ")
            onClicked: listModelSort( listModel,
                                      (a, b) => - (a.distance - b.distance) )
        }
    }

    function listModelSort(listModel, compareFunction) {
        let indexes = [ ...Array(listModel.count).keys() ]
        indexes.sort( (a, b) => compareFunction( listModel.get(a), listModel.get(b) ) )
        let sorted = 0
        while ( sorted < indexes.length && sorted === indexes[sorted] ) sorted++
        if ( sorted === indexes.length ) return
        for ( let i = sorted; i < indexes.length; i++ ) {
            listModel.move( indexes[i], listModel.count - 1, 1 )
            listModel.insert( indexes[i], { } )
        }
        listModel.remove( sorted, indexes.length - sorted )
    }
}

View the full SortingCities.qml on Github Gist.

 

 

3. ListModel vs Array

 

 

If you compare the ListModel QML Type with the Javascript Array type, you'll find that Array has better methods. In particular, Array has a sort method whilst ListModel does not.

 

When you assign an Array to a QML visual component, e.g. ListView, the Array behaves like a scalar value. If you were to push new records onto the Array the ListView will not update. There is no property binding between pushes to the Array and the ListView. To refresh the ListView you need to reassign the Array every time the Array changes. This will cause the ListView to repaint. This could lead to bad user experience. For instance, I may be writing an application that collates results from an online search. The results may arrive through a series of NetworkRequests to REST API end points. Each update will cause the ListView to repaint and scroll back to the top. This will be particularly annoying to the user if the user was already reading and scrolling through the results.

 

The experience is different with ListModels. When you assign a ListModel to a ListView you don't need to assign it again. When you append new records to the ListModel, the ListView will receive sufficient change notification and will automatically update to reflect that new records have arrived. If the ListModel were to change whilst the user was reading and scrolling through the ListView, the current scrolled position and the selected item will be unchanged. The user would not experience any negative user experience with the ListView.

 

In short, for good user experience, we prefer ListModels over Arrays.

 

 

4. Compare Functions as Arrow Functions

 

 

Array sort (and QML ListModel sort) requires a compare function. A compare function takes any two elements from the Array (or ListModel) and returns an integer. It can be negative, zero or positive. For example, if one wanted to compare any two numbers, one could write the following compare function:

 

function compareNumbers( a, b ) {
    return a - b
}

 

If a was greater than b then the function would return a positive number.

If a was the same as b then the function would return zero.

If a was less than b then the function would return a negative number.

 

Then, you would use the compare function as follows:

 

function compareNumbers( a, b ) {
    return a - b
}

let numbers = [ 5, 2, 11, 3, 7 ]
numbers.sort( compareNumbers )
console.log( numbers ) // [ 2, 3, 5, 7, 11 ]

 

If your compare function is simple, you can inline it with your sort call, i.e.

 

let numbers = [ 5, 2, 11, 3, 7 ]
numbers.sort( function compareNumbers( a, b ) { return a - b } )
console.log( numbers ) // [ 2, 3, 5, 7, 11 ]

 

In AppStudio 3.3, Qt 5.12.1 you can use the ECMAScript 6 arrow function syntax, i.e.

 

let numbers = [ 5, 2, 11, 3, 7 ]
numbers.sort( (a,b) => a - b )
console.log( numbers ) // [ 2, 3, 5, 7, 11 ]

 

You'll see the arrow function syntax allows us to omit the 'function' keyword, the function's name and the 'return' keyword. We make this choice if we believe such a choice improves our code readability and maintainability.


In the Sorting Cities sample app, I used the arrow function syntax for all 4 buttons, e.g.

 

Button {
    text: qsTr("  City  ")
    onClicked: listModelSort( citiesListModel,
                              (a, b) => a.city.localeCompare(b.city) )
}

 

 

5. Implementing listModelSort

 

 

There are plentiful sorting algorithms out there. Whilst we can implement one in QML / Javascript, I don't think this is a good idea. We would be implementing a sort algorithm that we would required to maintain. One of the best algorithms out there, Quicksort, is complex to transcribe correctly. Also such an implementation would be executing entirely in Javascript and will not be leveraging much from native code.


Instead, I wanted to leverage from Array sort's implementation. In a nutshell, the algorithm would be:

 

  1. Copy items from the QML ListModel to an Array
  2. Perform an Array sort
  3. Copy the resultant items from the sorted Array back to the QML ListModel

 

Turns out, we can optimized the above technique by not copying the items but linking to them via an index. i.e. The Array would be a set of integer indexes and we just be traversing it as a on the fly lookup into the QML ListModel.

 

Let's begin by creating an indexes Array the exact same size as our ListModel with numbers ranging from 0 to N - 1:

 

let indexes = [ ...Array(listModel.count).keys() ]

 

Then, we use Array's sort method to reorder the indexes Array. We use an arrow function to pick any two entries from the QML ListModel and feed them to the user supplied compare function:

 

indexes.sort( (a, b) => compareFunction( listModel.get(a), listModel.get(b) ) )

 

This reorders the indexes Array. We can now use this sorted Array to traverse the ListModel in sorted order.

 

Whilst we can stop here, I wanted to go further and use theses indexes to rearrange the QML ListModel.

 

for ( let i = 0; i < indexes.length; i++ ) {
    listModel.move( indexes[i], listModel.count - 1, 1 )
    listModel.insert( indexes[i], { } )
}
listModel.remove( 0, indexes.length )

 

The above loop iterates through the items in the ListModel in sorted order. For each item, we move it to the end of the ListModel in sorted order. Once all the items have been moved, we delete the empty holes left behind by the move. It's a heavy handed approach. In my final version I made an optimization that reduces the amount moving and deletion.

 

 

The animation above helps you visualize sorting the cities based on their distance to ESRI. We see it quickly identifies the sort order as Redlands, Los Angeles, Seattle, New York City and Hawaii. The records are tagged by an index 0...4. Next, the sorted records are moved, one by one, to the end, then the sorted records are moved back to the top deleting the temporary empty holes.

 

 

6. Closing Remarks

 

 

For good user interfaces I recommend storing your content in ListModels instead of Arrays. This is so that updates to the content will not interrupt the user experience.


Whenever you can, leverage from an existing implementation (e.g. Array sort) rather than implement your own. You minimize your coding effort and future maintenance efforts.

 

The ECMAScript 6 arrow function syntax can help you express simple sorting compare functions. Otherwise, if they're complex, then, then you should use a named compare function.

 

 

7. References

 

 

 

Send us your feedback to appstudiofeedback@esri.com

 

Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial.

 

Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase.

 

The AppStudio team periodically hosts workshops and webinars; please click on this link to leave your email if you are interested in information regarding AppStudio events.

1. Javascript Promise

 

 

AppStudio 3.3, Qt 5.12.1 and ECMAScript 7 (which includes ECMAScript 6) introduced native Javascript Promises.

 

This blog is to give you a brief overview of what Promises are and how they matter to AppStudio and QML. This blog isn't a comprehensive guide to Promises. There already is a lot of content on the internet. I recommend you seek them out.

 

Promises is a Javascript coding pattern for working with asynchronous tasks.

 

AppStudio (and QML) has signals and slots for working with asynchronous tasks. If your applications are written and easy to maintain then you probably don't need to look into Promises right now.

 

However, if your applications are complex with business logic spanning over a complex sequence of signals and slots then buying into Promises may be the key to reduce code complexity.

 

Note that the ArcGIS.AppFramework.Promises module is being deprecated in favour of native Javascript Promises. We advise discontinuing using ArcGIS.AppFramework.Promises module and begin removing them from your existing AppStudio apps.

 

 

2. Promise Download Sample

 

 

Let's look at an AppStudio app that simulates downloading content from the internet.

 

 

When the Button is clicked, we simulate 3 concurrent downloads. The downloads will finish 0-1000ms after the Button's onClicked handler has finished. Each download's success/failure will be independent of each other.

 

    Button {
      text: qsTr("Test Promise timeout")
      onClicked: {
        download( "GET", "https://community.esri.com/groups/appstudio" )
          .then( (message) => { console.log( message ) } )
          .catch( (error) => { console.log( error ) } )
        download( "GET", "https://appstudio.arcgis.com" )
          .then( (message) => { console.log( message ) } )
          .catch( (error) => { console.log( error ) } )
        download( "GET", "https://community.esri.com/groups/survey123" )
          .then( (message) => { console.log( message ) } )
          .catch( (error) => { console.log( error ) } )
      }
    }

    function download( method, url ) {
      return new Promise( (resolve, reject) => {
        if (Math.random() < 0.30)
          setTimeout( reject, Math.floor(1000 * Math.random()), `Download failure ${url}` )
        else
          setTimeout( resolve, Math.floor(1000 * Math.random()), `Download success ${url}` )
      } )
    }

View the full PromiseTimeout.qml on GitHub Gist.

 

Some key features of Promises:

 

  • The task begins as soon as new Promise() { ... } is called.
  • The task is defined as (resolve, reject) => { /* body */ } where either resolve(data) or reject(error) will be called when the task completes.
  • Use then() method to supply your success handler.
  • Use catch() method to supply your failure handler.

 

You may be thinking "This looking confusing" and "Do I need this?". I ask you to consider this: the Button's onClick handler fully describes 3 download tasks and what to do when each of them finishes. It describes this all this in one place, in the one function. If you didn't use Promises, we would have to create numerous signals and slots and spread the implementation throughout the application.

 

 

3. Promise chaining

 

 

Promise chaining is a coding pattern where you want to run your asynchronous tasks sequentially. You may need to do this if you wish to use the output of one task as an input to the next.

 

 

When the Button is clicked, we want to start the download of 3 web pages, one after the after. If downloads are successful, you will see the web page's title. If any task fails, we do not continue downloading. (N.B. here, the download function is real. The download function returns the web page's title).

 

    Button {
      text: qsTr( "Test Promise chaining" )
      onClicked: {
        download( "GET", "https://community.esri.com/groups/appstudio" )
        .then( (data) => {
          console.log( data.title )
          return download( "GET", "https://appstudio.arcgis.com" )
        } )
        .then( (data) => {
          console.log( data.title )
          return download( "GET", "https://community.esri.com/groups/survey123" )
        } )
        .then( (data) => {
          console.log( data.title )
        } )
        .catch( (error) => { console.log( error ) } )
      }
    }

View the full PromiseChaining.qml on GitHub Gist.

 

However, due to the https://bugreports.qt.io/browse/QTBUG-71329, Promise chaining doesn't work in AppStudio 3.3, Qt 5.12.1. Please vote to have it fixed! Because of the bug, the app does not run correctly:

 

 

 

4. Promise nesting (alternative to Promise chaining)

 

 

Since Promise chaining doesn't work in AppStudio 3.3, Qt 5.12.1 let's explore some alternatives. This one demonstrates Promises nesting where each subsequent task is nested in the success of the previous task. (N.B. here the download function returns an object containing the status, responseText, and title).

 

    Button {
      text: qsTr( "Test Promise nesting" )
      onClicked: {
        download( "GET", "https://community.esri.com/groups/appstudio" )
        .then( (data) => {
          console.log( data.title )
          download( "GET", "https://appstudio.arcgis.com" )
          .then( (data) => {
            console.log( data.title )
            download( "GET", "https://community.esri.com/groups/survey123" )
            .then( (data) => {
              console.log( data.title )
            } )
            .catch( (error) => { console.log( error ) } )
          } )
          .catch( (error) => { console.log( error ) } )
        } )
        .catch( (error) => { console.log( error) } ) 
      }
    }

View the full PromiseNesting.qml on GitHub Gist.

 

This implementation works, but it is extremely ugly. It is infamously known as callbackhell. You see each subsequent task incurs another level of indentation and another copy of the error handler.

 

 

5. Promise async/await (alternative to Promise chaining)

 

 

async/await is another alternative to Promise chaining. The main characteristic is the use of the new async and await keywords and that the main body reads sequentially and elegantly.

 

    Button {
      text: qsTr( "Test Promise async/await" )
      onClicked: {
        (async () => {
          try {
            console.log( ( await download( "GET", "https://community.esri.com/groups/appstudio") ).title)
            console.log( ( await download( "GET", "https://appstudio.arcgis.com") ).title)
            console.log( ( await download( "GET", "https://community.esri.com/groups/survey123" ) ).title)
          } catch ( error ) {
            console.log( error )
          }
        } )()
      }
    }

View the full PromiseAsyncAwait.qml on GitHub Gist.

 

Unfortunately, this feature didn't make it to AppStudio 3.3, Qt 5.12.1. Help get this into a future release of AppStudio by voting for it at https://bugreports.qt.io/browse/QTBUG-58620.

 

 

6. Promise async/await Babel (alternative to Promise chaining)

 

 

Generator functions made it into AppStudio 3.3, Qt 5.12.1. We can use https://babeljs.io/ to convert the async/await into Generator functions. This version works in AppStudio 3.3, Qt 5.12.1. Here's the converted output:

 

    Button {
      text: qsTr( "Test Promise async/await (Babel)" )
      onClicked: {
        _asyncToGenerator( function*() {
          try {
            console.log( ( yield download( "GET", "https://appstudio.arcgis.com" ) ).title )
            console.log( ( yield download( "GET", "https://community.esri.com/groups/appstudio" ) ).title )
            console.log( ( yield download( "GET", "https://community.esri.com/groups/survey123" ) ).title )
          } catch ( error ) {
            console.log( error )
          }
        } )()
      }
    }

View the full PromiseBabel.qml on GitHub Gist.

 

While waiting for the async/await feature to be implemented, I think the above syntax is compelling.

It is extremely close to async/await syntax and, I think, is significantly better than Promise chaining.

 

To make it work, _asyncToGenerator() and asyncGeneratorStep() functions are required.

 


7. Promise.all and Promise.race

 

 

Promise.all is a special Promise that succeeds by waiting for every sub Promise to succeeds else it fails to report the error from the first failing sub Promise.

 

 

Promise.race is a special Promise in that we are only interested in the first sub Promise that finishes the race. We aren't interested in the completion of the subsequent Promises. We will be notified of the success message or failure error of that first sub Promise.

 

Surprisingly, no matter how many times you try, you find the AppStudio Home Page always wins the race.

 

 

Why is the second Promise always succeeding in the race? That's because the community GeoNet page takes longer to load!

 

 

8. Closing remarks

 

 

I hope this blog helps gives a good overview ofPromises and why they matter in your AppStudio apps.

 

Promises, while new in AppStudio 3.3, Qt 5.12.1 is a feature that other Javascript developers have already been enjoying for some time now.

 

Promises, I believe, work very well with Qt's signal and slot mechanism taking it to a whole new level.

 

Promise chaining and async/await functions do not work. We need your help to make AppStudio come closer to parity with other Javascript developer environments by voting on getting these two bugs fixed:

 

 

Whilst we wait for async/await functions, I highly recommend you use https://babeljs.io/ to get async/await features today.

 

 

9. References

 

 

 

Send us your feedback to appstudiofeedback@esri.com

 

Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial.

 

Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase.

 

The AppStudio team periodically hosts workshops and webinars; please click on this link to leave your email if you are interested in information regarding AppStudio events.

1. Introduction

 

This blog talks about implementing your own setTimeout() function in AppStudio.

 

The blog also highlights ECMAScript 6 and ECMAScript 7 support in Qt 5.12.1 and AppStudio 3.3 features: arrow functions (my favourite), property shorthand, rest parameters and spread syntax.

 

setTimeout() is a function which executes another function or specified piece of code after waiting for the specified time delay in milliseconds.

 

The following will execute the following 4 console.log() after waiting 0 - 1000 milliseconds (determined by a random function). Each timer starts simulatenously but their completion times are random making the 4 console.log() appearing in random jumbled order.

 

    setTimeout( () => console.log("Hello"), randomDelay() )
    setTimeout( (a) => console.log(a), randomDelay(), 1 )
    setTimeout( (a, b) => console.log(a, b), randomDelay(), 21, 22 )
    setTimeout( (a, b, c) => console.log(a, b, c), randomDelay(), 31, 32, 33 )

 

randomDelay() is a function that computes a random time delay from the range of 0 – 1000 milliseconds:

 

    function randomDelay() {
        return Math.floor(1000 * Math.random())
    }

 

Here is the console output:

 

 

 

2. AppStudio implementation

 

setTimeout() is a part of other Javascript development environments, e.g. in Web development and Node.js. It exists globally on the window object.

 

However, in Qt and AppStudio, there isn’t an equivalent window object or setTimeout() function.

 

To bring over such a capability you need to implement it yourself.

 

Here is a sample AppStudio app that includes a simple implementation of setTimeout(). Everytime the button is clicked the 4 setTimeout() functions are triggered with the 4 resulting console.log() appearing in random order.

 

import QtQuick 2.12
import QtQuick.Controls 2.5
import ArcGIS.AppFramework 1.0

App {
    id: app

    width: 400 * AppFramework.displayScaleFactor
    height: 640 * AppFramework.displayScaleFactor

    StackView {
        id: stackView

        anchors.fill: parent

        initialItem: Item {
            Button {
                text: qsTr("Test")
                onClicked: {
                    setTimeout( () => console.log("Hello"), randomDelay() )
                    setTimeout( (a) => console.log(a), randomDelay(), 1 )
                    setTimeout( (a, b) => console.log(a, b), randomDelay(), 21, 22 )
                    setTimeout( (a, b, c) => console.log(a, b, c), randomDelay(), 31, 32, 33 )
                }
            }
        }
    }

    function randomDelay() {
        return Math.floor(1000 * Math.random())
    }

    function setTimeout(func, interval, ...params) {
        return setTimeoutComponent.createObject(app, { func, interval, params } )
    }

    function clearTimeout(timerObj) {
        timerObj.stop()
        timerObj.destroy()
    }

    Component {
        id: setTimeoutComponent
        Timer {
            property var func
            property var params
            running: true
            repeat: false
            onTriggered: {
                func(...params)
                destroy()
            }
        }
    }
}

 

Disclaimer: this is a minimal implementation of setTimeout() not a fully functioning implementation. It is here for the purposes of this blog, specifically to talk about aspects of ECMAScript. If you want to use this implementation you will need to adapt it accordingly.

 

This implementation of setTimeout() returns a Timer object (instead of a timerId). When the time delay specified in milliseconds has been reached the nominated Javascript function is triggered with the parameters you supplied to setTimeout(). If you wish to abort the action, you can passed the Timer object to the clearTimeout() function.

 

 

This implementation will now be deconstructed:

  • Global functions in AppStudio
  • ECMAScript 6 Arrow Functions
  • ECMAScript 6 Property shorthand
  • ECMAScript 6 Rest parameter and Spread syntax

 

3. Global functions in AppStudio

 

Functions implemented in the top level component (here, it was App) will be seen by all child components. In this example, we have a StackView which means, all pages that are pushed to the StackView will have access to the setTimeout() function.

 

4. Arrow functions

 

The first parameter to setTimeout() refers to a function or code. In our examples, we have been using the ECMAScript 6 arrow function syntax for anonymous functions. e.g. the arrow function (a, b, c) => console.log(a, b, c) appears in the following snippet:

 

setTimeout( (a, b, c) => console.log(a, b, c), randomDelay(), 31, 32, 33)

 

When used appropriately, arrow functions can help improve code readablity and maintainability.

 

For comparison, let's look at equivalent implementation to the above arrow function.

 

 

4.1 Original Anonymous functions

 

Prior to ECMAScript 6, we could always do anonymous functions, but the syntax was somewhat cumbersome with the required use of the function keyword:

 

setTimeout( function (a, b, c) { console.log(a, b, c) }, randomDelay(), 31, 32, 33 )

 

 

4.2 Using a named function

 

Sometimes, to avoid the use of the anonymous function syntax, I find it better to name things clearly and space out my code for increase readability and maintainability:

 

function testFunction(a, b, c) {
    console.log(a, b, c)
}

setTimeout(testFunction, randomDelay(), 31, 32, 33)

 

4.3 Calling console.log directly

 

For completeness, we recognize that console.log, is, itself, a function reference, so, we can supply this directly to the setTimeout() function.

 

setTimeout(console.log, randomDelay(), 31, 32, 33)

 

 

5. Property shorthand syntax

 

ECMAScript 6 introduces a property shorthand syntax for object creation.

In the following snippets, compare the code for both obj1 and obj2.

They both yield the same value, but the code for obj2 is much shorter and easier to read.

 

 

var x = 11
var y = 22
var z = 33
var obj1 = { x: x, y: y, z: z } // obj1 = { x: 11, y: 22, z: 33 }
var obj2 = { x, y, z } // obj2 = { x: 11, y: 22, z: 33 }

 

We used the shorthand syntax to simplify the following

 

setTimeoutComponent.createObject(app, { func:func, internval:interval, args:args } )

 

into:

 

setTimeoutComponent.createObject(app, { func, interval, args } )

 

 

6. Rest parameter and Spread syntax

 

ECMAScript 6 also introduces rest parameters and spread syntax.

We used it in the declaration of the setTimeout function to pick up all remaining parameters as an array.

 

function setTimeout(func, interval, ...params) {

 

when called with:

 

setTimeout( (a, b, c) => console.log(a, b, c), randomDelay(), 31, 32, 33)

 

results in:

 

func = (a, b, c) => console.log(a, b, c)
interval = randomDelay()
params = [ 31, 32, 33 ]

 

When calling func we passed the parameters using the spread syntax,  i.e.

 

func(...params)

 

which passes the parameter array as individual parameters to the function, i.e.

 

func(31, 32, 33)

 

which ultimate calls

 

console.log(31, 32, 33)

 

Closing Remarks

 

This blog discusses an AppStudio implementation of setTimeout(). It was used as a way to give you a glimpse of some of the new ECMAScript 6 features, i.e. arrow functions, property shorthand and rest parameters and spread syntax which you can utilize in your AppStudio apps.

 

I plan to talk about Promises in ECMAScript 6 and AppStudio in a future blog. That blog will reuse the setTimeout() function and arrow functions introduced here.

 

In the meantime, please help us improve Promises in AppStudio by voting on fixing Promise chains bug https://bugreports.qt.io/browse/QTBUG-71329 and by voting on implementing async/await support in https://bugreports.qt.io/browse/QTBUG-58620

 

 

References

 

 

Send us your feedback to appstudiofeedback@esri.com

 

Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial.

 

Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase.

 

The AppStudio team periodically hosts workshops and webinars; please click on this link to leave your email if you are interested in information regarding AppStudio events.

1. Introduction

 

This amazing clipart can be produced with a surprising short amount of code:

 

 

 

Text {
    text: "\u{1f304}"
    font.pointSize: 128
}

View EmojiSunrise.qml on GitHub Gist.

 

This blog talks about Unicode and the wonderful emojis and cliparts that are hidden within. It also covers some new syntax introduced in AppStudio 3.3, Qt 5.12.1 and ECMAScript 7 (which includes ECMAScript 6).

 

2. Use Unicode Clipart to rapidly create UI

 

You can use Unicode emojis and clipart in Buttons and MenuItems:

 

import QtQuick 2.7
import QtQuick.Controls 2.2
import ArcGIS.AppFramework 1.0

App {
    id: app
    width: 400 * AppFramework.displayScaleFactor
    height: 640 * AppFramework.displayScaleFactor

    Button {
        id: menuButton
        anchors.right: parent.right
        anchors.margins: 10
        text: "\u{2630}"
        onClicked: menu.open()
    }

    Menu {
        id: menu
        x: menuButton.x + menuButton.width - menu.width
        y: menuButton.y + menuButton.height
        MenuItem { text: "\u{1f4c2}  Open" }
        MenuItem { text: "\u{1f4be}  Save" }
        MenuItem { text: "\u{1f5a8}  Print" }
        MenuItem { text: "\u{2702}  Cut" }
        MenuItem { text: "\u{1f4cb}  Paste" }
        MenuItem { text: "\u{1f4c5}  Calendar" }
        MenuItem { text: "\u{2699}  Settings" }
        MenuItem { text: "\u{1f6aa}  Exit" }
    }
}

 

 

 

You can use emojis to improve text shown to the user:

 

import QtQuick 2.7
import ArcGIS.AppFramework 1.0

App {
    id: app
    width: 520 * AppFramework.displayScaleFactor
    height: 640 * AppFramework.displayScaleFactor

    ListView {
        anchors.fill: parent
        model: [
            '  \u{2139}  "Do. Or do not. There is no try." — Yoda',
            '  \u{26a0}  "It’s not wise to upset a Wookiee." - Han Solo',
            '  \u{1f534}  "It’s a trap!" - Admiral Ackbar'
        ]
        delegate: Text { text: modelData }
    }
}

 

 

3. Where is the list of emojis?

 

Using the String.fromCodePoint(codePoint) you can display any Unicode characters base on its code point number. You can use this code pattern to iterate code points searching for interesting emojis and clipart. The following shows the emojis and clipart from 1F300 to 1F3FF.

 

import QtQuick 2.7
import ArcGIS.AppFramework 1.0

App {
    id: app
    width: 520 * AppFramework.displayScaleFactor
    height: 640 * AppFramework.displayScaleFactor

    property int startCodePoint: 0x1f300
    property int endCodePoint: 0x1f3ff
    property int codePoint: startCodePoint

    Text {
        anchors.centerIn: parent
        text: String.fromCodePoint(codePoint)
        font.pointSize: 64
    }

    Timer {
        id: animTimer
        repeat: true
        running: true
        interval: 100
        onTriggered: codePoint = (codePoint < endCodePoint) ? codePoint + 1 : startCodePoint
    }
}

 

You can also use online lists to search for Unicode emojis and clipart, i.e. "sunrise over mountains" can be found in the following lists:

 

https://unicode.org/emoji/charts/full-emoji-list.html#1f304

https://emojipedia.org/sunrise-over-mountains/

 

Some interesting Unicode blocks are 2600 - 2BFF and 1F300 - 1F8FF.

 

For a listing of Unicode blocks refer to: https://en.wikipedia.org/wiki/Unicode_block

 

4. Character vs Code Point

 

A character is represented internally as a 16 bit number which means there can be a total of 65536.

 

Unicode allows for 17 planes, each of 65,536 characters. This gives a total of 1,114,112 possible characters (i.e. 0x to 0x10ffff).

 

If a Unicode Code Point is within a 16 bit number, it will only require 1 character to represent, otherwise, we will need two characters.

 

For instance, in AppStudio 3.2, Qt 5.11.2 and ES 5, we needed two characters to represent the sunrise over mountains emoji, i.e. "\ud83c\udf04".

 

The code point syntax "\u{1f304}" in AppStudio 3.3, Qt 5.12.1 and ECMAScript 7 (which includes ECMAScript 6) is new. It actually expands into the two characters that make it. The String.fromCodePoint() function is also new.


"\u{1f304}" === String.fromCodePoint(0x1f304)

 

we can always convert code point back into their numbers with:

 

"\u{1f304}".codePointAt(0) === 0x1f304

 

We can also convert code points to characters:

 

"\u{1f304}".length === 2
"\u{1f304}".charCodeAt(0) === 0xd83c
"\u{1f304}".charCodeAt(1) === 0xdf04
"\u{1f304}" === "\u{d83c}\u{df04}"

 


 

5. Caveats

 


5.1 Not all clipart is available or consistent cross platform

 


When viewing Unicode clipart on Windows 10 you find a set of Mahjong tiles at 1F000 - 1F02B. You might be inclined to use them in a Mahjong app:

 

 

However, when you check on other platforms, you may quickly change your mind. On Linux, we get the complete set of clipart, but, in black and white. On macOS, all of the characters appear black and white except for Mahjong Red Dragon appears in colour. On iOS, only the Mahjong Red Dragon appears whereas all other characters are unavailable. On Android, only the Mahjong Red Dragon tile is available there too. Also note the appearance of the no data tile is different on every platform.

 

On Linux:

 

 

On macOS:

 

 

On iOS:

 

 

On Android:

 

 

5.2 Issue with some CodePoints on Buttons

 

In the following code snippet we try to render the same text in Text and Button components:

 

import QtQuick 2.7
import QtQuick.Controls 2.2
import ArcGIS.AppFramework 1.0

App {
    id: app
    width: 520 * AppFramework.displayScaleFactor
    height: 640 * AppFramework.displayScaleFactor

    Column {
        Text {
            text: "\u{263a} \u{1f304} "
            font.pointSize: 32
        }

        Button {
            text: "\u{263a} \u{1f304} "
            font.pointSize: 32
            font.capitalization: Font.MixedCase
        }
    }
}

 

we see that the smiley and the face emoji renders okay on both the Text and the Button

 

 

However, the Button requires a workaround that disables the material design capitalization on Buttons. This is due to a bug in Qt 5.12.x reported in https://bugreports.qt.io/browse/QTBUG-75559. Help get this bug fix by voting on it.

 

 

Summary

 

You can find a lot of clipart or emojis that help deliver high visual impact for little expense in code.

 

Caveats are not every platform offers the same set of clipart or emojis nor are they consistent in appearance on the ones that they do offer.

 

Cross platform testing is essential.

 

 

References:

 

Send us your feedback to appstudiofeedback@esri.com

 

Become an AppStudio for ArcGIS developer! Watch this video on how to sign up for a free trial.

 

Follow us on Twitter @AppStudioArcGIS to keep up-to-date on the latest information and let us know about your creations built using AppStudio to be featured in the AppStudio Showcase.

 

The AppStudio team periodically hosts workshops and webinars; please click on this link to leave your email if you are interested in information regarding AppStudio events.