An enaml-native app is organized similar to a react-native app. When you create a new app with the enaml-native-cli. The project directory consists of the basic structure:
android/ #: Android project using gradle
ios/ #: iOS xcode project with cocoapods
src/ #: Python source for your app
venv/ #: Symlink to the conda env for enaml-native packages and cross compiled libraries
environment.yml #: Project config
Your actual apps are in the android
and ios
folders. The build scripts are configured to run enaml-native commands that build and package your python source files as required for the app based on the dependencies.
The src
folder contains the python code that the build system will copy into the root of your python app. It does a recursive copy so any files, folders, subfolders, and files within will be added into your will be available on the actual app.
Note: The location of the root folder of these files can be obtained from the
ASSETS
environment variable.
The environment.yml
file is your project config. It is a conda environment file with some extra sections. If you open it you see the following.
# Name
name: <app name>
bundle_id: com.example.app
# Channels to look for any specific libraries
channels:
- local
- codelv
# App dependencies to be installed
dependencies:
- python=3.10
- pip
- pip-tornado
- android-python * py310*
- enaml-native
- pip:
- enaml-native-cli
# Exclude unused packages and libs using glob patterns here.
# Only the ones required by enaml-native are left in by default.
# If you need to use a module (ex json) then remove lib._json.so
# from this list so it does not get exlcuded. You can also add specific
# exclusions under ios and android.
excluded:
# Packages
- idlelib
- ensurepip
- distutils
- lib2to3
- pydoc_data
- hotshot
- turtledemo
- venv
- site-packages/enaml/qt
- site-packages/enaml/workbench
- site-packages/enaml/*lib
- site-packages/enaml/scint*
# ...
# Android specific configuration
android:
sdk: /home/jrm/Android/Sdk/
ndk: /home/jrm/Android/Sdk/ndk/24.0.7956693
targets:
- x86_64
- x86
- arm
- arm64
excluded:
- lib._ctypes.so
# iOS specific configuration
ios:
targets:
- iphoneos
- iphonesimulator
As you can see there's a few shared properties such as name
, bundle_id
, and version
which are
self explanatory, sources
, and separate configs for ios
and android
.
Each platform config (ios
and android
) has arches
, dependencies
, and excluded
.
The rest are specfic for the platform.
As you may have guessed, targets
defines which platforms to compile python and extensions modules
for and dependencies
is a list of requirements to install on the app.
Note: All dependencies must have a package made specifically for using on android or ios.
The excluded
list is a list of patterns
that you can use to exclude unused python modules from your app to reduce the app size.
For android there's sdk
and ndk
which are the paths used for building. You MUST update these to
point to wherever your SDK and NDK are installed.
For ios there's the project
which defines the name of the <project>.xcworkspace
that will be
built.
It's easiest to just make release builds using android-studio or xcode. It will prompt you to
do any configuration necessary.
See https://developer.android.com/studio/publish/index.html
android
folder in android-studioBuild -> Generate signed APK
Once that is done you can then do release builds with the enaml-native cli using
enaml-native build-android --release
or enaml-native build-ios --release
.
The --release
flag tells it to do a release build (it's debug by default).
For help see the documentation for each platform, enaml-native does nothing special here.
Since apps must include both the python interpreter (as native libraries) and all the python
and app sources, the installed apps can get large if care is not taken to remove unused modules.
The excluded
list can be used to remove unnecessary packages and files from the python build (located under build/python/python.tar.gz
in your app
directory). Add glob patterns to tell the build system to ignore copying files into the python bundle.
excluded:
# Packages
- idlelib
- ensurepip
- distutils
- lib2to3
- pydoc_data
- hotshot
- turtledemo
- venv
- site-packages/enaml/qt
- site-packages/enaml/workbench
- site-packages/enaml/*lib
- site-packages/enaml/scint*
You can also use the apk analyzer in android-studio which nicely graphs which files are using space
within an apk (it even shows within the python.tar.gz!) so use that as well!
With the release of the enaml-native-cli it is now
possible to create a single pip package that includes android
and ios
libraries along
with the python source to use them. These are called enaml native packages
for lack of a better
name and can be installed with either pip
or the enaml-native
cli.
Because enaml-native
is growing and apps will typically want to only include the native
dependencies they need. The project was redesigned and broken down into smaller installable
EnamlPackages
each containing their own with separate native and python requirements.
These packages will allow any user to create, maintain, and share their own versions of
pluggable libraries as needed. There is no need to have your code merged in by some "core" group
of maintainers.
The concept of the package is pretty simple.
venv
with the enaml-native-cli
installed.packages
and recipes
in the venv
using either pip or the cliA package is simply a directory with the following subdirectories and files.
android/ #: Android library using gradle (if applicable)
ios/ #: iOS xcode library using cocoapods (if applicable)
src/ #: Python source for your app
src/setup.py #: Setupfile for your package's source (this is what is installed on the app)
setup.py #: Pip setup file for the enaml-native package
To make an "enaml-package" that follows this format use:
enaml-native init-package <some-package-name> <destination/folder>
If your package requires native dependencies (ex the enaml-native-maps
package
requires native android GoogleMaps
) the android or ios project can be "linked"
to your library when its installed by the user. This is done by the
enaml-native link
command.
"Linking" is automatically adding the necessary changes to the users android and ios projects
(such as adding your library as a project to compile build.gradle) so they can simply install
and use it right away. This makes it easier for new users to quickly get started with your code
without having to read through how to configure it all manually.
An entry point enaml_native_linker
was added to the cli that lets you define a custom function
to link the users project where required.
Note: Currently only linking has been implemented for android (you can use your an entry point)
Unlinking is the reverse of linking and unlinking our package from a users project should remove
any changes made during linking. This is required so upgrading or switching dependencies is
seamless and error free.
You can open the android
folder in Android studio and it will load like any normal android project.
This way you can easily modify any native java code and get all the highlighting and error
checking, etc. all android documentation applies here. The project uses the gradle build system.
enaml-native hooks itself into the gradle build process to include your python source and libraries.
This hook is in android/build.grade.
It simply runs enaml-native bundle-assets
which packages all the pre-compiled packages along with your apps src code into a file python.tar.gz
and copies it to android/app/src/main/assets/python/python.zip
.
enaml-native builds python and any dependencies that have compiled components (c, c++, cython)
using conda-mobile.
These compiled modules are simply added to your apps environment file so they are included in the bundling process.
At runtime extension libraries imported using a custom import hook, see import_hooks.py .
To add custom libraries:
android/app/build.gradle
as needed. You can see there's a few already being used. Once a library is added with gradle you can use it
via making a wrapper Proxy and Toolkit component (see the
native component docs) .
Note: iOS is not yet supported
You can open the project.xcworkspace within the ios
folder in xcode and work in your app normally. All iOS
docs apply here.
To add custom libraries:
ios/<app>/Podfile
as neededios/<app>
and run pod install
or pod update
You can see there's a few already being used. Once a library is added with cocopods you can use it via making
a wrapper Proxy and Toolkit component (see the native component docs) .
That's all for now! Thanks for reading! Please suggest more docs if something is confusing.