With @nativescript/capacitor 2.0 comes more maturity with advancements including:
Last year we introduced v1 which brought the ability to take advantage of NativeScript from Capacitor for the very first time and we continue to be inspired by the effective solutions it makes possible.
With the ability to utilize NativeScript plugins from Capacitor, the community has more options to solve development challenges.
Let's take for example a Capacitor Plugin Request for Zip handling found here.
You may come across a plugin which has support for one platform but perhaps not another. In this case, a skilled contributor has provided an Android zip plugin here however it does not currently support iOS.
If you are familiar with Swift and Objective C, you may be able to help contribute to that plugin to add iOS support but what if you're not?
Let's consider you are working on a mobile project under a tight deadline and you need Zip handling support for iOS right now.
By installing @nativescript/capacitor following the docs here you can now use various NativeScript plugins such as:
It supports iOS and Android in addition to even supporting password protected zip/unzip! 👀
What's neat about the ability to use NativeScript plugins in your Capacitor projects is you can use only what you need. You could use capacitor-zip for Android and you could also use @nativescript/zip for iOS.
@nativescript/capacitor gives you an isolated development area, src/nativescript
, alongside your Ionic codebase only active when Capacitor is running. It also isolates NativeScript dependencies for clean architectural control and organization.
cd src/nativescript
npm install @nativescript/zip
src/native-custom.d.ts
to contain the strongly typed utility you want to use from Ionic. This file helps you maintain a strongly typed interface to any custom NativeScript utilties you expose to your Ionic codebase./**
* Define your own custom strongly typed native helpers here.
*/
export interface ZipOptions {
directory: string;
archive?: string;
unzip?: boolean;
}
export interface nativeCustom {
// example provided with every install
openNativeModalView(): void;
/*
* Added for our new zip/unzip abilities!
*/
fileZip(options: ZipOptions): void;
}
Add a new file for our zip features: src/nativescript/examples/zip.ts
Import into src/nativescript/index.ts
(the NativeScript entry point):
/**
* Zip/Unzip handling
*/
import './examples/zip';
src/nativescript/examples/zip.ts
:import { Zip } from '@nativescript/zip';
import { path, knownFolders } from '@nativescript/core';
import { ZipOptions } from '../../native-custom';
native.fileZip = function (options: ZipOptions) {
const directory = path.join(knownFolders.documents().path, options.directory);
const archive = path.join(knownFolders.temp().path, options.archive);
if (options.unzip) {
console.log('Unzipping archive:', archive);
console.log('Into folder:', directory);
Zip.unzip({
directory,
archive,
onProgress: (progress) => {
console.log('unzip progress:', progress);
},
}).then((filePath) => {
console.log('Unzipped files are here:', filePath);
});
} else {
console.log('Zipping directory:', directory);
console.log('Into archive:', archive);
Zip.zip({
directory,
archive,
onProgress: (progress) => {
console.log('zip progress:', progress);
},
}).then((filePath) => {
console.log('Zipped file is here:', filePath);
});
}
};
src/app/explore-container.component.ts
:import { native } from '@nativescript/capacitor';
// ...
export class ExploreContainerComponent {
fileZip(unzip?: boolean) {
const directory = 'assets';
const archive = 'assets.zip';
if (unzip) {
native.fileUnzip({
directory,
archive,
});
} else {
native.fileZip({
directory,
archive,
});
}
}
}
<div id="container">
<p><a (click)="fileZip()">Zip File</a></p>
<p><a (click)="fileZip(true)">Unzip Files</a></p>
</div>
This shows how you can take advantage of utilities within @nativescript/core itself alongside anything you need.
For any NativeScript plugin you use, there can be iOS or Android dependencies associated with them. You can find out by opening any node_modules package to see if a platforms
folder exists. Capacitor also needs to know about these dependencies.
With @nativescript/zip we can see them, for example in VS Code, with the following:
code src/nativescript/node_modules/@nativescript/zip
You will see contents, in part, as follows:
platforms
android
include.gradle
ios
Podfile
index.android.js
index.d.ts
index.ios.js
The compiled index.{android,ios}.js files are the implementation and the platforms
folder specifies the native dependencies for each applicable platform. Opening platforms/ios/Podfile
reveals:
pod 'SSZipArchive', '~> 2.4.3'
That is the iOS Cocoapod that @nativescript/zip uses.
Open ios/App/Podfile
and let Capacitor's iOS build use it as well:
def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'NativescriptCapacitor', :path => '../../node_modules/@nativescript/capacitor'
end
target 'App' do
capacitor_pods
# Add your Pods here
# NativeScript
pod 'NativeScript'
pod 'NativeScriptUI'
# Zip/Unzip
pod 'SSZipArchive', '~> 2.4.3'
end
We can now prepare our build for Capacitor with:
yarn build:mobile
This will build your web app followed by bundling your src/nativescript
in isolation.
You now have fully operational zip/unzip handling:
ionic cap run ios -l --external
Right now with v2, when using NativeScript plugins with native dependencies (if they include a platforms/ios
or platforms/android
folder), you can add them manually to Capacitor Podfile (for iOS) or gradle (for Android).
In future versions, these will be added automatically.
There are a lot of features inside @nativescript/core and we have only tested a few with Capacitor at this time but feel free to experiment!
This shows using @nativescript/core file APIs to create a file and open it:
import { File, Utils, knownFolders, path } from '@nativescript/core';
native.createAndOpenFile = function (
content: string,
filename = 'new-file.txt'
) {
const newFile = File.fromPath(
path.join(knownFolders.documents().path, 'assets', filename)
);
newFile.writeTextSync(content, (err) => {
if (err) {
console.log('err:', err);
return;
}
});
Utils.openFile(newFile.path, 'File creation from {N} for Capacitor.');
};
When needing to communicate events back and forth between Ionic/Capacitor and your NativeScript utilities you can use:
notifyEvent: (name: string, data?: any) => void
: Notify event name with optional data.onEvent: (name: string, callback: Function) => void
: Listen to event name with callback.removeEvent: (name: string, callback?: Function) => void
: Remove event listeners.For example using the zip example above you can use notifyEvent
in your NativeScript utilities to emit events:
src/nativescript/examples/zip.ts
// ...
import { notifyEvent } from "@nativescript/capacitor/bridge";
native.fileZip = function (options: ZipOptions) {
// ...
Zip.zip({
directory,
archive,
onProgress: (progress) => {
notifyEvent('zipProgress', progress);
},
}).then((filePath) => {
notifyEvent('zipComplete', filePath);
});
}
};
You can then listen to those events in your Ionic components:
src/app/explore-container.component.ts
:import { native } from '@nativescript/capacitor';
// ...
export class ExploreContainerComponent implements OnInit {
ngOnInit() {
native.onEvent('zipComplete', (filepath) => {
console.log('ionic zip complete:', filepath);
});
native.onEvent('zipProgress', (progress) => {
console.log('ionic zip progress:', progress);
});
}
}
Using NativeScript with Capacitor can be further explored from these additional resources:
Presentation from OpenJS World 2021:
https://www.youtube.com/watch?v=OInjFvniccU
https://www.youtube.com/watch?v=AHcL_UbnnPY
You can contribute in a number of helpful ways:
Celebrate the wonders and joys of JavaScript development with us anytime.