How We Built a Mobile Electronic Health Record App Using Xamarin, Angular, and Web API

53
How We Built a Mobile Electronic Health Record App Using Xamarin, Angular, and Web API

Transcript of How We Built a Mobile Electronic Health Record App Using Xamarin, Angular, and Web API

PowerPoint Presentation

How We Built a Mobile Electronic Health Record App Using Xamarin, Angular, and Web API

1

About Matt SpradleySr. Product Manager at AprimaCo-founded 3 software companiesUsed to write codeTinkers every now and thenlinkedin.com/in/mattspradley [email protected]

2

3

How Should we Build a Cross Platform Mobile App?

http://imgs.xkcd.com/comics/efficiency.png

RequirementsTablet 90% functional parity with desktop EHRWork on iOS and Android phones and tabletsIntegrate with on-premise serversNo control over network (DNS, Firewall, etc.)Multiple versions of REST API

Test (POC) Dont Guess

And the ResultsScore66646353FactorWeightHTML/SteroidsHTML/TitaniumNative/XamarinNativeUX32233Code Reuse33321Special UI11233Control12233Effort33221Maintenance32232Cloud Reuse13311OS Integration22233Deployment Options13311Existing Dev Skills12231Hiring22232Vendor Stability32223UTDesign Decision12211UI Testability33311

Winner???

Web UI with Angular Is Easy

  • {{observations.Name}} {{observation.Value}} No known vitals.

    Web UI with Angular Is Easy, Really

    Code and BuildWeb UICode in HTML, Less, AngularJSBuildGruntLessTemplate processJsHintUglifyUnit Tests w/ Jasmine and KarmaDeployTranslateImages to Less

    Chocolate and Peanut Butter

    Our Customers LOVE Aprima NOW

    Go Native with Xamarin for Hard StuffForTaking PicturesImage AnnotationPDF ViewerWhyFlexibilityC#PerformanceCustom UILook and FeelServicesHiring

    Xamarin Forms Demo

    Aprima NOW Hybrid ArchitectureJavascript/HTMLJS Xamarin(C#) BridgeXamarin(C#)AngularJS AppJS BridgeCommon (portable class library)Xamarin.AndroidXamarin.iOSFire EventSubscribeSubscribeHandle EventsHandle EventsFire Event or SubscribeInvoke Callback from SubscriptionFire EventFire EventAppServer

    Javascript Bridge

    Jsbridge.jsJS object provides bridging capabilities to JS code Fire events to C#Add event handlers in JS to be called when event is fired from C#Bridge implementations for iOS, Android, and WinFormsNormalizes event structure for consistency:

    app://{module}/fireEvent?data={jsEventDataJson}&_={random}

    C# -> JS event FiringTo fire an event, call the following. BridgeContext is a bridge which is scoped to the UI WebView component (iOS: UIWebView; Android: WebView; WinForms: Form/Control/etc)

    this.BridgeContext.FireEvent(eventName, data);

    Which calls the following within the BridgeContext instance and actually dispatches the event to javascript. ExecuteScript is implemented differently by each platform.

    ExecuteScript(string.Format("bridge.app._dispatchEvent('{0}', {1});", eventName, json));

    iOS Jsbridge.jsTo send an event from JS to C#, iOS uses a custom NSUrlProtocol implementation which listens for XHR traffic on a custom protocol (app://).To send event from JS to C#, an XHR request is made to custom protocol with event data in the url.

    function (url) { var x1 = new bridge.app.CustomXhr(); x1.onerror = function (e) { console.log('XHR error:' + JSON.stringify(e)); }; x1.open('GET', url); x1.send();}

    iOS C# BridgeRegisters global NSUrlProtocol handler, takes an instance of UIWebView and bind global events to specific UIWebViews.To send event from C# to JS, executes dynamically generated JS which calls into jsbridge.js component.

    public override void ExecuteScript(string javascript){ webView.BeginInvokeOnMainThread(() => _webView.EvaluateJavascript(javascript));}

    iOS JavaScript Bridge Demo

    Android jsbridge.jsTo send an event from JS to C#, Android uses a Java object which is made accessible as a JavaScript object by adding the object instance to the Webview as a JavaScript Interface.To send event from JS to C#, a method on the Java object instance is invoked from JavaScript.

    function (url){ var result = window.AndrApp.Event(url); result = JSON.parse(result);

    if (!result.Success) { console.log('Android bridge error: ' + JSON.stringify(result)); }}

    Android C# bridgeInstantiates and adds java object with [Export] or [JavascriptInterface] members. Members are accessible from javascript.

    webView.AddJavascriptInterface(new JsBridgeExports(this), "AndrApp");

    To execute a script, it loads a url which contains javascript to be executed:

    public override void ExecuteScript(string javascript){ activity.RunOnUiThread(() => _webView.LoadUrl(string.Format("javascript: {0}", javascript)));}

    WinForms jsbridge.jsTo send an event from JS to C#, WinForms uses a C# object which is made accessible as a Javascript object by adding the object instance to the WebView as the ObjectForScripting.To send event from JS to C#, a method on the C# object instance is invoked from javascript.

    function (url) { var result = window.external.HandleEvent(url); result = JSON.parse(result);

    if (!result.Success) { console.log('PRM bridge error: ' + JSON.stringify(result)); }}

    WinForms C# bridgeInstantiates and adds C# object to the WebBrowser instance as the ObjectForScripting. ObjectForScripting must be have[ComVisible(true)]

    this._browser.ObjectForScripting = new JsBridgeWindowExternalHandler(this);

    To fire event from C# to JS, eval function is invoked from C# with a IIFE (Immediately Invoked Function Expression)

    public override void ExecuteScript(string javascript){ _browser.Invoke(new Action(() => { if (_browser.Document != null) { var script = string.Format("(function() {{ {0}; }})();", javascript);

    _browser.Document.InvokeScript("eval", new object[] { script }); } }));}

    JS Code to fire and listen for eventsangular.module('amodule').controller('SomeCtrl', ['$scope', 'Bridge', function ($scope, Bridge){ //fire event Bridge.navigate('aRoute', { id : 0 });

    //listen for event Bridge.on('someEvent', function (data) { $scope.data = data; }); }]);

    C# code to fire and listen for events//Fire Eventthis.BridgeContext.FireEvent("navigate", new { id = 0});

    //listen to eventthis.BridgeContext.AddEventListener("someEvent", (SomeType data) => { //do something with data });

    NotesBridge lifetimes vary by platform. iOS has a singleton bridge because NSProtocolHandler is added for an entire application instead of for a specific UIWebView. iOS bridge has logic to broker events to the correct bridge context which isassociated with a specific UIWebView.Android bridges are instantiated per WebView instancesWinForms bridges are instantiated per WebBrowser instanceNative components must tell JS bridge which native implementation is used. This can happen AFTER events have already been fired from JS. This required queuing of events until the bridge was finished being setup.

    NotesMajority of code is in a PCL library and reused by all platformsDll exists for each platform that contains platform specific codeVery little platform specific codeBridge behavior consistent across the platforms

    Frontend Tech StackXamarinAngularBootstraplodashLessHammer.JS

    Testing

    http://www.idyllic-software.com/blog/category/ruby-on-rails/page/5/

    Web UI is Easy to Test and Debug

    Device Testing with Xamarin Test Cloud

    Xamarin Test Cloud Testing[Test]public void PinchToZoom(){ LoginPage.Login(app);

    app.WaitForElement(x => x.Css("#dashboard")); app.Screenshot("Then I am logged in at my home screen");

    app.GoToPatients();

    QuicksearchPage.SearchForPatient(app, "Anderson"); QuicksearchPage.SelectPatient(app, "e2ab0790-f271-471a-bdc2-e6bca0889dad");

    app.WaitForElement(x => x.Css("#patient-widgets"), "Timed out waiting for patient dashboard to load", new TimeSpan(0, 0, 3)); app.Screenshot("Then I see Jeff's profile");

    PatientDashboardPage.ExpandWidget(app, PatientDashboardWidget.ObservationResults); PatientDashboardPage.TapObservationResult(app, "3f39a0fa-99e9-494b-a234-170f3ff824ba");

    Thread.Sleep(5000); app.Screenshot("Now I should see the images"); //Scroll down to view first image app.ScrollDownEnough(x => x.WebView().Css(".image-viewer"));

    var rect = app.Query(x => x.WebView().Css(".image-viewer"))[0].Rect; app.Zoom(rect.CenterX, rect.CenterY, 100); app.Screenshot("Now I zoom in on the image");}

    Not All Web Browsers are Equal

    iOS and Android Webview Update Differences

    Inertial Scroll or EasyScrollerSafari: Fixed DIVs Badhttps://github.com/zynga/scroller

    Xamarin IssuesConstant updatesThings breakUniversal API kerfuffleIDE lockupsTheyre still awesome and smart

    http://www.doomsteaddiner.net/blog/wp-content/uploads/2013/04/wheels-off-hummer.png

    BackendWeb APIOWINAzure RelaySQL ServerFeature List for VersionsNEO (home brew ORM)

    On-Premise Server

    Azure Relay WebHttpBinding

    A Developers Guide to Service Bus in Windows Azure Platform

    Service Bus Relay Web API Host

    https://pfelix.wordpress.com/tag/asp-net-web-api/

    Relay DemoBy pass on-premise issues and pesky IT road blocks

    http://imgs.xkcd.com/comics/security.png

    Azure Relay Performance

    Code MetricsBackend 50K lines87% C#10% Build scripts3% OtherFrontend 100K lines59% JavaScript30% HTML (HTML, CSS, Less)8% C# (90% in common PCL)3% Build scripts

    It Works

    Thanks to a Great TeamMobile TeamRyan CadyKenneth CrawfordMike DuranJeff LottContributorsDoug JostChris MojicaKarl ShearerDesignMore Simple

    Top Ten Rules of Software Development Order the T-shirts for the Development teamAnnounce availabilityWrite the codeWrite the manualHire a Product ManagerSpec the software (writing the specs after the code helps to ensure that the software meets the specifications)ShipTest (the customers are a big help here)Identify bugs as potential enhancementsAnnounce the upgrade programhttp://www.nullskull.com/a/722/the-top-ten-rules-of-software-development.aspx

    Linkshttp://xamarin.com/https://angularjs.org/https://github.com/crdeutsch/MonoTouch-JsBridgehttp://zynga.github.io/scroller/https://pfelix.wordpress.com/tag/asp-net-web-api/Designing Evolvable Web APIs with ASP.NEThttps://github.com/pmhsfelix/WebApi.Explorations.ServiceBusRelayHostwww.moresimple.com