Jasonelle

Jasonelle is a nice native wrapper for your Web Application.
Price
The project requires purchasing a license if you want to:
- Use MPLv2 code.
- Execute in real hardware.
With this small contribution you enable us to continue creating awesome tools for your web applications.
By adquiring a License Key you agree with the following:
- Use Jasonelle's technologies and licenses in an ethical way.
- Respect the artifacts license terms and conditions.
- Must not share the license keys with third parties.
- Must not create direct competitors to Jasonelle by rebranding, customizing and reselling the framework and related technologies or services.
Please select the License Key that better fits your project.
Jasonelle Friend's Membership Subscription | Thank you Key |
---|---|
Monthly Payment Requirement | Single Payment |
Jasonelle's Membership Keys can be used in any amount of projects, as long as the subscription is maintained. | This license allows Jasonelle framework to be used by you or one client, in a single commercial end product. |
The license expires if the membership is terminated | Thank you key licenses do not expire. That's why they are a little more expensive than the subscription. |
Join Subscription | Purchase Thank you Key |
Please go to Jasonelle Store to purchase a license and edit Love.h to allow real device execution and MPLv2 licensed code.
If no license is purchased then the code is under AGPLv3. And will not execute using real devices. Although you can activate the full mode if you want to test if Jasonelle is good for your use case (some features like GPS need real hardware for testing).
Please purchase a license key if you want to use Jasonelle's solutions in your project.
Since we cannot enforce this. We rely on your good heart. We trust you are a kind person who will activate this mode ethically.
Community
For help and general chat go to our Telegram group.
The main repository is:
Releases
The latest releases are available at:
Current Version
The current version is v3.
Documentation
The projects documentation is inside the jasonelle.github.io
repository. You can access it online at:
Dual License
Jasonelle Project is dual licensed. You can choose between AGPLv3 or MPLv2. MPLv2 is only valid if the software has a unique Jasonelle Key which was purchased in official channels.
🤩 Credits
Made with ♥ by Ninjas.cl .
iOS Version 3.0.1
This small guide will help you getting started.
Requirements
- XCode 13 or greater
- iOS 14 or greater
Steps
1 - Download the project
Download Bleeding Edge or Stable version. We recommend bleeding edge for the latest features and stable for the more battle tested.
Decompress and open App.xcworkspace
file (White icon).
Find sources/xcode/App/JS/lib/screens/main.js
.
2 - Configure your website
Put your URL for your application. This can be an external url or an HTML file.
Local Files
If you use res://
you can access local html files such as the example index.html
.
You can store local files in the Files
directory.
index.html
Is the example html file with code examples to test different Jasonelle features.
3 - Allowed URLs
By default Jasonelle will open all urls. If you wish to limit this behaviour you can use an allowed
urls property in the configuration file.
Any url that is not present in the allowed list will force open using the native browser.
Check the Configuration File
Example
List the allowed urls. Otherwise it will launch native browser If not present will allow all urls
Put the same URL from main.js here to allow it (just the domain)
allowed: ["file://", "google.cl"]
4 - Done
You can now configure your project as a normal XCode iOS. Change your App Icon and other settings.
Happy Coding!.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
v3.0.1 (next)
Added
-
Ability for extensions to inject Javascript to the WKWebView instance.
-
Ability for the WKWebView instance to load url by using deep links like:
jasonelle://href?=https://google.cl
. -
Added
JLPhotoLibrary
extension that helps requesting access to photo library. -
$keychain
extension:$keychain.set
,$keychain.get
,$keychain.remove
for usage within the WebView. This will enable storing data securely in the iOS's keychain. -
$cookies
extension:$cookies.set
,$cookies.get
,$cookies.remove
for usage within the WebView. This will help with storing cookies in the keychain. Requires$keychain
extension to be enabled. Also you can use$cookies.Cookies
to access js-cookie library. -
$contacts
extension:$contacts.all
for usage within the WebView. -
Ability to have
allowed
list of urls in configuration. Not allowed urls will launch native browser. -
Added LaunchScreen file (Both in SwiftUI and Storyboard file).
-
Added WebView.edgesIgnoringSafeArea(.all). Some websites need this, specially when using a navbar. Thanks to @Mättu in Telegram for pointing this out.
-
Added meta viewport js fix for websites that do not have proper metatag. In webview.js.
-
Added example extension.
-
Added hook triggering for extensions.
-
Added event triggering in webview for extensions.
-
Added Reachability Events Extension.
Fixed
WKWebView
triggeredappdidLoad
event more than once. Now it only triggers the event when loaded.
Downloads
v3.0.0 (September 2022)
This is a new engine created from scratch by Camilo in 2022. (AGPLv3 or MPLv2 Licenses). It was ditched the old json
based approach to a javascript
one. Consists of mainly a WebView engine, because there are lots of competition of "native over the wire" frameworks, and was better to focus on the Web App market such as Bubble users.
- New rewrite of the engine from scratch.
- Will only focus on webview workflow.
- No need for Cocoapods, Carthage or Swift PM.
- Native over the wire workflows delegated to other frameworks like Native Live.
Downloads
Legacy Versions (2016 - 2022)
These versions are not currently supported, but maybe something can be learned or be useful. These were using the old engine created originally by Ethan. (MIT License).
Downloads
Templates
A template is a pre configured App.xcworkspace file that is tailored for a specific use case.
They are meant to be used as an starting point for your application.
You can easily update the core if needed, just by replacing the directories.
Cocoapods Template
If you want to use Cocoapods you can use this starter template.
OneSignal Template
If you want to use OneSignal SDK you can use this starter template.
It contains the pre configured project. But it needs the specific configurations, such as capabilities, groups and other user related settings.
Extensions
Extensions are a great way to add new functionality and helpers to Jasonelle. They can be updated separately from the core and be added or removed at will.
Adding an Extension
Extensions are a single *.xcodeproj (blue icon) that contains the project that can be added to the App.xcworkspace (white icon) file.
Step 1
Open App.xcworkspace with Xcode and look for the *.xcodeproj file in Finder.
Step 2
Draw it to the Extensions directory inside Xcode.
Step 3
Add it to the frameworks in App.xcproj file. Be sure that Embed & Sign option is selected.
Step 4
import the extension in AppExtensions.m.
and add it to the extension registry in install
method
using the class
attribute.
Example: [self.extensions add: JLContacts.class];
Done
If everything is ok then the extension was installed successfully, maybe some other configurations would be needed depending on the extension itself.
Removing an Extension
If you don't need an extension you can remove it from the workspace.
Step 1
Remove (or comment) the setup code in AppExtensions.m
file
described in Adding an Extension: Step 4
.
Step 2
Remove the framework
from the App.xcproj file.
Step 3
Delete the *.xcodeproj
file from the Workspace.
It will pop up a confirmation dialog. We recommend Remove Reference
option.
Create Extensions
Creating extensions requires some knowledge of Objective-C
and JavaScript
. These instructions are for advanced users.
Step 1
Create a new xcodeproj file.
Select Framework
as the template. Be sure that is in the iOS tab.
Add it to the Extensions directory inside the App.xcworkspace.
Step 2
Configure the iOS Deployment Target to 14.0
(or the recommended version for your project).
Add the JLKernel
framework. Select Do Not Embed as the option. Since all the frameworks will be embeded in App.xcproj
later.
Step 3
Create the *.h
and *.m
files that inherits from JLExtension.
Example
MyExtension.h
#import <Foundation/Foundation.h>
//! Project version number for MyExtension.
FOUNDATION_EXPORT double MyExtensionVersionNumber;
//! Project version string for MyExtension.
FOUNDATION_EXPORT const unsigned char MyExtensionVersionString[];
#import <JLKernel/JLKernel.h>
NS_ASSUME_NONNULL_BEGIN
@interface MyExtension : JLExtension
@end
NS_ASSUME_NONNULL_END
MyExtension.m
#import "MyExtension.h"
@implementation MyExtension
@end
Done
Now you can install it as any other extension in your App.xcodeproj file.
Extension Coding
When coding an extension you can access some events and properties that will enable to add new features to Jasonelle.
- (void) install
This method will be executed when installing the extension in AppDelegate
lifecycle. This is before all the extensions are ready,
because a single extension is being installed at the time.
Be sure to call the [super install]
method.
- (void) install {
[super install];
// your code here
}
self.handlers
The handlers are the names of the native bridges that will be called when used inside the webview. Must be configured in the install
method.
See JLKeychain extension as an example with using handlers.
- (void) install {
// ...
self.handlers = @{
@"$keychain.set": setHandler,
@"$keychain.get": getHandler,
@"$keychain.remove": removeHandler,
@"$keychain.clear": clearHandler
};
}
Handlers
A handler is a native function that is called within the webview.
Handlers will be called using $agent.trigger()
inside the webview.
- Example:
$agent.trigger('$keychain.get')
They will return a JS Promise
.
A wrapper can be created to call directly.
- Example: JLContacts.js
(() => {
if (window && window.$myextension) {
return;
}
window.$myextension = {};
window.$myextension.run = () => $agent.trigger("$myextension.run", {});
})();
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions
This method is called in AppDelegate
after the setup and before loading the webview. Normally for post install logic. All the other extensions would be available at this point.
- (nonnull WKWebView *)appDidLoadWithWebView:(nonnull WKWebView *)webView
This method is called when the WKWebView
is ready. You can use it to
inject JavaScript files or make additional configurations to the webview element.
- (nonnull WKWebView *)appDidLoadWithWebView:(nonnull WKWebView *)webView {
[super appDidLoadWithWebView:webView];
return webView;
}
Examples
See JLCookies as an example extension that only loads a JS file.
More methods
See which other methods are available in JLApplicationExtensions
FileSystem Helper
You can access the filesystem by using the app.utils.fs
helper.
Example
Reads a file named JLContacts.js inside the self bundle.
NSString * js = [self.app.utils.fs
readJS:@"JLContacts"
for:self];
// If is named the same as the class then can be used like this
NSString * js = [self.app.utils.fs
readJSFor:self];
WebView Helper
To help injecting scripts to webview you can use app.utils.webview
helper.
webView = [self.app.utils.webview inject:@"js.cookie.min" intoWebView:webView for:self];
// Reads the filename as Classname.js
return [self.app.utils.webview inject:self intoWebView:webView];
Extension Service Locator
You can fetch the extension instance by using the app.ext
service locator.
return (JLATTrackingManager *) [self.app.ext get:JLATTrackingManager.class];
return (JLApplicationBadge *) [self.app.ext get:JLApplicationBadge.class];
JLATTrackingManager
Optionally install ATTracking Support Request user authorization to access app-related data for tracking the user or the device. Ensure to configure the plist as well. If your app calls the App Tracking Transparency API, you must provide custom text, known as a usage-description string, which displays as a system-permission alert request. The usage-description string tells the user why the app is requesting permission to use data for tracking the user or the device.
- Since:
3.0.1
Overview
Request user authorization to access app-related data for tracking the user or the device.
Topics
Installation
The ATTracking message will be triggered on install
.
info.plist
Configure your info.plist
and include the NSUserTrackingUsageDescription
key and fill it with the description.
Properties
status
Returns the current status for the ATTracking.
JLApplicationBadge
Can manipulate Application Icon Badge Number
- Since:
3.0.1
Overview
The number currently set as the badge of the app icon on the Home screen.
Topics
Actions
$badge.clear
: Cleans the badge number icon.$badge.set
: Sets the badge number icon.
Example
$badge.set(3);
$badge.clear();
JLCookies
A helper to work with the WebView
cookies.
- Since:
3.0.1
Overview
Requires $keychain
extension.
Topics
Actions
$cookies.set
: Stores document.cookie in keychain.$cookies.get
: Gets cookie value stored in keychain.$cookies.remove
: Removes cookies value from keychain.$cookies.write
: Gets the cookie value stored in keychain and write it to document.cookie.$cookies.Cookies
: js-cookie Helper.
Examples
Sets the cookie and save it to keychain
// Set a value inside document.cookies
$cookies.Cookies.set('name', 'Ethan');
// Save the document.cookies to keychain.
$cookies.set().then(val => alert(val));
Gets the current cookie value from keychain
$cookies.get().then(val => alert(val));
Reads cookie from keychain and then writes to document.cookie
$cookies.write().then(val => alert(val));
Removes cookies from keychain
$cookies.Cookies.remove('name');
$cookies.remove().then(val => alert(val));
JLKeychain
Keychain extension allows storing values inside iOS Keychain
- Since:
3.0.1
Overview
Keychain is made as a key - value secure storage.
Topics
Actions
$keychain.set
: Sets a key to store a value.$keychain.get
: Gets the value for a given key.$keychain.remove
: Removes a key from the keychain.$keychain.clear
: Removes all keys and values from the keychain.
Example
// Stores the number 3 in keychain
$keychain.set('number', 3);
// Get the number 3 and show an alert with it
$keychain.get('number').then(num => alert(num));
// Removes the stored number
$keychain.remove('number');
// Clears the keychain
$keychain.clear();
JLPhotoLibrary
Provides methods to requesting Photo Library permissions
- Since:
3.0.1
Overview
It would be required if your app wants to access camera or photo library.
Will trigger only in install
event. Does not have webview actions yet.
Topics
Actions
$photolibrary.granted()
Returns Promise.resolve(Bool)
if the permission is granted.
Example
$photolibrary.granted().then(granted => alert(granted));
$photolibrary.authorize(showAlert = false)
Returns Promise.resolve(Int)
with the Authorization Status.
If showAlert = true
then it will prompt an alert requesting the user go Settings.
The returned number corresponds to PHAuthorizationStatus
-
PHAuthorizationStatusNotDetermined = 0 User has not yet made a choice with regards to this application
-
PHAuthorizationStatusRestricted = 1
This application is not authorized to access photo data. The user cannot change this application’s status, possibly due to active restrictions such as parental controls being in place.
- PHAuthorizationStatusDenied = 2
User has explicitly denied this application access to photos data.
- PHAuthorizationStatusAuthorized = 3
User has authorized this application to access photos data.
- PHAuthorizationStatusLimited = 4
User has authorized this application for limited photo library access. Add PHPhotoLibraryPreventAutomaticLimitedAccessAlert = YES to the application's Info.plist to prevent the automatic alert to update the users limited library selection.
Example
$photolibrary.authorize().then(status => $logger.trace(status));
info.plist
Add NSPhotoLibraryUsageDescription and NSCameraUsageDescription permissions to info.plist
<key>NSPhotoLibraryUsageDescription</key>
<string>If you want to use the photolibrary, you have to give permission.</string>
<key>NSCameraUsageDescription</key>
<string>If you want to use the camera, you have to give permission.</string>
JLContacts
The Contacts extension provides APIs to access the user’s contact information.
- Since:
3.0.1
Overview
An iOS app linked on or after iOS 10 needs to include in its Info.plist
file the usage description keys for the types of data it needs to access or it crashes. To access Contacts data specifically, it needs to include NSContactsUsageDescription
.
More Info
Topics
Schema
"name": String,
"lastname": String,
"phone": String,
"phones": Array,
"email": String,
"emails: Array,
"address": String,
"addresses": Array
Actions
$contacts.all()
: Returns apromise
with all the contacts.
Example Return:
[{
"phone": "5555648583",
"phones": ["5555648583", "4155553695"],
"emails": ["kate-bell@mac.com"],
"address": "165 Davis Street\nHillsborough CA 94010",
"lastname": "Bell",
"addresses": ["165 Davis Street\nHillsborough CA 94010"],
"email": "kate-bell@mac.com",
"name": "Kate"
}, {
"phone": "5554787672",
"phones": ["5554787672", "4085555270", "4085553514"],
"emails": ["d-higgins@mac.com"],
"address": "332 Laguna Street\nCorte Madera CA 94925\nUSA",
"lastname": "Higgins",
"addresses": ["332 Laguna Street\nCorte Madera CA 94925\nUSA"],
"email": "d-higgins@mac.com",
"name": "Daniel"
}, {
"phone": "8885555512",
"phones": ["8885555512", "8885551212"],
"emails": ["John-Appleseed@mac.com"],
"address": "3494 Kuhl Avenue\nAtlanta GA 30303\nUSA",
"lastname": "Appleseed",
"addresses": ["3494 Kuhl Avenue\nAtlanta GA 30303\nUSA", "1234 Laurel Street\nAtlanta GA 30303\nUSA"],
"email": "John-Appleseed@mac.com",
"name": "John"
}, {
"phone": "5555228243",
"phones": ["5555228243"],
"emails": ["anna-haro@mac.com"],
"address": "1001 Leavenworth Street\nSausalito CA 94965\nUSA",
"lastname": "Haro",
"addresses": ["1001 Leavenworth Street\nSausalito CA 94965\nUSA"],
"email": "anna-haro@mac.com",
"name": "Anna"
}, {
"phone": "5557664823",
"phones": ["5557664823", "7075551854"],
"emails": ["hank-zakroff@mac.com"],
"address": "1741 Kearny Street\nSan Rafael CA 94901",
"lastname": "Zakroff",
"addresses": ["1741 Kearny Street\nSan Rafael CA 94901"],
"email": "hank-zakroff@mac.com",
"name": "Hank"
}, {
"phone": "5556106679",
"phones": ["5556106679"],
"emails": [],
"address": "1747 Steuart Street\nTiburon CA 94920\nUSA",
"lastname": "Taylor",
"addresses": ["1747 Steuart Street\nTiburon CA 94920\nUSA"],
"email": "",
"name": "David"
}]
Examples
Alert
Join all the contacts by the first name in a single string. And present an alert.
$contacts.all().then(val => alert(val.reduce((acc, v) => acc + v.name + ' ', '')))
Log
Logs the resulting object.
$contacts.all().then(info => $logger.trace(JSON.stringify(info)))
JLReachability
Helps to detect when access to internet was lost.
- Since:
3.0.1
Overview
Sends a notification event inside the WebView and NSNotificationCenter.
Topics
Status
const reachability = {
// Apple NetworkStatus Compatible Names.
status: {
not_reachable: 0,
wwan_reachable: 1,
cellular_reachable: 1, // the same as wwan, just to have ubiquotus language
wifi_reachable: 2
},
status_names: {
0: "No Connection",
1: "Cellular",
2: "Wifi"
}
};
Actions
$reachability.get
: Returns the current reachability object.
Schema
{
"status": Number, // Status of the Connection
"reachable": Boolean, // true if is reachable
"label": String // String with the current connection label
}
Example
$reachability.get().then(result => alert(result.label));
Events
$reachability.events.changed
: Triggered when a change in reachability is detected.
Example
document.addEventListener("$reachability.events.changed", function(e) {
$logger.trace("Reachability Changed: " + JSON.stringify(e.detail));
});
Notification
An NSNotification is sent with the name kReachabilityChangedNotification
.
// Here we set up a NSNotification observer. The Reachability that caused the notification
// is passed in the object parameter
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
See more details at Tony Million's Reachability Docs.