What the heck are CocoaPods?
Is Android Arsenal an Area 51 Project?
...Like npm for native iOS and Android code.
Absolute
Dock
Grid
Stack
Wrap
<Page xmlns="http://schemas.nativescript.org/tns.xsd">
<StackLayout class="p-20">
<Label text="Tap the button" class="h1 text-center"/>
<Button text="TAP" tap="{{ onTap }}" class="btn btn-primary"/>
</StackLayout>
</Page>
// DOM comparison...
<html>
<body>
<div class="p-20">
<h1 class="h1 text-center">Tap the button</h1>
<button type="button" class="btn btn-primary"
(tap)="onTap()">TAP</button>
</div>
</body>
</html>
var time = new android.text.format.Time();
time.set( 26, 3, 2016 );
console.log( time.format( "%D" ) );
"04/26/16"
Runs on V8 JavaScript VM
var alert = new UIAlertView();
alert.message = "Hello world!";
alert.addButtonWithTitle( "OK" );
alert.show();
Runs on JavaScriptCore VM
// CommonJS
var fileSystem = require( "file-system" );
var file = new fileSystem.File( path );
// ES6
import { File } from 'file-system';
let file = new File( path );
let file = new java.io.File( path );
let fileManager = NSFileManager.defaultManager();
fileManager.createFileAtPathContentsAttributes( path );
// CommonJS
var http = require( "http" );
// ES6
import * as http from 'http';
http.getJSON( "https://api.myservice.com" )
.then((result) => {
// result is JSON Object
});
import image = require("image-source");
import httpRequest = require("http/http-request");
import dts = require("http");
global.moduleMerge(httpRequest, exports);
export function getString(arg: any): Promise<string> {
return new Promise<string>((resolve, reject) => {
httpRequest.request(typeof arg === "string" ? { url: arg, method: "GET" } : arg)
.then(r => {
try {
var str = r.content.toString();
resolve(str);
} catch (e) {
reject(e);
}
}, e => reject(e));
});
}
export function getJSON<T>(arg: any): Promise<T> {
return new Promise<T>((resolve, reject) => {
httpRequest.request(typeof arg === "string" ? { url: arg, method: "GET" } : arg)
.then(r => {
try {
var json = r.content.toJSON();
resolve(json);
} catch (e) {
reject(e);
}
}, e => reject(e));
});
}
export function getImage(arg: any): Promise<image.ImageSource> {
return new Promise<image.ImageSource>((resolve, reject) => {
httpRequest.request(typeof arg === "string" ? { url: arg, method: "GET" } : arg)
.then(r => {
r.content.toImage().then(source => resolve(source), e => reject(e));
}, e => reject(e));
});
}
export function getFile(arg: any, destinationFilePath?: string): Promise<any> {
return new Promise<any>((resolve, reject) => {
httpRequest.request(typeof arg === "string" ? { url: arg, method: "GET" } : arg)
.then(r => {
try {
var file = r.content.toFile(destinationFilePath);
resolve(file);
} catch (e) {
reject(e);
}
}, e => reject(e));
});
}
export function addHeader(headers: dts.Headers, key: string, value: string): void{
if(!headers[key]) {
headers[key] = value;
} else if (Array.isArray(headers[key])){
(<string[]>headers[key]).push(value);
} else {
let values: string[] = [<string>headers[key]];
values.push(value);
headers[key] = values;
}
}
/**
* Android specific http request implementation.
*/
import types = require("utils/types");
import * as utilsModule from "utils/utils";
import * as imageSourceModule from "image-source";
import * as platformModule from "platform";
import * as fsModule from "file-system";
// this is imported for definition purposes only
import http = require("http");
var requestIdCounter = 0;
var pendingRequests = {};
var utils: typeof utilsModule;
function ensureUtils() {
if (!utils) {
utils = require("utils/utils");
}
}
var imageSource: typeof imageSourceModule;
function ensureImageSource() {
if (!imageSource) {
imageSource = require("image-source");
}
}
var platform: typeof platformModule;
function ensurePlatform() {
if (!platform) {
platform = require("platform");
}
}
var completeCallback: com.tns.Async.CompleteCallback;
function ensureCompleteCallback() {
if (completeCallback) {
return;
}
completeCallback = new com.tns.Async.CompleteCallback({
onComplete: function (result: any, context: any) {
// as a context we will receive the id of the request
onRequestComplete(context, result);
}
});
}
function onRequestComplete(requestId: number, result: com.tns.Async.Http.RequestResult) {
var callbacks = pendingRequests[requestId];
delete pendingRequests[requestId];
if (result.error) {
callbacks.rejectCallback(new Error(result.error.toString()));
return;
}
// read the headers
var headers: http.Headers = {};
if (result.headers) {
var jHeaders = result.headers;
var length = jHeaders.size();
var i;
var pair: com.tns.Async.Http.KeyValuePair;
for (i = 0; i < length; i++) {
pair = jHeaders.get(i);
(<any>http).addHeader(headers, pair.key, pair.value);
}
}
callbacks.resolveCallback({
content: {
raw: result.raw,
toString: () => {
if (types.isString(result.responseAsString)) {
return result.responseAsString;
} else {
throw new Error("Response content may not be converted to string");
}
},
toJSON: () => {
ensureUtils();
return utils.parseJSON(result.responseAsString);
},
toImage: () => {
ensureImageSource();
return new Promise<any>((resolveImage, rejectImage) => {
if (result.responseAsImage != null) {
resolveImage(imageSource.fromNativeSource(result.responseAsImage));
}
else {
rejectImage(new Error("Response content may not be converted to an Image"));
}
});
},
toFile: (destinationFilePath?: string) => {
var fs: typeof fsModule = require("file-system");
var fileName = callbacks.url;
if (!destinationFilePath) {
destinationFilePath = fs.path.join(fs.knownFolders.documents().path, fileName.substring(fileName.lastIndexOf('/') + 1));
}
var stream: java.io.FileOutputStream;
try {
var javaFile = new java.io.File(destinationFilePath);
stream = new java.io.FileOutputStream(javaFile);
stream.write(result.raw.toByteArray());
return fs.File.fromPath(destinationFilePath);
}
catch (exception) {
throw new Error(`Cannot save file with path: ${destinationFilePath}.`);
}
finally {
if (stream) {
stream.close();
}
}
}
},
statusCode: result.statusCode,
headers: headers
});
}
function buildJavaOptions(options: http.HttpRequestOptions) {
if (!types.isString(options.url)) {
throw new Error("Http request must provide a valid url.");
}
var javaOptions = new com.tns.Async.Http.RequestOptions();
javaOptions.url = options.url;
if (types.isString(options.method)) {
javaOptions.method = options.method;
}
if (types.isString(options.content) || options.content instanceof FormData) {
javaOptions.content = options.content.toString();
}
if (types.isNumber(options.timeout)) {
javaOptions.timeout = options.timeout;
}
if (options.headers) {
var arrayList = new java.util.ArrayList<com.tns.Async.Http.KeyValuePair>();
var pair = com.tns.Async.Http.KeyValuePair;
for (var key in options.headers) {
arrayList.add(new pair(key, options.headers[key] + ""));
}
javaOptions.headers = arrayList;
}
ensurePlatform();
// pass the maximum available image size to the request options in case we need a bitmap conversion
var screen = platform.screen.mainScreen;
javaOptions.screenWidth = screen.widthPixels;
javaOptions.screenHeight = screen.heightPixels;
return javaOptions;
}
export function request(options: http.HttpRequestOptions): Promise<http.HttpResponse> {
if (!types.isDefined(options)) {
// TODO: Shouldn't we throw an error here - defensive programming
return;
}
return new Promise<http.HttpResponse>((resolve, reject) => {
try {
// initialize the options
var javaOptions = buildJavaOptions(options);
// remember the callbacks so that we can use them when the CompleteCallback is called
var callbacks = {
url: options.url,
resolveCallback: resolve,
rejectCallback: reject
};
pendingRequests[requestIdCounter] = callbacks;
ensureCompleteCallback();
//make the actual async call
com.tns.Async.Http.MakeRequest(javaOptions, completeCallback, new java.lang.Integer(requestIdCounter));
// increment the id counter
requestIdCounter++;
} catch (ex) {
reject(ex);
}
});
}
/**
* iOS specific http request implementation.
*/
import http = require("http");
import * as types from "utils/types";
import * as imageSourceModule from "image-source";
import * as utilsModule from "utils/utils";
import * as fsModule from "file-system";
import domainDebugger = require("./../debugger/debugger");
var GET = "GET";
var USER_AGENT_HEADER = "User-Agent";
var USER_AGENT = "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25";
var sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration();
var queue = NSOperationQueue.mainQueue();
var session = NSURLSession.sessionWithConfigurationDelegateDelegateQueue(sessionConfig, null, queue);
var utils: typeof utilsModule;
function ensureUtils() {
if (!utils) {
utils = require("utils/utils");
}
}
var imageSource: typeof imageSourceModule;
function ensureImageSource() {
if (!imageSource) {
imageSource = require("image-source");
}
}
export function request(options: http.HttpRequestOptions): Promise<http.HttpResponse> {
return new Promise<http.HttpResponse>((resolve, reject) => {
try {
var debugRequest = domainDebugger.network && domainDebugger.network.create();
var urlRequest = NSMutableURLRequest.requestWithURL(
NSURL.URLWithString(options.url));
urlRequest.HTTPMethod = types.isDefined(options.method) ? options.method : GET;
urlRequest.setValueForHTTPHeaderField(USER_AGENT, USER_AGENT_HEADER);
if (options.headers) {
for (var header in options.headers) {
urlRequest.setValueForHTTPHeaderField(options.headers[header] + "", header);
}
}
if (types.isString(options.content) || options.content instanceof FormData) {
urlRequest.HTTPBody = NSString.alloc().initWithString(options.content.toString()).dataUsingEncoding(4);
}
if (types.isNumber(options.timeout)) {
urlRequest.timeoutInterval = options.timeout / 1000;
}
var dataTask = session.dataTaskWithRequestCompletionHandler(urlRequest,
function (data: NSData, response: NSHTTPURLResponse, error: NSError) {
if (error) {
reject(new Error(error.localizedDescription));
} else {
var headers: http.Headers = {};
if (response && response.allHeaderFields) {
var headerFields = response.allHeaderFields;
headerFields.enumerateKeysAndObjectsUsingBlock((key, value, stop) => {
(<any>http).addHeader(headers, key, value);
});
}
if (debugRequest) {
debugRequest.mimeType = response.MIMEType;
debugRequest.data = data;
var debugResponse = {
url: options.url,
status: response.statusCode,
statusText: NSHTTPURLResponse.localizedStringForStatusCode(response.statusCode),
headers: headers,
mimeType: response.MIMEType,
fromDiskCache: false
}
debugRequest.responseReceived(debugResponse);
debugRequest.loadingFinished();
}
resolve({
content: {
raw: data,
toString: () => { return NSDataToString(data); },
toJSON: () => {
ensureUtils();
return utils.parseJSON(NSDataToString(data));
},
toImage: () => {
ensureImageSource();
if (UIImage.imageWithData["async"]) {
return UIImage.imageWithData["async"](UIImage, [data])
.then(image => {
if (!image) {
throw new Error("Response content may not be converted to an Image");
}
var source = new imageSource.ImageSource();
source.setNativeSource(image);
return source;
});
}
return new Promise<any>((resolveImage, rejectImage) => {
var img = imageSource.fromData(data);
if (img instanceof imageSource.ImageSource) {
resolveImage(img);
} else {
rejectImage(new Error("Response content may not be converted to an Image"));
}
});
},
toFile: (destinationFilePath?: string) => {
var fs: typeof fsModule = require("file-system");
var fileName = options.url;
if (!destinationFilePath) {
destinationFilePath = fs.path.join(fs.knownFolders.documents().path, fileName.substring(fileName.lastIndexOf('/') + 1));
}
if (data instanceof NSData) {
data.writeToFileAtomically(destinationFilePath, true);
return fs.File.fromPath(destinationFilePath);
} else {
reject(new Error(`Cannot save file with path: ${destinationFilePath}.`));
}
}
},
statusCode: response.statusCode,
headers: headers
});
}
});
if(options.url && debugRequest) {
var request = {
url: options.url,
method: "GET",
headers: options.headers
};
debugRequest.requestWillBeSent(request);
}
dataTask.resume();
} catch (ex) {
reject(ex);
}
});
}
function NSDataToString(data: any): string {
return NSString.alloc().initWithDataEncoding(data, 4).toString();
}
ActionBar {
background-color: #000;
color:white;
}
.logo-text {
color:purple;
horizontal-align:center;
font-size:30;
}
.fa {
font-family: FontAwesome;
font-size:80;
horizontal-align:center;
}
.instruction {
font-size:35;
font-style: italic;
horizontal-align:center;
color:#555;
margin:8;
}
<Page xmlns="http://schemas.nativescript.org/tns.xsd">
<!--navigatingTo="navigatingTo"-->
<Page.actionBar>
<ActionBar title="CSS Styling"/>
</Page.actionBar>
<StackLayout padding="10">
<Label text="Logo text" margin="10" class="logo-text"/>
<Label text="\uf09b" margin="10" class="fa"/>
<Label text="Instruction text" margin="10" class="instruction"/>
</StackLayout>
</Page>
Typescript
Debugging abilities is close to none
Saved me from runtime mistakes
Complicated API (native)
Hard to debug
debug mode doesn't support livesync
no inspect element
very long reload time
the console logs are not colored
Slow unit tests makes TDD difficult
Performance is feature
Webpack
Lazy loading
aot
Very long reload time
Develop your api/bussines logic in a normal angular 2 project and port it to nativescript