NativeScript: Cross-Platform Mobile Apps with JavaScript and Angular

Post on 16-Apr-2017

774 views 3 download

Transcript of NativeScript: Cross-Platform Mobile Apps with JavaScript and Angular

NativeScriptCross-platform mobile apps with JavaScript

Todd Anglin@toddanglin

Burke Holland@burkeholland

Workshop Agenda

• Intro to NativeScript• NativeScript core concepts• “Hello World”• Defining Views• Debugging JavaScript• Basic data binding• Styling views with CSS

• Intro to Angular 2 concepts• Angular 2 + NativeScript• Navigating views• BONUS

• Extending with plugins• Adding animations• Using Telerik Platform

Ante Meridiem (9AM to Noon) Post Meridiem (1PM to 4PM)

Intro to NativeScript”How did we get here?”

2013 2014 2015 2016 2017

Early prototypes

“Core” engineering

Public launch

Adoption ramp-up

Mass adoption

Project Timeline

Delivering on the overdue promise of

“hybrid.”

Swift/Obj-C Java .NET

Mobile WebHybridNative

("1st Gen" x-plat native)

We ❤Web.But…

We need:• Better offline support• Access to device APIs• Home screen icons• Push notifications• App monetization• …

Hybrid Promise

100% Web

100% NativeHybrid

ReachCode/Skill Reuse

RichnessPremium experience

Device APIs

Best of both?

Hybrid Reality

80% coded 20%remaining

80% of dev time

Hybrid Reality

80% coded 20%remaining

80% of dev time 80% of dev time

Hybrid Reality

Hybrid "Peak"

Hybrid Promise

100% Web

100% NativeHybrid

ReachCode/Skill Reuse

RichnessPremium experience

Device APIs

Compromises

Binary Choice

NativeHybrid

😎 Best experience🐢 One platform at a time

🔥 Fast to market😭 Compromise on UX

Binary Choice

NativeHybrid

🔥 Fast to market😎 Best experience

JavaScript-driven native

🔥 Fast to market😎 Best experience

"Hybrid"Web UI with limited access to

native APIsNative App Shell

WebViewPlugins Plugins

Entire app lives here

Native App

"JavaScript-driven Native"Native UI driven by JavaScript

Native UI

JavaScript-to-Native bridge

JavaScript Engine(Your app code runs here)

Native APIs

NativeScript(by Telerik)

React Native(by Facebook)

“JavaScript-drive Native”

Native App

Native UI

JavaScript-to-Native runtime

JavaScript Engine(Your app code runs here)

Native APIs

Native App

Native UI

JavaScript Engine(Your app code runs here)

Native APIsAPI

WrapperAPI

Wrapper

PluginsPlugins

API Wrapper

API Wrapper

API Wrapper

API Wrapper

Plugins created with JS/TypeScript Plugins created with native code

“JavaScript-driven Native”

• Share code• Reuse existing skills/teams• Reuse existing libraries

• Native UI (no WebView!)• Full access to device APIs• Immediate access to new OS

features

🔥 Fast to market😎 Best experience

What is NativeScript?

• Open source framework (ASLv2)• Create native mobile apps for iOS, Android

(and eventually Windows 10)• Use JavaScript (“web skills”)• Write once, run everywhere

• Share 100% code between iOS/Android• Share 80% code with web

• Reuse popular plugins from NodeJS/iOS/Android

• Integrates deeply with Angular and TypeScript

With NativeScript…

• No web views (platform native UI)• Use JavaScript (or TypeScript)• 100% access to ALL native APIs (even new APIs)• Made for “web developers” (JS, CSS, XML)• Use Angular for web AND native mobile• Reuse thousands of libraries from

Node/iOS/Android/Web

The {N} difference…

Demo available in the app stores in May

Rich, animated, “no compromise” native UI(with shared UI code)

1

Measurable native UI performance(“no jank”)

2

Maximum code and skill reusability

3

Video credit: Nathan Walker, {N} community member

How does NativeScript work?Under the covers

Generated at build time for OS & 3rd party

native libraries

NativeScript Android example

output:

JavaScript

NativeScript iOS example

JavaScript

Runs on V8 JavaScript VM

Runs on JavaScriptCore VM

NativeScript Module Layer (NML)

• Abstractions on native APIs that provide unified, cross-platform API

• Dozens available out of the box• Easy for developers to add• IMPORTANT: All native APIs still available at JavaScript layer for platform-specific

scenarios

• NativeScript modules follow Node module’s conventions (CommonJS).

Example: NativeScript file module

Putting it all together

Style with CSSDefine UI with XML Logic with JavaScript

“Hello World”

Two ways to use NativeScript

1)

2)

Command Line Interface (CLI)

• Use Command Prompt (Win) or Terminal (Mac, Linux)

• Free, Part of open source project

• Requires installation, local environment setup to build for iOS/Android (requires Mac for iOS)

• Integrates with Visual Studio Code (via plugin)

WHY: More control, Free, Integrate with existing IDEs/code editors

WHO: More technical developers used to using CLI, Open source developers

Telerik Platform

• Use AppBuilder or Screen Builder• Subscription required

• Build in the cloud (no local install required)

• Easiest way to get started

WHY: Richer tooling, Easier setup, Platform integrated

WHO: Less technical developers, Prefer Platform integrations, Windows developers targeting iOS

Telerik PlatformPowerful visual tooling, cloud builds and

more for NativeScript app developers.

NativeScript CLI requirements

https://github.com/nativescript/nativescript-cli#system-requirements

Xcode, Xcode CLI tools, iOS SDK

JDK, Apache Ant, Android SDK

$ tns doctor

Choice in Architecture

JavaScriptWrite your application using plain JavaScript

TypeScriptUse TypeScript to get

Object Oriented features and compile time error

checking

AngularUse Angular to architect

application. Reuse almost all code between web and

mobile

Angular & TypeScript

• Created by Google• Open source• Popular JavaScript framework

for building complex web apps• “Angular 2” ships in May

• Created by Microsoft• Open source• Popular compiler for JavaScript that

adds powerful language features• Used to create Angular 2 and {N}

Starting a new project

{demo}

Running on iOS

or$ tns emulate ios

Running on Android

or$ tns emulate android

A better Android emulator

Visual Studio Android Emulator

Genymotion

NativeScript LiveSync$ tns livesync //For all connected devices/emulators

$ tns livesync android //Target specific platform

$ tns livesync ios --emulator

$ tns livesync ios --emulator --watch

• Refresh app with latest changes to JS, CSS, XML

• No re-build• Works with

emulators AND device

app.js

Defining Views

Pages

• XML markup structure• Elements (e.g. <Page>, <Label>) are

NativeScript modules

LayoutsUI Elements

Layouts

Absolute Dock Grid Stack Wrap

UI Widgets

• Button• Label• TextField• TextView• SearchBar• Switch• Slider• Progress• ActivityIndicator• Image

• ListView• HtmlView• WebView• TabView• SegmentedBar• DatePicker• TimePicker• ListPicker• Dialogs

Out-of-the-boxNative UI widgetsmeans…• Native behavior• Native perf• Native accessibility• Parity with “native”

Native UI widgets

Native API

{N} Module button

android.widget.Button UIButton

Label

TextField

Repeater

SegmentedBar

GridLayout

GridLayout

Label TextField

StackLayout

{demo}

Targeting Views• Target based on:

• screens size• minWH<X>, minW<X>, minH<X>

• platform• ios, android, windows

• Orientation• land, port

<file-name>[.<qualifier>]*.<extension>

styles.android.cssstyles.ios.css

mypage.minWH600.xmlmypage.xml

TIP• Platform specific capabilities are always available

• JavaScript: <view>.android or <view>.ios• Markup: <android></android> or <ios></ios>• Attributes: android:<attribute> or ios:<attribute>

• Ex: <label android:class="…" ios:class="…" />

Write once by default.Target specific platform capabilities when needed.

{demo}

Handling EventsXML<button text="Click Me" tap="{{ onTap }}" ...

JS (ViewModel)function pageViewModel() {

var viewModel;viewModel.onTap = function() {

alert("You tapped me!");} return viewModel;

}

exports.pageViewModel = pageViewModel;

JS (View)function onViewLoad(args) {

var page = args.object; page.bindingContext = pageViewModel();

}

• Create event handlers in JavaScript

• Bind to event names

Handling Eventstaplabel.on(gestures.GestureTypes.tap, function (args) {

console.log("Tap");});

swipelabel.on(gestures.GestureTypes.swipe, function (args) {

console.log("Swipe Direction: " + args.direction); });

Multiple eventslabel.on("tap, doubleTap, longPress", function (args) {

console.log("Event: " + args.eventName);});

• Tap• Double Tap• Long Press• Swipe• Pan• Pinch• Rotation• Touch

{demo}

Debugging

“If debugging is the process of removing software bugs, then programming must be the process of putting them in.”

- Edsger Dijkstra

Debugging Strategies• Debug by alert (no really)• Debug by console.log• Debug by Developer Tools• Debug by IDE

• Visual Studio• Visual Studio Code

Debug By Alert

{demo}

Telerik UI for NativeScript

- Open source- Widgets: ListView,

SlideDrawer- No support

UI for {N}

- Widgets: Chart, Calendar, DataForm

- No support in entry pricing

- UI for {N} can buy this version for support

UI for {N} PROFREE $199/$599 (with support)

Advanced, rich, native UI widgets for iOS and Android

Telerik UI for NativeScript

npm install nativescript-telerik-ui --save

• Add powerful ListView, SlideDrawer to apps

• Free native widgets

• Support available with PRO license

Custom XML Components<page xmlns:custom="modules/mymodule">

<custom:MyCustomControl /></page>

• Encapsulate reusable UI in components• JS only, OR• XML + CSS + JS

• Add to pages with xmlns

modulesmymodule

.XML

MyCustomControl.js

MyCustomControl.css

MyCustomControl.xml

{demo}

Navigating Views

Basics

• Topmost frame is root-level container

• Facilitates navigation between views

"topmost frame"

var frameModule = require("ui/frame");var topmost = frameModule.topmost();

Navigating with "topmost"1. By File Nametopmost.navigate("details-page");

2. With Navigation Entryvar navigationEntry = {

moduleName: "details-page",context: {info: "something"},animated: false

};topmost.navigate(navigationEntry);

3. Dynamically with Functionvar factoryFunc = function () {

var page = new pagesModule.Page();page.content = ...return page;

};topmost.navigate(factoryFunc);

• Always navigating with topmost.navigate()

• Pass context to views and parse in onNavigatedTo event

• Go back with:• topmost.goBack();

View transitionsvar navigationEntry = {

moduleName: "main-page",animated: true,transition: {

name: "slide",duration: 380,curve: "easeIn" }

};topmost.navigate(navigationEntry);

• curl (same as curlUp) (iOS only)• curlUp (iOS only)• curlDown (iOS only)• explode (Android Lollipop and later)• fade• flip (same as flipRight)• flipRight• flipLeft• slide (same as slideLeft)• slideLeft• slideRight• slideTop• slideBottom

Different transitions can be set for iOS and Android

{demo}

Basic Data Binding

Data binding

{{ newTodo }}

{demo}

Data binding improved

Data binding lists<Page>

<StackLayout><ListView items="{{ items }}" height="200">

<ListView.itemTemplate><Label text="{{ $value }}" />

</ListView.itemTemplate></ListView>

</StackLayout></Page>

• Bind to array or collection of data

• Access named properties or $value

• Access parent binding context with $parents• EX: OrderID in list of Order Details

{{ todos }}

{{ $value }}

{demo}

Data binding expressions

Static Expression<TextField text="{{ sourceProperty, sourceProperty + ' some static text' }}" />

Ternary Operator<TextField class="{{ isConditionTrue ? 'myClass true' : 'myClass false' }}" />

Converter<TextField text="{{ testDate, testDate | dateConverter('DD.MM.YYYY') }}" />

Execute expressions during data binding to determine visual state

Source Property Binding Expression

Convertersvar dateConverter = {

toView: function (value, format) {var result = format;var day = value.getDate();...//Return formatted model valuereturn result;},toModel: function (value, format) {

//Convert value from UI format to model formatreturn result;

}}

• Encapsulate more complex data binding formatting rules

• Can be one-way or two-way

• Add to page binding context OR global application context

Data binding expressionsFeature Example

property access obj1.obj2.prop1

array access arrayVar[indexVar]

logical operators !var1

unary operators +var1, -var2

binary operators var1 + var2

comparison operators var1 > var2

logical comparison operators var1>1 && var2>1.

ternary operator var1 ? var2 : var3

grouping parenthesis (a + b) * (c + d)

function calls myFunc(var1, var2, ..., varN)

filters expression \| filter1(param1, ...) | filter 2

{{ total, total +' items left' }}

{demo}

Basic Styling with CSS

CSS

Convention:

app.css <-- Global styles

[viewName].css <-- View styles

[viewName].[platform].css

Supported Properties• color• background-color• background-image• background-repeat• background-position• background-size• border-color• border-width• border-radius• font• font-family• font-size

• font-style• font-weight• text-align• text-decoration• text-transform• vertical-align• horizontal-align• margin• margin-top• margin-right• margin-bottom• margin-left

• width• height• min-width• min-height• padding• padding-top• padding-right• padding-bottom• padding-left• visibility• opacity

Supported Selectors

• Type• button { color: red; }

• Class• .mybutton { color: green; }

• ID• #myButton { color: #FFF; }

{demo}

Custom Fonts1. Use TTF or OTF fonts2. Put fonts in: app > fonts3. Use in CSS

.icon { font-size: 30; font-family: 'FontAwesome';

}

{demo}

Sass & LESS• Use Sass or LESS syntax• Auto-compiled

$ tns install sassOR

$tns install less

Pre-lunch Wrap-up

Bottom line• Access all native APIs with JavaScript• 0-day support for new APIs• Use modules to increase cross-platform “write

once”• Use CSS to style native UI• Use XML markup to define views• Use CLI (+ IDE integrations) or Telerik Platform to

build and debug

Lunch Break Resume @ 1PM

Workshop Agenda

• Intro to NativeScript• NativeScript core concepts• “Hello World”• Defining Views• Debugging JavaScript• Basic data binding• Styling views with CSS

• Intro to Angular 2 concepts• Angular 2 + NativeScript• Navigating views• BONUS

• Extending with plugins• Adding animations• Using Telerik Platform

Ante Meridiem (9AM to Noon) Post Meridiem (1PM to 4PM)

Adoption trendsWarm the brains-up after lunch

Adoption Goals (2016)Goal for 2016: Grow NativeScript adoption by 15x

Jan Feb Mar Apr May Jun Jul Aug Sept Oct Nov DecAug Sept Oct Nov Dec

2x

2016

5x

3x

Google Angular2 event (“ngConf”);NativeScript Angular2 push begins

Measured* active developers(via NativeScript CLI)

• Active monthly CLI users doubledAug to Dec ‘15

• Doubled again Dec to Feb ‘16

• On track to double again by end of April

60% increase in new users since January

Developer Comments

Join the growing NativeScript

community on Slack

bit.ly/nativescript-slack

Integrations & Ecosystem

Plugin Ecosystem

Plugin Ecosystem

Reusable libraries IDEs Verified Plugins

Modulus Sachse Construction Day Care This or That!

PocketSmith

“With NativeScript it became apparent quickly that we would no longer have a need for iOS and Android specialists—our Microsoft .NET team became fully functional—fast.”

Allan Kreyer, CIM Mobility Founder

Angular 2 & NativeScript

One framework. Mobile & desktop.

Mobile

ResponsivePWA (Progressive Web Apps)Native Mobile

Why Angular 2?

Template< ... >

Component{ ... }

Metadata

Directive{ ... }

InjectorService A

Service B

RendererTemplate

< ... >

RendererTemplate

< ... >

An Intro to Angular 2

Template< ... >

Component{ ... }

Metadata

Directive{ ... }

InjectorService A

Service B

Template< ... >

Component{ ... }

Metadata

<!DOCTYPE html><html>

<head><base href="/">

</head>

<body ng-app=”app">

<app>Loading...</app>

<script src="bundle.js"></script>

</body>

</html>

index.html

const AppComponent = {template: `<h1>Root Component</h1>

`};

angular.module('app', []).component('app', AppCompon3ent);

app.js

Root Component

const AppComponent = {template: `<h1>Root Component</h1>

`};

angular.module('app', []).component('app', AppComponent);

app.js

const AppComponent = {template: `<h1>Root Component</h1>

`};

angular.module('app', []).component('app', AppComponent);

app.component.ts

import { Component } from ”@angular/core”

const AppComponent = {template: `<h1>Root Component</h1>

`};

angular.module('app', []).component('app', AppComponent);

app.component.ts

import { Component } from ”@angular/core”

@Component({template: `<h1>Root Component</h1>

`});

angular.module('app', []).component('app', AppComponent);

app.component.ts

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<h1>Root Component</h1>

`});

angular.module('app', []).component('app', AppComponent);

app.component.ts

<!DOCTYPE html><html>

<head><base href="/">

</head>

<body ng-app=”app">

<app>Loading...</app>

<script src="bundle.js"></script>

</body>

</html>

index.html

<!DOCTYPE html><html>

<head><base href="/">

</head>

<body>

<app>Loading...</app>

<script src="bundle.js"></script>

</body>

</html>

index.html

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<h1>Root Component</h1>

`});

angular.module('app', []).component('app', AppComponent);

app.component.ts

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<h1>Root Component</h1>

`});

export class AppComponent {}

app.component.ts

Root Component

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<h1>Root Component</h1>

`});

export class AppComponent {}

app.component.ts

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<h1>Root Component</h1>

`});

export class AppComponent {message: 'Root Component'

}

app.component.ts

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<h1>{{ message }}</h1>

`});

export class AppComponent {message: 'Root Component'

}

app.component.ts

Root Component

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<h1>{{ message }}</h1>

`});

export class AppComponent {message: 'Root Component'

}

app.component.ts

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<input value=”{{ message }}”>

`});

export class AppComponent {message: 'Root Component'

}

app.component.ts

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<input [value]=”message”>

`});

export class AppComponent {message: 'Root Component'

}

app.component.ts

[ value ] or {{ value }} = One way binding from class to view

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<input [value]=”message”>

`});

export class AppComponent {message: 'Root Component'

}

app.component.ts

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<input [value]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: 'Root Component’;showMessage() {

alert(this.message); }

}

app.component.ts

Root Component Show Me

The page at http://localhost/3000 says:

Root Component

( event ) = Binds an event to a function

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<input [value]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: 'Root Component’;showMessage() {

alert(this.message); }

}

app.component.ts

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: 'Root Component’;showMessage() {

alert(this.message); }

}

app.component.ts

[(ngModel)] = Two way binding

Services And Injection

export class MessageService {message: string = ”Service Message”

}

message.service.ts

import { Injectable } from ”@angular/core”

export class MessageService {message: string = ”Service Message”

}

message.service.ts

import { Injectable } from ”@angular/core”

@Injectable()export class MessageService {

message: string = ”Service Message”}

message.service.ts

import { Component } from ”@angular/core”

@Component({selector: 'app',template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: 'Root Component’;showMessage() {

alert(this.message); }

} app.component.ts

import { Component } from ”@angular/core”import { MessageService } from ”./message.service”

@Component({selector: 'app',template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: 'Root Component’;showMessage() {

alert(this.message); }

} app.component.ts

@Component({selector: 'app',providers: [MessageService]template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: string;showMessage() {

alert(this.message); }constructor(MessageService _messageService) {this.message = _messageService.message;

} app.component.ts

Service Message Show Me

The page at http://localhost/3000 says:

Service Message

Directives

import { Component } from ”@angular/core”import { MessageService } from ”./message.service.ts”

@Component({selector: 'app',providers: [MessageService]template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: string;showMessage() {

alert(this.message); }constructor(MessageService _messageService) {this.message = _messageService.message;

}} app.component.ts

import { MessageService } from ”./message.service.ts”

export class MessageDirective {}

message.directive.ts

import { MessageService } from ”./message.service.ts”

@Component({selector: ’message',providers: [MessageService]template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class MessageDirective {}

message.directive.ts

import { MessageService } from ”./message.service.ts”

@Component({selector: ’message',providers: [MessageService]template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class MessageDirective {message: string;showMessage() {

alert(this.message); }constructor(MessageService _messageService) {this.message = _messageService.message;

} }

message.directive.ts

import { Component } from ”@angular/core”import { MessageService } from ”./message.service.ts”

@Component({selector: 'app',providers: [MessageService]template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: string;showMessage() {

alert(this.message); }constructor(MessageService _messageService) {this.message = _messageService.message;

}} app.component.ts

import { Component } from ”@angular/core”import { MessageDirective } from ”./message.directive.ts”

@Component({selector: 'app',providers: [MessageService]template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: string;showMessage() {

alert(this.message); }constructor(MessageService _messageService) {this.message = _messageService.message;

}} app.component.ts

import { Component } from ”@angular/core”import { MessageDirective } from ”./message.directive.ts”

@Component({selector: 'app',directives: [MessageDirective]template: `<input [(ngModel)]=”message”><button (click)=“showMessage()”>Show Message</button>

`});

export class AppComponent {message: string;showMessage() {

alert(this.message); }constructor(MessageService _messageService) {this.message = _messageService.message;

}} app.component.ts

import { Component } from ”@angular/core”import { MessageDirective } from ”./message.directive.ts”

@Component({selector: 'app',directives: [MessageDirective]template: `<message></message>

`});

export class AppComponent {message: string;showMessage() {

alert(this.message); }constructor(MessageService _messageService) {this.message = _messageService.message;

}}

app.component.ts

import { Component } from ”@angular/core”import { MessageDirective } from ”./message.directive.ts”

@Component({selector: 'app',directives: [MessageDirective]template: `<message></message>

`});

export class AppComponent {}

app.component.ts

Service Message Show Me

The page at http://localhost/3000 says:

Service Message

{demo}Let’s Code

an open source framework for building truly native mobile apps with JavaScript. Use web skills, like Angular and CSS, and get native UI and performance on iOS and Android.

NativeScript is…

NativeScriptdelivers the promises

of “hybrid.”

APENDIX

Adding TypeScript

tns install typescript

{demo}

Common App Patterns• Services

• An object that is responsible for getting and setting data

• Models• Super dumb objects that define the structure of the data

• ViewModels• AKA Controllers. Controls the state of the UI and performs when changes are made to model objects.

Angular 2

Data binding with Angular• {{ }} – still works!

• You can still use {N}'s standard binding…• But you can do it even better.

• [] – Property binding • One way.• Like array notation in JavaScript. One way

• () – Event Binding• When you want to bind to an event.

• Two Way – [(ngModel)]• Almost always used with ngModel

Extra Angular Goodies• Dependency Injection• Routing• Components• Pipes• Services• FAST

Using Telerik PlatformCloud-based tooling for NativeScript

Why?• Cloud based, zero setup• Build for iOS from Windows• Deploy to device without provisioning profiles• Manage app properties faster

AppBuilder

Screen Builder

Companion App

{demo}

Animations

Animation basics

Animate:• opacity• backgroundColor• translateX and translateY• scaleX and scaleY• rotate

Configure:• target (UI element)• duration (in ms)• delay• iterations• curve

Simple animationvar view = page.getViewById("myLabel");

view.animate({translate: { x: 0, y: 100},duration: 1000,curve: enums.AnimationCurve.easeIn

});

• Animations can be chained together

• Multiple properties and elements can be animated

• Return a promise that can be canceled

{demo}