Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/Support/CommonMark/CommonMark.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace App\Support\CommonMark;

use App\Extensions\TorchlightWithCopyExtension;
use League\CommonMark\CommonMarkConverter;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
Expand Down
2 changes: 1 addition & 1 deletion config/docs.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
'mobile' => 2,
],

];
];
4 changes: 4 additions & 0 deletions resources/views/docs/mobile/2/plugins/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: Plugins
order: 60
---
130 changes: 130 additions & 0 deletions resources/views/docs/mobile/2/plugins/bridge-functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
title: Bridge Functions
order: 400
---

## How Bridge Functions Work

Bridge functions are the connection between your PHP code and native platform code. When you call a method like
`MyPlugin::doSomething()`, NativePHP routes that to your Swift or Kotlin implementation running on the device.

The flow:
1. PHP calls `nativephp_call('MyPlugin.DoSomething', $params)`
2. The native bridge locates the registered function
3. Your native code executes and returns a response
4. PHP receives the result

## Declaring Bridge Functions

In your `nativephp.json`, declare each function with its platform implementations:

```json
{
"bridge_functions": [
{
"name": "MyPlugin.DoSomething",
"ios": "MyPluginFunctions.DoSomething",
"android": "com.vendor.plugin.myplugin.MyPluginFunctions.DoSomething",
"description": "Does something useful"
}
]
}
```

The `name` is what PHP uses. The platform-specific values point to your native class and method.

## Swift Implementation (iOS)

Create your functions in `resources/ios/Sources/`:

```swift
import Foundation

enum MyPluginFunctions {

class DoSomething: BridgeFunction {
func execute(parameters: [String: Any]) throws -> [String: Any] {
let option = parameters["option"] as? String ?? ""

// Do your native work here

return BridgeResponse.success(data: [
"result": "completed",
"option": option
])
}
}
}
```

Key points:
- Implement the `BridgeFunction` protocol
- Parameters come as a dictionary
- Return using `BridgeResponse.success()` or `BridgeResponse.error()`

## Kotlin Implementation (Android)

Create your functions in `resources/android/src/.../`:

```kotlin
package com.vendor.plugin.myplugin

import com.example.androidphp.bridge.BridgeFunction
import com.example.androidphp.bridge.BridgeResponse

object MyPluginFunctions {

class DoSomething : BridgeFunction {
override fun execute(parameters: Map<String, Any>): Map<String, Any> {
val option = parameters["option"] as? String ?: ""

// Do your native work here

return BridgeResponse.success(mapOf(
"result" to "completed",
"option" to option
))
}
}
}
```

<aside>

#### Package Naming

Use your plugin's namespace in the Kotlin package name. The scaffolding command sets this up correctly.

</aside>

## Calling from PHP

Create a facade method that calls your bridge function:

```php
class MyPlugin
{
public function doSomething(array $options = []): mixed
{
$result = nativephp_call('MyPlugin.DoSomething', json_encode($options));

return json_decode($result)?->data;
}
}
```

## Error Handling

Return errors from native code using `BridgeResponse.error()`:

```swift
// Swift
return BridgeResponse.error(message: "Something went wrong")
```

```kotlin
// Kotlin
return BridgeResponse.error("Something went wrong")
```

The error message is available in PHP through the response.
154 changes: 154 additions & 0 deletions resources/views/docs/mobile/2/plugins/creating-plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
title: Creating Plugins
order: 300
---

## Scaffolding a Plugin

The quickest way to create a plugin is with the interactive scaffolding command:

```shell
php artisan native:plugin:create
```

This walks you through naming, namespace selection, and feature options, then generates a complete plugin structure.

## Plugin Structure

A plugin follows a standard layout:

```
my-plugin/
├── composer.json # Package metadata, type must be "nativephp-plugin"
├── nativephp.json # Plugin manifest
├── src/
│ ├── MyPluginServiceProvider.php
│ ├── MyPlugin.php # Main class
│ ├── Facades/
│ │ └── MyPlugin.php
│ ├── Events/
│ │ └── SomethingHappened.php
│ └── Commands/ # Lifecycle hook commands
├── resources/
│ ├── android/src/ # Kotlin bridge functions
│ ├── ios/Sources/ # Swift bridge functions
│ └── js/ # JavaScript library stubs
```

## The composer.json

Your `composer.json` must specify the plugin type:

```json
{
"name": "vendor/my-plugin",
"type": "nativephp-plugin",
"extra": {
"laravel": {
"providers": ["Vendor\\MyPlugin\\MyPluginServiceProvider"]
},
"nativephp": {
"manifest": "nativephp.json"
}
}
}
```

The `type: nativephp-plugin` tells NativePHP to look for native code in this package.

## The nativephp.json Manifest

The manifest declares everything about your plugin:

```json
{
"name": "vendor/my-plugin",
"namespace": "MyPlugin",
"bridge_functions": [
{
"name": "MyPlugin.DoSomething",
"ios": "MyPluginFunctions.DoSomething",
"android": "com.vendor.plugin.myplugin.MyPluginFunctions.DoSomething"
}
],
"events": ["Vendor\\MyPlugin\\Events\\SomethingHappened"],
"permissions": {
"android": ["android.permission.SOME_PERMISSION"],
"ios": {
"NSCameraUsageDescription": "We need camera access"
}
}
}
```

The key fields:

- **namespace** — Used to generate bridge function registration code
- **bridge_functions** — Maps PHP calls to native implementations
- **events** — Event classes your plugin dispatches
- **permissions** — Platform permissions your plugin requires

## Local Development

During development, add your plugin to your app's `composer.json` as a path repository:

```json
{
"repositories": [
{"type": "path", "url": "../packages/my-plugin"}
]
}
```

Then require it:

```shell
composer require vendor/my-plugin
```

Changes to your plugin's PHP code are picked up immediately. Changes to native code require a rebuild with
`php artisan native:run`.

<aside>

#### Validate Early and Often

Run `php artisan native:plugin:validate` to catch manifest errors, missing native code, or mismatched function
declarations before you try to build.

</aside>

## JavaScript Library

Plugins can provide a JavaScript library for SPA frameworks. The scaffolding creates a stub in `resources/js/`:

```js
// resources/js/myPlugin.js
const baseUrl = '/_native/api/call';

async function bridgeCall(method, params = {}) {
const response = await fetch(baseUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ method, params })
});
return response.json();
}

export async function doSomething(options = {}) {
return bridgeCall('MyPlugin.DoSomething', options);
}
```

Users can then import your functions directly in Vue, React, or vanilla JS.

## Plugin Writer Agent

If you're building a complex plugin, install the plugin-writer agent to help with native code patterns:

```shell
php artisan native:plugin:install-agent
```

This copies a specialized agent configuration to your project's `.claude/agents/` directory. The agent understands
NativePHP plugin patterns and can help write Swift/Kotlin bridge functions, event dispatching, and hook commands.
Loading