Bug 60379 - HttpClient GetStringAsync on WatchOS is Extremely Slow
Summary: HttpClient GetStringAsync on WatchOS is Extremely Slow
Status: RESOLVED UPSTREAM
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.WatchOS.dll ()
Version: XI 11.2 (d15-4)
Hardware: Other Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2017-10-25 21:13 UTC by John R
Modified: 2017-10-27 16:19 UTC (History)
3 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Sample Project Showing Issue (76.60 KB, application/zip)
2017-10-25 21:13 UTC, John R
Details
Xcode Project Showing Same Issue (67.02 KB, application/zip)
2017-10-26 17:03 UTC, John R
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED UPSTREAM

Description John R 2017-10-25 21:13:13 UTC
Created attachment 25459 [details]
Sample Project Showing Issue

I've building an Apple Watch app that makes web calls directly from the watch, however, in my findings I am seeing extremely slow performance from HttpClient.GetStringAsync().

From the WatchOS App: 4500ms - 7000ms
From Google Chrome hitting the test url: 280.37ms

Here are my results from the test url (configured in watch app in attached project): http://samples.openweathermap.org/data/2.5/weather?zip=94040,us&appid=b1b15e88fa797225412429c1c50c122a1

-----

2017-10-25 14:56:19.122 watchhttpclientslowiOSwatchkitExtension[6392:1365101] PerformanceTester - 61312 - GetWeather - Started
2017-10-25 14:56:25.332 watchhttpclientslowiOSwatchkitExtension[6392:1365101] PerformanceTester - 61312 - GetWeather - Stopped: 00:00:06.2064543
2017-10-25 14:56:25.333 watchhttpclientslowiOSwatchkitExtension[6392:1365101] {"coord":{"lon":-122.08,"lat":37.39},"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"base":"stations","main":{"temp":277.14,"pressure":1025,"humidity":86,"temp_min":275.15,"temp_max":279.15},"visibility":16093,"wind":{"speed":1.67,"deg":53.0005},"clouds":{"all":1},"dt":1485788160,"sys":{"type":1,"id":471,"message":0.0116,"country":"US","sunrise":1485789140,"sunset":1485826300},"id":5375480,"name":"Mountain View","cod":200}
2017-10-25 14:57:19.267 watchhttpclientslowiOSwatchkitExtension[6392:1365101] PerformanceTester - 63801 - GetWeather - Started
2017-10-25 14:57:24.287 watchhttpclientslowiOSwatchkitExtension[6392:1365101] PerformanceTester - 63801 - GetWeather - Stopped: 00:00:05.0203054
2017-10-25 14:57:24.288 watchhttpclientslowiOSwatchkitExtension[6392:1365101] {"coord":{"lon":-122.08,"lat":37.39},"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"base":"stations","main":{"temp":277.14,"pressure":1025,"humidity":86,"temp_min":275.15,"temp_max":279.15},"visibility":16093,"wind":{"speed":1.67,"deg":53.0005},"clouds":{"all":1},"dt":1485788160,"sys":{"type":1,"id":471,"message":0.0116,"country":"US","sunrise":1485789140,"sunset":1485826300},"id":5375480,"name":"Mountain View","cod":200}

------




Any help would be appreciated.

John R


System Details:


=== Visual Studio Community 2017 for Mac ===

Version 7.2 (build 636)
Installation UUID: deb4c29c-a6bd-41aa-98db-a36303c16de8
Runtime:
	Mono 5.4.0.201 (2017-06/71277e78f6e) (64-bit)
	GTK+ 2.24.23 (Raleigh theme)

	Package version: 504000201

=== NuGet ===

Version: 4.3.1.4445

=== .NET Core ===

Runtime: /usr/local/share/dotnet/dotnet
Runtime Versions:
	2.0.0
	1.1.1
	1.0.4
SDK: /usr/local/share/dotnet/sdk/2.0.0/Sdks
SDK Versions:
	2.0.0
	1.0.1
MSBuild SDKs: /Library/Frameworks/Mono.framework/Versions/5.4.0/lib/mono/msbuild/15.0/bin/Sdks

=== Xamarin.Profiler ===

Version: 1.5.6
Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler

=== Xamarin.Android ===

Version: 8.0.0.33 (Visual Studio Community)
Android SDK: /Users/u0225413/Library/Developer/Xamarin/android-sdk-macosx
	Supported Android versions:
		7.1 (API level 25)
		8.0 (API level 26)

SDK Tools Version: 26.1.1
SDK Platform Tools Version: 26.0.1
SDK Build Tools Version: 26.0.1

Java SDK: /usr
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)

Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

=== Apple Developer Tools ===

Xcode 9.0.1 (13249)
Build 9A1004

=== Xamarin.iOS ===

Version: 11.2.0.11 (Visual Studio Community)
Hash: 2f8bbec0
Branch: d15-4-xi
Build date: 2017-10-05 20:56:05-0400

=== Xamarin.Mac ===

Version: 3.8.0.49 (Visual Studio Community)

=== Xamarin Inspector ===

Version: 1.3.2
Hash: 461f09a
Branch: 1.3-release
Build date: Tue, 03 Oct 2017 18:26:57 GMT
Client compatibility: 1

=== Build Information ===

Release ID: 702000636
Git revision: b349c8fc0c2e1aab0179917d56eed75eb3a03337
Build date: 2017-10-06 15:20:04-04
Xamarin addins: a8da992c78f7eb65b3cdb3f951c34a30337cab35
Build lane: monodevelop-lion-d15-4

=== Operating System ===

Mac OS X 10.13.0
Darwin 17.0.0 Darwin Kernel Version 17.0.0
    Thu Aug 24 22:01:05 PDT 2017
    root:xnu-4570.1.46~3/RELEASE_X86_64 x86_64

=== Enabled user installed addins ===

MFractor 3.02.12
Azure Functions development (Preview) 7.2
NuGet Package Management Extensions 0.12.5
Redth's Addins 1.0.9
Comment 1 John R 2017-10-25 21:14:21 UTC
I'm using System.Net.Http.

I've tried with modernhttpclient.standard and without modernhttpclient.standard and the results remain the same.
Comment 2 John R 2017-10-25 21:28:04 UTC
Note: I've also tested this on an actual Apple Watch Series 3 with Cellular both connected to my iPhone and with my iPhone off with the iPhone on LTE and also when on WIFI. It's roughly between 4-6 seconds in all situations I've tested.
Comment 3 Rolf Bjarne Kvinge [MSFT] 2017-10-26 08:08:03 UTC
Marking bug as private since the url contains an API key.
Comment 4 John R 2017-10-26 16:37:46 UTC
Not sure if this needs to be private as the apikey is actually against the sample service and is not live data which is available to everyone from this url: http://openweathermap.org/current

Either way is fine with me.
Comment 5 John R 2017-10-26 17:03:07 UTC
Created attachment 25473 [details]
Xcode Project Showing Same Issue

I've created a sample XCode project and it seems to be showing the same issue so this leads me to believe that this is not a Xamarin issue. I'll keep looking into this.
Comment 6 John R 2017-10-26 17:26:50 UTC
Others seem to be hitting this issue as well in WatchOS. A sample project that compares NSData to NSURLSession is available here: https://github.com/joshbuhler/urlTest/ when NSData is much faster than NSURLSession. I modified my sample Xcode project to use NSData and it returns in about 335ms, almost 10x faster than the 3.5s I was seeing using NSURLSession.
Comment 7 John R 2017-10-26 19:09:15 UTC
Please mark this bug as Public for others to find.

TLDR; I have found a workaround using NSData and NSUrlConnection.SendSychronousRequest (while dispatching it Async). As it seems to be an Apple bug, I'm hoping they'll resolve it before they deprecate the above methods for retrieving data.

------

I just tested calling the following code in my Xamarin solution:

private async Task GetWeatherNSData()
        {
            using (new PerformanceTester("GetWeatherNSData"))
            {

                NSUrl url = new NSUrl("http://samples.openweathermap.org/data/2.5/weather?zip=94040,us&appid=b1b15e88fa797225412429c1c50c122a1");
                var data = NSData.FromUrl(url);
                Console.WriteLine(data);
            }



        }

And it returns in less than 300ms instead of the 3000ms+ of the NSUrlSession (HttpClient). The docs for WatchOS say to not use NSData FromUrl for files stored on the web because it is not async, however, a 3s wait vs a 300ms wait is a big deal in an app, so I'm attempting to make it work until they resolve/address the delay in service calls on the watch.

In the service I'm actually using, I had to pass in an auth token, so the following worked for that:

NSMutableUrlRequest request = new NSMutableUrlRequest(url, NSUrlRequestCachePolicy.ReturnCacheDataElseLoad, 20.0f);

                    var keys = new object[] { "Authorization" };
                    var objects = new object[] { "Token " + Constants.ESVBibleAPIToken };
                    var dictionary = NSDictionary.FromObjectsAndKeys(objects, keys);
                    request.Headers = dictionary;

                    //NSUrlConnection blah = new NSUrlConnection(request, null, true);
                    NSUrlResponse response;
                    NSError error;

                    var result = NSUrlConnection.SendSynchronousRequest(request, out response, out error); 

 I was also able to get it to work async (it was blocking the UI changes) by running the NSUrlConnection.SendSynchronousRequest call inside the following

DispatchQueue.GetGlobalQueue(DispatchQueuePriority.Background).DispatchAsync(() =>
             
I hope this helps somebody.

Regards,

John R
Comment 8 Timothy Risi 2017-10-26 19:35:57 UTC
I'm closing this bug since it looks to be an Apple issue.
Comment 9 John R 2017-10-26 21:32:16 UTC
It looks like NSUrlConnection does not work on an actual Apple Watch (only works in the Simulator...). See the details here on the Apple Developer Forums documenting the slowness I've reported as well as the non-support for NSUrlConnection on the Apple Watch:

https://forums.developer.apple.com/thread/17176

specifically this quote:

"Next, with regards NSURLConnection, it’s not been arbitrarily removed.  Rather, it would not work on the Watch because it does all of its networking ‘in process’, and that doesn’t work on the Watch (again, I’m going to point you to the thread I referenced earlier)."
Comment 10 Rolf Bjarne Kvinge [MSFT] 2017-10-27 11:30:54 UTC
(In reply to John R from comment #4)
> Not sure if this needs to be private as the apikey is actually against the
> sample service and is not live data which is available to everyone from this
> url: http://openweathermap.org/current
> 
> Either way is fine with me.

(In reply to John R from comment #7)
> Please mark this bug as Public for others to find.

OK, I've made the bug public again (I made it private because it's always better to be safe than sorry)

Regarding your issue: I've found that pretty much anything that happens on the watch is very slow :(

With regards to networking, watchOS will dispatch the network request to a separate process, which decides how to send it (direct LTE or Wifi, or bluetooth tunnel to the phone), and it wouldn't surprise me if the device also bundles requests together to save battery (so it waits 3-4 seconds before sending anything to see if there are more requests coming from other processes - this is just pure speculation though).

Apple's recommendation would likely be to not wait for the user to raise his wrist before updating your data, instead update in the background on the phone, then ask the phone to send this data to the watch, so that the data is already there when the user raises his wrist, only slightly out of date).
Comment 11 John R 2017-10-27 16:18:41 UTC
Rolf,

Thanks for the response. Yeah, I definitely agree on the 'better safe than sorry' idea.

The part that's a little crazy is that the NSData (which uses NSURLConnection internally) returns quickly and works on the simulator, but that's it sandboxed on the apple watch and doesn't work. In my case, it actually crashes when deployed on the actual apple watch. I chased an alternative that worked on the simulator, but doesn't work on the device...

As for the usage in my app, what it loads is based on user interaction on a large number of options. I have some ideas on how to make the app seem quicker in regards to the service calls. We'll see how my ideas pan out.

Thanks again for the help on this,

John
Comment 12 John R 2017-10-27 16:19:34 UTC
As reference for others, I've found this post on the Apple Forums that also talks about the slowness and refers to a bug that has been reported. I've asked more questions on it hoping for some clarification from Apple: https://forums.developer.apple.com/message/263903#263903

John