How make multiple WebRequests with different URLs in WebRequest SDK sample?

I want to make multiple WebRequests with different URLs in the WebRequest SDK sample.

In the WebRequest SDK sample I have duplicated the WebRequestDelegate.mc file and renamed it WebRequestDelegate2.mc.

In the new WebRequestDelegate2.mc file I have renamed the onReceive function to onReceive2 and changed the end of makeRequest() function to method(:onReceive2).

In both WebRequestDelegate.mc and WebRequestDelegate2.mc added makeRequest(); to initialize function so it makes request at start without pressing a button.

In the WebRequestView.mc file I have duplicated the onReceive function and renamed it to onReceive2.

In the WebRequestApp.mc file I have modified the getInitialView function to this:

    //! Return the initial view for the app
    //! @return Array Pair [View, Delegate]
    public function getInitialView() as Array<Views or InputDelegates>? {
        var view = new $.WebRequestView();
        var delegate = new $.WebRequestDelegate(view.method(:onReceive));
        var delegate2 = new $.WebRequestDelegate2(view.method(:onReceive2));
        return [view, delegate] as Array<Views or InputDelegates>;
    }

In the WebRequestView.mc I have two string variables to display the content which is received in onReceive and onReceive2 functions in WebRequestView.mc.

So far everything works and the data loads correctly on startup.

But if I press the menu or select button it seems it only runs the onMenu() and onSelect() function in WebRequestDelegate.mc file and not also in WebRequestDelegate2.mc.

Did I make this correctly or is there a much better way to make multiple WebRequests with different URLs in the WebRequest SDK sample?

  • What you want to do is chain your requests - do them one at a time.  Wait for the first to complete, then do the second.

    In WebRequestdelegate, you have makeRequest and onReceive()

    Create functions for makeRequest2 and in that onReceive, call makeRequest2, with it's callback being onReceive2,

    so makeRequest is called and then

    onReceive is called, which calls makeRequest2

    and then onRecieve2 is called.

  • Thank you Jim.

    When I press the menu or select button it only refreshes the string1 in WebRequestView.mc.

    And when using two different urls in makeRequest and makeRequest2 in WebRequestDelegate.mc, string1 and string2 in WebRequestView.mc both have the value of string1.

    WebRequestApp.mc:

    //
    // Copyright 2015-2021 by Garmin Ltd. or its subsidiaries.
    // Subject to Garmin SDK License Agreement and Wearables
    // Application Developer Agreement.
    //
    
    import Toybox.Application;
    import Toybox.Lang;
    import Toybox.WatchUi;
    
    //! This app demonstrates how to make web requests through GCM.
    class WebRequestApp extends Application.AppBase {
    
        //! Constructor
        public function initialize() {
            AppBase.initialize();
        }
    
        //! Handle app startup
        //! @param state Startup arguments
        public function onStart(state as Dictionary?) as Void {
        }
    
        //! Handle app shutdown
        //! @param state Shutdown arguments
        public function onStop(state as Dictionary?) as Void {
        }
    
        //! Return the initial view for the app
        //! @return Array Pair [View, Delegate]
        public function getInitialView() as Array<Views or InputDelegates>? {
            var view = new $.WebRequestView();
            var delegate = new $.WebRequestDelegate(view.method(:onReceive));
            var delegate2 = new $.WebRequestDelegate(view.method(:onReceive2));
            return [view, delegate] as Array<Views or InputDelegates>;
            //return [view, delegate2] as Array<Views or InputDelegates>;
        }
    }

    WebRequestDelegate.mc:

    //
    // Copyright 2016-2021 by Garmin Ltd. or its subsidiaries.
    // Subject to Garmin SDK License Agreement and Wearables
    // Application Developer Agreement.
    //
    
    import Toybox.Communications;
    import Toybox.Lang;
    import Toybox.WatchUi;
    
    //! Creates a web request on menu / select events
    class WebRequestDelegate extends WatchUi.BehaviorDelegate {
        private var _notify as Method(args as Dictionary or String or Null) as Void;
    
        //! Set up the callback to the view
        //! @param handler Callback method for when data is received
        public function initialize(handler as Method(args as Dictionary or String or Null) as Void) {
            WatchUi.BehaviorDelegate.initialize();
            _notify = handler;
            // Make request at start
            makeRequest();
            return true;
        }
    
        //! On a menu event, make a web request
        //! @return true if handled, false otherwise
        public function onMenu() as Boolean {
            makeRequest();
            return true;
        }
    
        //! On a select event, make a web request
        //! @return true if handled, false otherwise
        public function onSelect() as Boolean {
            makeRequest();
            return true;
        }
    
        //! Make the web request
        private function makeRequest() as Void {
            _notify.invoke("Executing\nRequest");
    
            var options = {
                :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
                :headers => {
                    "Content-Type" => Communications.REQUEST_CONTENT_TYPE_URL_ENCODED
                }
            };
    
            Communications.makeWebRequest(
                "https://jsonplaceholder.typicode.com/todos/115",
                {},
                options,
                method(:onReceive)
            );
        }
    
        //! Receive the data from the web request
        //! @param responseCode The server response code
        //! @param data Content from a successful request
        public function onReceive(responseCode as Number, data as Dictionary?) as Void {
            if (responseCode == 200) {
                _notify.invoke(data);
            } else {
                _notify.invoke("Failed to load\nError: " + responseCode.toString());
            }
            makeRequest2();
            return true;
        }
    
        //! Make the web request 2
        private function makeRequest2() as Void {
            _notify.invoke("Executing\nRequest");
    
            var options = {
                :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
                :headers => {
                    "Content-Type" => Communications.REQUEST_CONTENT_TYPE_URL_ENCODED
                }
            };
    
            Communications.makeWebRequest(
                "https://jsonplaceholder.typicode.com/todos/118",
                {},
                options,
                method(:onReceive2)
            );
        }
    
        //! Receive the data from the web request 2
        //! @param responseCode The server response code
        //! @param data Content from a successful request
        public function onReceive2(responseCode as Number, data as Dictionary?) as Void {
            if (responseCode == 200) {
                _notify.invoke(data);
            } else {
                _notify.invoke("Failed to load\nError: " + responseCode.toString());
            }
        }
    }
    WebRequestView.mc:
    //
    // Copyright 2015-2021 by Garmin Ltd. or its subsidiaries.
    // Subject to Garmin SDK License Agreement and Wearables
    // Application Developer Agreement.
    //
    
    import Toybox.Graphics;
    import Toybox.Lang;
    import Toybox.WatchUi;
    
    //! Shows the web request result
    class WebRequestView extends WatchUi.View {
        private var _message as String = "Press menu or\nselect button";
        private var _string1 as String = "";
        private var _string2 as String = "";
    
        //! Constructor
        public function initialize() {
            WatchUi.View.initialize();
        }
    
        //! Load your resources here
        //! @param dc Device context
        public function onLayout(dc as Dc) as Void {
        }
    
        //! Restore the state of the app and prepare the view to be shown
        public function onShow() as Void {
        }
    
        //! Update the view
        //! @param dc Device Context
        public function onUpdate(dc as Dc) as Void {
            dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);
            dc.clear();
            //dc.drawText(dc.getWidth() / 2, dc.getHeight() / 2, Graphics.FONT_MEDIUM, _message, Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
            dc.drawText(dc.getWidth() / 2, dc.getHeight() / 2 -(dc.getHeight() / 5), Graphics.FONT_TINY, _string1, Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
            dc.drawText(dc.getWidth() / 2, dc.getHeight() / 2 +(dc.getHeight() / 5), Graphics.FONT_TINY, _string2, Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
        }
    
        //! Called when this View is removed from the screen. Save the
        //! state of your app here.
        public function onHide() as Void {
        }
    
        //! Show the result or status of the web request
        //! @param args Data from the web request, or error message
        public function onReceive(args as Dictionary or String or Null) as Void {
            if (args instanceof String) {
                _message = args;
                _string1 = args;
            } else if (args instanceof Dictionary) {
                // Print the arguments duplicated and returned by jsonplaceholder.typicode.com
                var keys = args.keys();
                _message = "";
                _string1 = "";
                for (var i = 0; i < keys.size(); i++) {
                    _message += Lang.format("$1$: $2$\n", [keys[i], args[keys[i]]]);
                    _string1 = "STRING 1\n"+args.get("title").toString();
                }
            }
            WatchUi.requestUpdate();
        }
    
        //! Show the result or status of the web request 2
        //! @param args Data from the web request, or error message
        public function onReceive2(args as Dictionary or String or Null) as Void {
            if (args instanceof String) {
                _message = args;
                _string2 = args;
            } else if (args instanceof Dictionary) {
                // Print the arguments duplicated and returned by jsonplaceholder.typicode.com
                var keys = args.keys();
                _message = "";
                _string2 = "";
                for (var i = 0; i < keys.size(); i++) {
                    _message += Lang.format("$1$: $2$\n", [keys[i], args[keys[i]]]);
                    _string2 = "STRING 2\n"+args.get("title").toString();
                }
            }
            WatchUi.requestUpdate();
        }
    }
    

  • I made another test with two different urls.

    It diplays the same content for string1 and string2.

    And when pressing the menu or select button it refreshes only string1.

  • In your onReceive and onReceive2 callbacks (at the end) you'll want to do a WatchUi.requestUpdate() so onUpdate() will get called.

  • There is already WatchUi.requestUpdate(); at the end of onReceive and onReceive2 in WebRequestView.mc.

    When I press the menu or select button it only refreshes the string1 in WebRequestView.mc.

    And when using two different urls in makeRequest and makeRequest2 in WebRequestDelegate.mc, string1 and string2 in WebRequestView.mc both have the value of string1.

    I have updated the code posted in my reply above so you can see my whole code.

  • Add some System.println() calls so you can see when things are happening.

  • I am really a beginner with Monkey C coming from Unity.

    I think it has something to do with this in WebRequestApp.mc.

    When I change the line to return [view, delegate2] as Array<Views or InputDelegates>; than only string2 from onReceive2 is refreshing when pressing the select or menu button.

    And when using [view, delegate] it only refreshes the string 1 from onReceive.

    //! Return the initial view for the app
        //! @return Array Pair [View, Delegate]
        public function getInitialView() as Array<Views or InputDelegates>? {
            var view = new $.WebRequestView();
            var delegate = new $.WebRequestDelegate(view.method(:onReceive));
            var delegate2 = new $.WebRequestDelegate(view.method(:onReceive2));
            return [view, delegate] as Array<Views or InputDelegates>;
            //return [view, delegate2] as Array<Views or InputDelegates>;
        }

    The other thing I dont understand is this _notify = handler; in WebRequestDelegate.mc.

    Is the problem that both onReceive and onReceive2 have the same _notify.invoke?

    Is this why both values are the same?

    //! Creates a web request on menu / select events
    class WebRequestDelegate extends WatchUi.BehaviorDelegate {
        private var _notify as Method(args as Dictionary or String or Null) as Void;
    
        //! Set up the callback to the view
        //! @param handler Callback method for when data is received
        public function initialize(handler as Method(args as Dictionary or String or Null) as Void) {
            WatchUi.BehaviorDelegate.initialize();
            _notify = handler;
            // Make request at start
            makeRequest();
            return true;
        }

  • You don't need two delegates for input.  Just one. The functionality of chaining the calls only needs one.  In it, with onSelect, etc. it makes the 1st request, and in it's onReceive callback, it makes the second request

  • This is how I have it now. I am sorry I dont get what the problem is.

    I have added some System.println in onReceive and onReceive2 in WebRequestDelegate.mc.

    The data is correct.

    But something is wrong in the other onReceive and onReceive2 in WebRequestView.mc.

    At the end both strings are the same.

  • Thank you Jim again!

    I have now found out that I only need one onReceive function in WebRequestView.mc.

    I pass now a dataNumber variable in onReceive and onReceive2 in WebRequestDelegate.mc.

    _notify.invoke(data, 1); or _notify.invoke(data, 2);

    Than in onReceive function in WebRequestView.mc I know which data is coming.

    Here is the complete working code if someone is interested:

    WebRequestApp.mc:
    //
    // Copyright 2015-2021 by Garmin Ltd. or its subsidiaries.
    // Subject to Garmin SDK License Agreement and Wearables
    // Application Developer Agreement.
    //
    
    import Toybox.Application;
    import Toybox.Lang;
    import Toybox.WatchUi;
    
    //! This app demonstrates how to make web requests through GCM.
    class WebRequestApp extends Application.AppBase {
    
        //! Constructor
        public function initialize() {
            AppBase.initialize();
        }
    
        //! Handle app startup
        //! @param state Startup arguments
        public function onStart(state as Dictionary?) as Void {
        }
    
        //! Handle app shutdown
        //! @param state Shutdown arguments
        public function onStop(state as Dictionary?) as Void {
        }
    
        //! Return the initial view for the app
        //! @return Array Pair [View, Delegate]
        public function getInitialView() as Array<Views or InputDelegates>? {
            var view = new $.WebRequestView();
            var delegate = new $.WebRequestDelegate(view.method(:onReceive));
            return [view, delegate] as Array<Views or InputDelegates>;
        }
    }
    WebRequestDelegate.mc:
    //
    // Copyright 2016-2021 by Garmin Ltd. or its subsidiaries.
    // Subject to Garmin SDK License Agreement and Wearables
    // Application Developer Agreement.
    //
    
    import Toybox.Communications;
    import Toybox.Lang;
    import Toybox.WatchUi;
    using Toybox.System;
    
    //! Creates a web request on menu / select events
    class WebRequestDelegate extends WatchUi.BehaviorDelegate {
        //private var _notify as Method(args as Dictionary or String or Null) as Void;
        private var _notify as Method(args as Dictionary or String or Null, dataNumber as Number) as Void;
    
        //! Set up the callback to the view
        //! @param handler Callback method for when data is received
        //public function initialize(handler as Method(args as Dictionary or String or Null) as Void) {
        public function initialize(handler as Method(args as Dictionary or String or Null, dataNumber as Number) as Void) {
            WatchUi.BehaviorDelegate.initialize();
            _notify = handler;
            // Make request at start
            makeRequest();
            return true;
        }
    
        //! On a menu event, make a web request
        //! @return true if handled, false otherwise
        public function onMenu() as Boolean {
            makeRequest();
            return true;
        }
    
        //! On a select event, make a web request
        //! @return true if handled, false otherwise
        public function onSelect() as Boolean {
            makeRequest();
            return true;
        }
    
        //! Make the web request
        private function makeRequest() as Void {
            _notify.invoke("Executing\nRequest", 1);
    
            var options = {
                :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
                :headers => {
                    "Content-Type" => Communications.REQUEST_CONTENT_TYPE_URL_ENCODED
                }
            };
    
            Communications.makeWebRequest(
                "https://jsonplaceholder.typicode.com/todos/115",
                {},
                options,
                method(:onReceive)
            );
        }
    
        //! Receive the data from the web request
        //! @param responseCode The server response code
        //! @param data Content from a successful request
        public function onReceive(responseCode as Number, data as Dictionary?) as Void {
            if (responseCode == 200) {
                _notify.invoke(data, 1);
                System.println( "onReceive data "+data );
            } else {
                _notify.invoke("Failed to load 1\nError: " + responseCode.toString(), 1);
            }
            makeRequest2();
            return true;
        }
    
        //! Make the web request 2
        private function makeRequest2() as Void {
            _notify.invoke("Executing\nRequest", 2);
    
            var options = {
                :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
                :headers => {
                    "Content-Type" => Communications.REQUEST_CONTENT_TYPE_URL_ENCODED
                }
            };
    
            Communications.makeWebRequest(
                "https://jsonplaceholder.typicode.com/todos/118",
                {},
                options,
                method(:onReceive2)
            );
        }
    
        //! Receive the data from the web request 2
        //! @param responseCode The server response code
        //! @param data Content from a successful request
        public function onReceive2(responseCode as Number, data as Dictionary?) as Void {
            if (responseCode == 200) {
                _notify.invoke(data, 2);
                System.println( "onReceive2 data "+data );
            } else {
                _notify.invoke("Failed to load 2\nError: " + responseCode.toString(), 2);
            }
        }
    }
    WebRequestView.mc:
    //
    // Copyright 2015-2021 by Garmin Ltd. or its subsidiaries.
    // Subject to Garmin SDK License Agreement and Wearables
    // Application Developer Agreement.
    //
    
    import Toybox.Graphics;
    import Toybox.Lang;
    import Toybox.WatchUi;
    
    //! Shows the web request result
    class WebRequestView extends WatchUi.View {
        //private var _message as String = "Press menu or\nselect button";
        private var _string1 as String = "";
        private var _string2 as String = "";
    
        //! Constructor
        public function initialize() {
            WatchUi.View.initialize();
        }
    
        //! Load your resources here
        //! @param dc Device context
        public function onLayout(dc as Dc) as Void {
        }
    
        //! Restore the state of the app and prepare the view to be shown
        public function onShow() as Void {
        }
    
        //! Update the view
        //! @param dc Device Context
        public function onUpdate(dc as Dc) as Void {
            dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);
            dc.clear();
            //dc.drawText(dc.getWidth() / 2, dc.getHeight() / 2, Graphics.FONT_MEDIUM, _message, Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
            dc.drawText(dc.getWidth() / 2, dc.getHeight() / 2 -(dc.getHeight() / 5), Graphics.FONT_TINY, _string1, Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
            dc.drawText(dc.getWidth() / 2, dc.getHeight() / 2 +(dc.getHeight() / 5), Graphics.FONT_TINY, _string2, Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
        }
    
        //! Called when this View is removed from the screen. Save the
        //! state of your app here.
        public function onHide() as Void {
        }
    
        //! Show the result or status of the web request
        //! @param args Data from the web request, or error message
        public function onReceive(args as Dictionary or String or Null, dataNumber as Number) as Void {
            // data from makeRequest
            if (dataNumber == 1){
            if (args instanceof String) {
                //_message = args;
                _string1 = args;
            } else if (args instanceof Dictionary) {
                // Print the arguments duplicated and returned by jsonplaceholder.typicode.com
                var keys = args.keys();
                //_message = "";
                _string1 = "";
                for (var i = 0; i < keys.size(); i++) {
                    //_message += Lang.format("$1$: $2$\n", [keys[i], args[keys[i]]]);
                    _string1 = "STRING 1\n"+args.get("title").toString();
                }
            }
            }
            // data from makeRequest2
            if (dataNumber == 2){
            if (args instanceof String) {
                //_message = args;
                _string2 = args;
            } else if (args instanceof Dictionary) {
                // Print the arguments duplicated and returned by jsonplaceholder.typicode.com
                var keys = args.keys();
                //_message = "";
                _string2 = "";
                for (var i = 0; i < keys.size(); i++) {
                    //_message += Lang.format("$1$: $2$\n", [keys[i], args[keys[i]]]);
                    _string2 = "STRING 2\n"+args.get("title").toString();
                }
            }
            }
            WatchUi.requestUpdate();
        }
    }