Skip to content

Native modules

Cross-platform wrappers around device APIs that are not part of the view tree: camera, GPS, file I/O, notifications, clipboard, share sheet, deep links, permissions, connectivity, secure storage, battery, haptics, and biometrics. Each module is a Swift and a Kotlin class registered by name in the native runtime; the Python classes below are facades that call them through native_module, with a Python implementation registered for the desktop and tests.

Both synchronous and coroutine APIs exist (chosen to match the platform call). For the call-site patterns, the reactive use_app_state / use_net_info hooks, and the runtime coroutines are scheduled on, see the Native modules guide and the Async + data guide.

Registry

Native module registry: name -> callable module, on device or off.

A native module is a named bag of methods implemented in Swift and Kotlin (Camera, Storage, Haptics, ...). Python facades in this package never touch platform APIs; they obtain a NativeModule through native_module and call methods on it:

_clipboard = native_module("Clipboard")
_clipboard.call("set_string", text="hello")
text = _clipboard.call("get_string")
result = await _camera.call_async("take_photo")

On device the module is a BridgeModule that speaks the call(module, method, args_json) protocol described in docs/concepts/bridge.md. Off device (tests, pn preview) the same name resolves to a PythonModule wrapping a plain Python object with the same method names; the built-in fallbacks live in pythonnative.native_modules.fallback and third-party packages register theirs through the pythonnative.modules entry point group or register_python_module.

Modules can also push events (AppState changes, deep links, battery updates). Facades subscribe with NativeModule.add_listener; native delivers through dispatch_module_message and Python implementations through emit.

Classes:

Name Description
NativeModuleError

A native module method failed.

NativeModule

Common surface of bridge-backed and Python-backed modules.

BridgeModule

A module implemented natively; every call crosses the bridge once.

PythonModule

A module implemented by a plain Python object.

Functions:

Name Description
register_python_module

Register impl as the off-device implementation of module name.

unregister_python_module

Remove a Python implementation registered for name (tests).

native_module

Return the module registered under name for the current platform.

dispatch_module_message

Route a native callback("module", ...) payload.

on_event

Subscribe to module/event without resolving the module.

emit

Deliver event to every listener of module (any platform).

Attributes:

Name Type Description
ENTRY_POINT_GROUP

Entry-point group for Python (fallback / test) implementations of native modules.

ENTRY_POINT_GROUP module-attribute

ENTRY_POINT_GROUP = 'pythonnative.modules'

Entry-point group for Python (fallback / test) implementations of native modules.

NativeModuleError

NativeModuleError(
    module: str,
    method: str,
    message: str,
    code: Optional[str] = None,
)

Bases: RuntimeError

A native module method failed.

Attributes:

Name Type Description
module

Module name.

method

Method name.

code

Optional machine-readable code supplied by native.

NativeModule

NativeModule(name: str)

Common surface of bridge-backed and Python-backed modules.

Methods:

Name Description
call

Invoke method synchronously and return its value.

call_async

Invoke method and await its result.

add_listener

Subscribe to event; returns an unsubscribe callable.

listener_count

Number of listeners for event (or for every event when None).

call

call(method: str, **args: Any) -> Any

Invoke method synchronously and return its value.

Raises:

Type Description
NativeModuleError

When the native side reports a failure.

RuntimeError

When the method only completes asynchronously (use call_async).

call_async async

call_async(method: str, **args: Any) -> Any

Invoke method and await its result.

add_listener

add_listener(
    event: str, callback: Listener
) -> Callable[[], None]

Subscribe to event; returns an unsubscribe callable.

listener_count

listener_count(event: Optional[str] = None) -> int

Number of listeners for event (or for every event when None).

BridgeModule

BridgeModule(name: str, transport: Any = None)

Bases: NativeModule

A module implemented natively; every call crosses the bridge once.

Methods:

Name Description
call

Call a native module method with a {"call_id", "args"} envelope.

call_async

Invoke method and await its result.

Attributes:

Name Type Description
transport Any

The transport in use (resolved lazily on first access).

transport property

transport: Any

The transport in use (resolved lazily on first access).

call

call(method: str, **args: Any) -> Any

Call a native module method with a {"call_id", "args"} envelope.

call_async async

call_async(method: str, **args: Any) -> Any

Invoke method and await its result.

PythonModule

PythonModule(name: str, impl: Any = None)

Bases: NativeModule

A module implemented by a plain Python object.

Methods are looked up by name on impl; keyword arguments are forwarded. A method may be a coroutine function (or return an awaitable), in which case call raises and call_async awaits it. The implementation can push events with emit.

Methods:

Name Description
call

Call a native module method with a {"call_id", "args"} envelope.

call_async

Invoke method and await its result.

Attributes:

Name Type Description
impl Any

The implementation object, resolved on first use.

impl property

impl: Any

The implementation object, resolved on first use.

Facades call native_module() at import time, so resolution is deferred until the first method call; by then the package's fallback implementation (or entry point) has had a chance to register.

Raises:

Type Description
KeyError

If no implementation is registered for the module.

call

call(method: str, **args: Any) -> Any

Call a native module method with a {"call_id", "args"} envelope.

call_async async

call_async(method: str, **args: Any) -> Any

Invoke method and await its result.

register_python_module

register_python_module(name: str, impl: Any) -> None

Register impl as the off-device implementation of module name.

impl is an object (or a zero-arg factory returning one) whose methods match the module's method names. Registering replaces any earlier implementation and invalidates the cached module so the next native_module call picks it up.

unregister_python_module

unregister_python_module(name: str) -> None

Remove a Python implementation registered for name (tests).

native_module

native_module(name: str) -> NativeModule

Return the module registered under name for the current platform.

On iOS and Android this is always a BridgeModule; the native runtime decides whether the module exists when a method is first called. Elsewhere it is the registered PythonModule.

Off device the implementation is resolved lazily, on the first method call, so facades can call this at import time; a missing implementation surfaces then as KeyError.

dispatch_module_message

dispatch_module_message(
    module: str, message: Dict[str, Any]
) -> None

Route a native callback("module", ...) payload.

{"call_id": n, "ok": ..., "value"|"error": ...} settles a pending call_async; {"event": name, "payload": ...} fans out to listeners.

on_event

on_event(
    module: str, event: str, callback: Listener
) -> Callable[[], None]

Subscribe to module/event without resolving the module.

Facades use this at import time to route native pushes (AppState change, Linking url, ...) into their own listener lists. Unlike NativeModule.add_listener the subscription survives module re-creation and never touches the platform. Returns an unsubscribe callable.

emit

emit(module: str, event: str, payload: Any = None) -> None

Deliver event to every listener of module (any platform).

Python implementations use this to behave like their native counterparts (a test can emit AppState change events, for example).

Desktop implementations

Pure-Python fallbacks for the built-in native modules.

On iOS and Android every module in this package is implemented in Swift and Kotlin (PythonNativeKit and the pythonnative Gradle module). Off device the same module names resolve to the plain Python classes below, which keep the API usable without a device: in-memory buffers, "unknown" states, and no-op feedback. Unit tests use them directly; the browser preview implements a handful of modules in the page (Alert, Clipboard, Device, ...) and routes the rest here.

Each class has the same method names and argument shapes as its native counterpart, so a facade in this package never branches on platform. Apps and tests may swap any of these with register_python_module.

Functions:

Name Description
default_implementation

Return the factory for the built-in Python fallback implementation of name.

FallbackDevice

Static device information for the host machine.

FallbackStorage

FallbackStorage()

Dict-backed AsyncStorage with optional JSON persistence.

Set PN_STORAGE_DIR to persist between runs (pn preview does this); leave it unset in tests for a purely in-memory store.

FallbackAlert

Records alerts and answers with scripted responses.

The log and response queue live on Alert (Alert._test_log and Alert.set_test_response) so tests have one place to look.

FallbackAssets

FallbackAssets()

Read bundled assets from the checked-out app/assets/ directory.

FallbackImages

Header-only image measurement for PNG, JPEG, GIF, WebP, and BMP.

image_dimensions

image_dimensions(data: bytes) -> Optional[tuple[int, int]]

Return (width, height) in pixels from an image file header, or None.

default_implementation

default_implementation(
    name: str,
) -> Optional[Callable[[], Any]]

Return the factory for the built-in Python fallback implementation of name.

Camera

Cross-platform camera and gallery access.

Both entry points are coroutines: await Camera.take_photo() returns the saved image path (a str) or None if the user cancels. The native Camera module presents UIImagePickerController (iOS) or launches MediaStore.ACTION_IMAGE_CAPTURE / ACTION_PICK (Android) and resolves the call when the picker finishes; Python only awaits the promise.

Example
import pythonnative as pn

async def add_photo():
    path = await pn.Camera.take_photo()
    if path is None:
        return  # user cancelled
    await save_to_album(path)

Classes:

Name Description
Camera

Camera and image-picker interface (static coroutines).

Camera

Camera and image-picker interface (static coroutines).

Methods:

Name Description
take_photo

Launch the device camera to capture a photo.

pick_from_gallery

Open the system gallery picker.

take_photo async staticmethod

take_photo(
    *, quality: float = 0.9, allow_editing: bool = False
) -> Optional[str]

Launch the device camera to capture a photo.

Parameters:

Name Type Description Default
quality float

JPEG compression quality from 0 to 1 on iOS. Android's system capture application controls its output quality.

0.9
allow_editing bool

Present the iOS crop editor before saving.

False

Returns:

Type Description
Optional[str]

The saved image path, or None if the user cancelled (or

Optional[str]

there is no camera to present, as in the browser preview).

Raises:

Type Description
NativeModuleError

If the picker can't be presented, for example because another picker is already open.

pick_from_gallery(
    *, quality: float = 0.9, allow_editing: bool = False
) -> Optional[str]

Open the system gallery picker.

Returns:

Type Description
Optional[str]

The selected image path, or None if the user cancelled.

Raises:

Type Description
NativeModuleError

If the picker can't be presented.

Location

Cross-platform location / GPS access.

Location.get_current is a coroutine that resolves to a (latitude, longitude) tuple, or None if no fix is available (the user denied permission, location services are off, or the request timed out). The native Location module owns the CLLocationManager / LocationManager session and resolves the call with {"latitude", "longitude", "accuracy", "altitude", "timestamp"}.

Permission prompts are triggered the first time a location-using API is called; ensure the appropriate manifest entries (android.permission.ACCESS_FINE_LOCATION) and Info.plist keys (NSLocationWhenInUseUsageDescription) are present.

Example
import pythonnative as pn

async def show_position():
    coords = await pn.Location.get_current()
    if coords is None:
        return
    lat, lon = coords
    print(f"You are at {lat:.5f}, {lon:.5f}")

Classes:

Name Description
Location

GPS / location-services interface.

Location

GPS / location-services interface.

Methods:

Name Description
get_current

Request the device's current location.

get_current_fix

Like get_current but returns the full fix dict.

get_current async staticmethod

get_current(
    *,
    accuracy: Literal["balanced", "high"] = "balanced",
    timeout: float = 10.0
) -> Optional[Coords]

Request the device's current location.

Parameters:

Name Type Description Default
accuracy Literal['balanced', 'high']

Balanced accuracy or the highest available accuracy.

'balanced'
timeout float

Maximum seconds to wait for a fix.

10.0

Returns:

Type Description
Optional[Coords]

(latitude, longitude) if a fix was obtained, otherwise

Optional[Coords]

None.

Raises:

Type Description
NativeModuleError

If the native module fails.

get_current_fix async staticmethod

get_current_fix(
    *,
    accuracy: Literal["balanced", "high"] = "balanced",
    timeout: float = 10.0
) -> Optional[Dict[str, float]]

Like get_current but returns the full fix dict.

Keys: latitude, longitude, and when the platform reports them accuracy (meters), altitude (meters), speed (m/s), heading (degrees), timestamp (Unix seconds).

File system

App-scoped file I/O.

FileSystem answers one question the standard library can't, "where may this app write?", and then gets out of the way: app_dir comes from the native Device module, and path turns an app-relative name into a pathlib.Path you use like any other. The read/write helpers are thin conveniences over that path; they raise the same OSError subclasses open and os do (FileNotFoundError, PermissionError, ...) rather than hiding them behind None and False.

Relative paths are resolved against app_dir; absolute paths are used as-is. Everything here is synchronous: it is local disk I/O on the calling thread, exactly like the standard library.

Example
from pythonnative import FileSystem

FileSystem.write_text("notes/today.txt", "Hello, file system!")
print(FileSystem.read_text("notes/today.txt"))

# Or work with the Path directly:
notes = FileSystem.path("notes")
for entry in sorted(notes.iterdir()):
    print(entry.name)

Classes:

Name Description
FileSystem

App-scoped file I/O.

FileSystem

App-scoped file I/O.

Every helper accepts an absolute path or a path relative to app_dir, and raises OSError (or a subclass) when the operation fails, like the standard library it wraps.

Methods:

Name Description
app_dir

Return the app's writable data directory.

path

Resolve path against app_dir and return it as a pathlib.Path.

read_text

Read a text file; raises FileNotFoundError and friends like open does.

write_text

Write a text file, creating parent directories as needed.

read_bytes

Read a binary file.

write_bytes

Write a binary file, creating parent directories as needed.

exists

Return whether a file or directory exists.

delete

Delete a single file.

list_dir

Return the entry names in a directory (app_dir by default), sorted.

get_size

Return a file's size in bytes.

ensure_dir

Create a directory (and any missing parents) if needed; returns its Path.

join

Join path components with the OS separator (os.path.join over str(part)).

app_dir staticmethod

app_dir() -> str

Return the app's writable data directory.

On Android the result is Context.getFilesDir(). On iOS it is the app's Documents directory. Off device, without either runtime, a .pythonnative_data directory under the user's home folder is used. The value comes from the native Device module's info() and is cached after the first call.

Returns:

Type Description
str

Absolute path to the app's data directory.

path staticmethod

path(path: PathLike = '') -> Path

Resolve path against app_dir and return it as a pathlib.Path.

Absolute paths are returned unchanged. With no argument, returns app_dir itself.

read_text staticmethod

read_text(path: PathLike, encoding: str = 'utf-8') -> str

Read a text file; raises FileNotFoundError and friends like open does.

write_text staticmethod

write_text(
    path: PathLike, content: str, encoding: str = "utf-8"
) -> None

Write a text file, creating parent directories as needed.

read_bytes staticmethod

read_bytes(path: PathLike) -> bytes

Read a binary file.

write_bytes staticmethod

write_bytes(path: PathLike, data: bytes) -> None

Write a binary file, creating parent directories as needed.

exists staticmethod

exists(path: PathLike) -> bool

Return whether a file or directory exists.

delete staticmethod

delete(path: PathLike, *, missing_ok: bool = False) -> None

Delete a single file.

Parameters:

Name Type Description Default
path PathLike

Absolute or app_dir-relative path.

required
missing_ok bool

Ignore a missing file instead of raising FileNotFoundError (same as Path.unlink).

False

list_dir staticmethod

list_dir(path: PathLike = '') -> List[str]

Return the entry names in a directory (app_dir by default), sorted.

get_size staticmethod

get_size(path: PathLike) -> int

Return a file's size in bytes.

ensure_dir staticmethod

ensure_dir(path: PathLike) -> Path

Create a directory (and any missing parents) if needed; returns its Path.

join staticmethod

join(*parts: Any) -> str

Join path components with the OS separator (os.path.join over str(part)).

Notifications

Local notifications and remote push registration.

Coroutines for requesting permission and scheduling / cancelling local notifications, backed by the native Notifications module (UNUserNotificationCenter on iOS, NotificationManager on Android).

Call await Notifications.request_permission() before scheduling on either platform. Android 13+ requests POST_NOTIFICATIONS; scheduling returns False when notifications aren't enabled. Android delayed notifications use OS-owned, inexact alarms and survive ordinary process death. Delivery may be deferred by power management. Reboot, force-stop, and app removal clear alarms.

For remote (server-sent) pushes, enable the remote_notifications capability in pythonnative.toml and call Notifications.get_device_token() to register with APNs and receive the device token your server needs. Android remote push requires Firebase Cloud Messaging, which needs a per-app google-services.json and is not wired up by the built-in module; get_device_token returns None there.

Example
import pythonnative as pn

async def setup_reminders():
    if not await pn.Notifications.request_permission():
        return
    await pn.Notifications.schedule(
        title="Reminder",
        body="Time for a walk!",
        delay_seconds=60,
        identifier="walk",
    )

Classes:

Name Description
Notifications

Local notification interface.

Notifications

Local notification interface.

Methods:

Name Description
request_permission

Request notification permission from the user.

schedule

Schedule a local notification.

cancel

Cancel a pending notification by its identifier (a no-op when none is pending).

get_device_token

Register for remote notifications and return the device token.

request_permission async staticmethod

request_permission() -> bool

Request notification permission from the user.

On Android 12 and below the manifest declaration is sufficient and this returns True without prompting. On Android 13+ (API 33) the POST_NOTIFICATIONS runtime permission prompt is shown if the user hasn't decided yet.

Returns:

Type Description
bool

True if granted (or no prompt is needed), False if

bool

the user declined (always False off device).

Raises:

Type Description
NativeModuleError

If the native module fails.

schedule async staticmethod

schedule(
    title: str,
    body: str = "",
    *,
    delay_seconds: float = 0,
    identifier: str = "default"
) -> bool

Schedule a local notification.

Parameters:

Name Type Description Default
title str

Notification title.

required
body str

Notification body text.

''
delay_seconds float

Seconds from now until delivery. Use 0 for an effectively immediate notification.

0
identifier str

Stable ID used by cancel to target this notification.

'default'

Returns:

Type Description
bool

True once scheduled, False if the user has denied

bool

notification permission (so nothing was scheduled).

Raises:

Type Description
NativeModuleError

If the native module fails.

cancel async staticmethod

cancel(identifier: str = 'default') -> None

Cancel a pending notification by its identifier (a no-op when none is pending).

get_device_token async staticmethod

get_device_token() -> Optional[str]

Register for remote notifications and return the device token.

On iOS this calls registerForRemoteNotifications and waits for the APNs callback; the token is a lowercase hex string your server passes to APNs. Requires the remote_notifications capability (which adds the aps-environment entitlement) and a real device (the simulator has no APNs connection).

Returns:

Type Description
Optional[str]

The APNs token, or None on platforms without built-in

Optional[str]

remote push support (Android and off device).

Raises:

Type Description
NativeModuleError

If APNs registration fails; code is "apns" and the message carries the system's error.

Clipboard

Cross-platform clipboard access.

Clipboard reads and writes the system pasteboard through the native Clipboard module (UIPasteboard on iOS, ClipboardManager on Android). The pasteboard lives in process memory on both platforms, so every method is synchronous.

Off device the module is a process-local string buffer, which keeps it usable in pn preview and unit tests.

Example
import pythonnative as pn

pn.Clipboard.set_string("hello")
assert pn.Clipboard.get_string() == "hello"

Classes:

Name Description
Clipboard

System clipboard interface (synchronous).

Clipboard

System clipboard interface (synchronous).

Raises:

Type Description
NativeModuleError

If the native module reports a failure.

Methods:

Name Description
set_string

Copy text onto the system clipboard.

get_string

Return the current clipboard string ("" when empty).

has_string

Return True when the clipboard holds non-empty text.

set_string staticmethod

set_string(text: str) -> None

Copy text onto the system clipboard.

get_string staticmethod

get_string() -> str

Return the current clipboard string ("" when empty).

has_string staticmethod

has_string() -> bool

Return True when the clipboard holds non-empty text.

Share

Present the system share sheet.

Share.share is a coroutine that opens UIActivityViewController (iOS) or an ACTION_SEND chooser (Android) through the native Share module and resolves to True once the user completes a share or False if they dismiss it.

Example
import pythonnative as pn

async def share_link():
    await pn.Share.share(
        message="Check out PythonNative!",
        url="https://example.com",
    )

Classes:

Name Description
Share

System share-sheet interface.

Share

System share-sheet interface.

Methods:

Name Description
share

Open the share sheet with message / url.

share async staticmethod

share(
    *,
    message: Optional[str] = None,
    url: Optional[str] = None,
    title: Optional[str] = None
) -> bool

Open the share sheet with message / url.

Parameters:

Name Type Description Default
message Optional[str]

Text body to share.

None
url Optional[str]

A URL to share (combined with message on Android).

None
title Optional[str]

Chooser title (Android) / subject (iOS mail).

None

Returns:

Type Description
bool

True if the user completed a share, False if they

bool

dismissed the sheet or the platform has no share UI (tests).

Raises:

Type Description
NativeModuleError

If the sheet could not be presented.

Linking

Open URLs, deep links, and the system settings page.

Linking wraps UIApplication.openURL / Intent(ACTION_VIEW) (in the native Linking module) so a Python app can hand a URL (https:, mailto:, tel:, a custom scheme, ...) to the OS.

Outbound methods hand the URL to the OS and return at once, so they are synchronous; the bool says whether the platform accepted the request. Off device they return False.

Inbound deep links flow the other way: declare your schemes in pythonnative.toml (app.url_schemes) and the native module pushes a url event for every URL that opens the app, which lands in dispatch_url. The URL that cold-started the app is kept and returned by get_initial_url; later URLs reach subscribers added with add_listener.

Example
import pythonnative as pn

if url := pn.Linking.get_initial_url():
    navigate_to(url)

unsubscribe = pn.Linking.add_listener(navigate_to)

Classes:

Name Description
Linking

System URL / deep-link interface (synchronous).

Functions:

Name Description
set_initial_url

Record the launch URL (or clear it with None).

dispatch_url

Deliver an inbound deep link.

Linking

System URL / deep-link interface (synchronous).

Raises:

Type Description
NativeModuleError

If the native module fails.

Methods:

Name Description
open_url

Hand url to the OS. Returns True if it was accepted.

can_open_url

Return True when some installed app can handle url.

open_settings

Open this app's entry in the system Settings app.

get_initial_url

Return the URL that launched the app, if any.

add_listener

Subscribe to deep links that arrive while the app is running.

open_url staticmethod

open_url(url: str) -> bool

Hand url to the OS. Returns True if it was accepted.

can_open_url staticmethod

can_open_url(url: str) -> bool

Return True when some installed app can handle url.

open_settings staticmethod

open_settings() -> bool

Open this app's entry in the system Settings app.

get_initial_url staticmethod

get_initial_url() -> Optional[str]

Return the URL that launched the app, if any.

add_listener staticmethod

add_listener(
    callback: Callable[[str], None],
) -> Callable[[], None]

Subscribe to deep links that arrive while the app is running.

Parameters:

Name Type Description Default
callback Callable[[str], None]

Called with the full URL string for every inbound deep link (including the initial one, which is dispatched right after startup).

required

Returns:

Type Description
Callable[[], None]

A zero-arg function that unsubscribes when called.

set_initial_url

set_initial_url(url: Optional[str]) -> None

Record the launch URL (or clear it with None).

dispatch_url

dispatch_url(url: str) -> None

Deliver an inbound deep link.

The first URL ever dispatched is also recorded as the initial URL (a cold start from a deep link reaches Python only after the interpreter boots, so native can't report it earlier than this).

Parameters:

Name Type Description Default
url str

The full URL string that opened the app.

required

Permissions

Runtime permission checks and requests.

Permissions normalizes the very different iOS and Android permission models behind two calls, both served by the native Permissions module:

  • check(permission): a coroutine that returns a status without prompting. Some platform settings APIs answer asynchronously.
  • request(permission): a coroutine that shows the system prompt (if needed) and resolves to the resulting status.

Statuses are "granted", "denied", "blocked" (denied with "don't ask again" / Settings required), or "undetermined".

Permission names are the same words you declare in the [permissions] table of pythonnative.toml, so the string that puts NSCameraUsageDescription in your Info.plist is the string you pass here: "camera", "microphone", "photo_library", "location_when_in_use", "contacts", "notifications". The full list is RUNTIME_PERMISSIONS; capabilities that have no runtime prompt (vibration, background_fetch, ...) are declared in the config only. A name outside the list raises ValueError before anything reaches native.

Example
import pythonnative as pn

async def scan():
    if await pn.Permissions.request("camera") != "granted":
        return
    await pn.Camera.take_photo()

Classes:

Name Description
Permissions

Runtime permission interface.

Attributes:

Name Type Description
PermissionStatus

Outcome of a check or request.

PermissionName

A capability that has a runtime prompt; the same names as [permissions] in pythonnative.toml.

RUNTIME_PERMISSIONS

Every permission name check / request accept, in the [permissions] vocabulary.

PermissionStatus module-attribute

PermissionStatus = Literal[
    "granted", "denied", "blocked", "undetermined"
]

Outcome of a check or request.

PermissionName module-attribute

PermissionName = Literal[
    "camera",
    "microphone",
    "photo_library",
    "location_when_in_use",
    "contacts",
    "notifications",
]

A capability that has a runtime prompt; the same names as [permissions] in pythonnative.toml.

RUNTIME_PERMISSIONS module-attribute

RUNTIME_PERMISSIONS = (
    "camera",
    "microphone",
    "photo_library",
    "location_when_in_use",
    "contacts",
    "notifications",
)

Every permission name check / request accept, in the [permissions] vocabulary.

Permissions

Runtime permission interface.

Raises:

Type Description
ValueError

For a permission name outside RUNTIME_PERMISSIONS.

NativeModuleError

If the native module fails.

Methods:

Name Description
check

Return the current status of permission without prompting.

request

Prompt for permission (if needed) and return the result.

check async staticmethod

check(permission: PermissionName) -> PermissionStatus

Return the current status of permission without prompting.

request async staticmethod

request(permission: PermissionName) -> PermissionStatus

Prompt for permission (if needed) and return the result.

Off device (pn preview, tests) the answer is always "undetermined": there is no prompt to show.

App state

Foreground / background app lifecycle state.

AppState exposes the current lifecycle phase ("active", "inactive", or "background") and lets you subscribe to transitions. The native AppState module pushes a change event on every transition; off device, tests drive the same path through dispatch_app_state.

Prefer the use_app_state hook inside components; use the imperative API for non-UI code.

Example
import pythonnative as pn

@pn.component
def Banner():
    state = pn.use_app_state()
    return pn.Text(f"App is {state}")

Classes:

Name Description
AppState

App lifecycle state interface.

Functions:

Name Description
dispatch_app_state

Update the current state and notify every listener.

use_app_state

Subscribe a component to AppState.

AppState

App lifecycle state interface.

Methods:

Name Description
current_state

Return the current lifecycle phase.

add_listener

Subscribe to lifecycle changes.

current_state staticmethod

current_state() -> AppStateStatus

Return the current lifecycle phase.

add_listener staticmethod

add_listener(
    callback: Callable[[AppStateStatus], None],
) -> Callable[[], None]

Subscribe to lifecycle changes.

Returns:

Type Description
Callable[[], None]

A zero-arg function that unsubscribes when called.

dispatch_app_state

dispatch_app_state(state: AppStateStatus) -> None

Update the current state and notify every listener.

Unknown values are ignored so a misbehaving host can't push garbage into the tree.

use_app_state

use_app_state() -> AppStateStatus

Subscribe a component to AppState.

Returns:

Type Description
AppStateStatus

The current lifecycle phase; the component re-renders whenever

AppStateStatus

it changes.

Network connectivity

Network connectivity state.

NetInfo reports whether the device is online and over what kind of connection. fetch returns a snapshot dict; add_listener (and the use_net_info hook) deliver live updates pushed by the native NetInfo module (NWPathMonitor on iOS, ConnectivityManager.NetworkCallback on Android) as change events. Off device, tests push snapshots through dispatch_net_info.

A snapshot looks like::

{"is_connected": True, "type": "wifi", "is_internet_reachable": True}

type is one of "wifi", "cellular", "ethernet", "none", or "unknown".

Classes:

Name Description
NetInfo

Network connectivity interface.

Functions:

Name Description
dispatch_net_info

Push a new connectivity snapshot and notify listeners.

use_net_info

Subscribe a component to NetInfo.

NetInfo

Network connectivity interface.

Methods:

Name Description
fetch

Return a fresh snapshot of connectivity state.

add_listener

Subscribe to connectivity changes; returns an unsubscribe fn.

fetch staticmethod

fetch() -> NetInfoState

Return a fresh snapshot of connectivity state.

add_listener staticmethod

add_listener(
    callback: Callable[[NetInfoState], None],
) -> Callable[[], None]

Subscribe to connectivity changes; returns an unsubscribe fn.

dispatch_net_info

dispatch_net_info(state: NetInfoState) -> None

Push a new connectivity snapshot and notify listeners.

use_net_info

use_net_info() -> NetInfoState

Subscribe a component to NetInfo.

Returns:

Type Description
NetInfoState

The latest connectivity snapshot dict; the component

NetInfoState

re-renders whenever connectivity changes.

Secure storage

Encrypted key/value storage for secrets (tokens, credentials).

SecureStore persists small string values in the iOS Keychain and Android EncryptedSharedPreferences (the native SecureStore module), the right place for auth tokens and other secrets that AsyncStorage (plain, unencrypted) should never hold.

Both backing stores complete on the calling thread, so every method is synchronous. Reads return Optional[str]; writes return nothing and raise on failure. Off device the module falls back to an in-process dict so code paths stay exercisable without a device Keychain.

Example
import pythonnative as pn

pn.SecureStore.set_item("token", "abc123")
token = pn.SecureStore.get_item("token")

Classes:

Name Description
SecureStore

Encrypted secret storage (synchronous).

SecureStore

Encrypted secret storage (synchronous).

Raises:

Type Description
NativeModuleError

If the Keychain / EncryptedSharedPreferences operation fails (for example a Keychain entitlement problem).

Methods:

Name Description
set_item

Store value under key, replacing any previous value.

get_item

Return the value for key, or None if absent.

delete_item

Delete key. Returns True if it existed, False if there was nothing to delete.

set_item staticmethod

set_item(key: str, value: str) -> None

Store value under key, replacing any previous value.

get_item staticmethod

get_item(key: str) -> Optional[str]

Return the value for key, or None if absent.

delete_item staticmethod

delete_item(key: str) -> bool

Delete key. Returns True if it existed, False if there was nothing to delete.

Battery

Battery level and charging state.

Battery reports the current charge fraction (0.0 to 1.0, or -1.0 when the platform doesn't know) and charging state, and lets you subscribe to changes. Both getters read a value the OS already holds, so they are synchronous. The native Battery module pushes a change event with {"level", "state"}; off device, tests drive the same path through dispatch_battery.

Classes:

Name Description
Battery

Battery interface (synchronous getters + change listener).

Functions:

Name Description
dispatch_battery

Notify listeners of a battery change.

Battery

Battery interface (synchronous getters + change listener).

Raises:

Type Description
NativeModuleError

If the native module fails.

Methods:

Name Description
get_level

Return the charge fraction in [0, 1], or -1.0 when the platform can't report it.

get_state

Return "charging" / "full" / "unplugged" / "unknown".

add_listener

Subscribe to battery changes; returns an unsubscribe fn.

get_level staticmethod

get_level() -> float

Return the charge fraction in [0, 1], or -1.0 when the platform can't report it.

get_state staticmethod

get_state() -> BatteryState

Return "charging" / "full" / "unplugged" / "unknown".

add_listener staticmethod

add_listener(
    callback: Callable[[Dict[str, object]], None],
) -> Callable[[], None]

Subscribe to battery changes; returns an unsubscribe fn.

Each callback receives {"level": float, "state": str}.

dispatch_battery

dispatch_battery(level: float, state: BatteryState) -> None

Notify listeners of a battery change.

Haptics & vibration

Haptic feedback and raw vibration.

Two interfaces live here, both backed by the native Haptics module:

  • Haptics: semantic, iOS-style feedback (impact / notification / selection) backed by UIFeedbackGenerator on iOS and VibrationEffect patterns on Android.
  • Vibration: a blunt "buzz for N milliseconds" interface for cases where you want an explicit duration.

Every method is synchronous (the OS queues the effect and returns). Missing hardware is not an error: on a device without a Taptic Engine or vibrator, and off device the native module simply does nothing.

Classes:

Name Description
Haptics

Semantic haptic feedback (synchronous).

Vibration

Raw vibration control (synchronous).

Haptics

Semantic haptic feedback (synchronous).

Raises:

Type Description
NativeModuleError

If the native module fails.

Methods:

Name Description
impact

Play a physical "impact" tap of the given style.

notification

Play a success / warning / error notification pattern.

selection

Play the light "selection changed" tick.

impact staticmethod

impact(style: ImpactStyle = 'medium') -> None

Play a physical "impact" tap of the given style.

notification staticmethod

notification(type_: NotificationType = 'success') -> None

Play a success / warning / error notification pattern.

selection staticmethod

selection() -> None

Play the light "selection changed" tick.

Vibration

Raw vibration control (synchronous).

Raises:

Type Description
NativeModuleError

If the native module fails.

Methods:

Name Description
vibrate

Vibrate for duration_ms milliseconds.

cancel

Cancel an in-progress vibration (Android only).

vibrate staticmethod

vibrate(duration_ms: int = 400) -> None

Vibrate for duration_ms milliseconds.

iOS has no arbitrary-duration API; the native module approximates short buzzes with a heavy impact and longer ones with the legacy system vibration sound.

cancel staticmethod

cancel() -> None

Cancel an in-progress vibration (Android only).

Biometrics

Biometric authentication (Face ID / Touch ID / fingerprint).

Biometrics gates an action behind the device's biometric hardware via LAContext (iOS) and BiometricPrompt (Android), both implemented in the native Biometrics module.

is_available is synchronous (a capability lookup); authenticate is a coroutine that presents the system prompt and resolves to True on success or False when the user fails or cancels the prompt.

Example
import pythonnative as pn

async def unlock():
    if await pn.Biometrics.authenticate("Unlock your vault"):
        show_secrets()

Classes:

Name Description
Biometrics

Biometric authentication interface.

Biometrics

Biometric authentication interface.

Raises:

Type Description
NativeModuleError

If the native module fails.

Methods:

Name Description
is_available

Return True when biometric auth can be attempted (enrolled hardware; False off device).

authenticate

Present the biometric prompt; resolve True on success, False on failure or cancel.

is_available staticmethod

is_available() -> bool

Return True when biometric auth can be attempted (enrolled hardware; False off device).

authenticate async staticmethod

authenticate(reason: str = 'Authenticate') -> bool

Present the biometric prompt; resolve True on success, False on failure or cancel.

Next steps