Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Pigeon] readme updates #3705

Merged
merged 29 commits into from
Jun 30, 2023
Merged
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
4 changes: 4 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 10.1.1

* Updates README to better reflect modern usage.

## 10.1.0

* [objc] Adds macOS support to facilitate code sharing with existing iOS plugins.
Expand Down
304 changes: 88 additions & 216 deletions packages/pigeon/README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,107 @@
# Pigeon

Pigeon is a code generator tool to make communication between Flutter and the
host platform type-safe, easier and faster.
host platform type-safe, easier, and faster.

## Supported Platforms
Pigeon removes the necessity to manage strings across multiple platforms and languages.
It also improves efficiency over common method channel patterns. Most importantly though,
it removes the need to write custom platform channel code, since pigeon generates it for you.

Currently Pigeon supports generating:
For usage examples, see the [Example README](./example/README.md).

## Features

### Supported Platforms

Currently pigeon supports generating:
* Kotlin and Java code for Android
* Swift and Objective-C code for iOS and macOS
* C++ code for Windows

## Runtime Requirements
### Supported Datatypes

Pigeon uses the `StandardMessageCodec` so it supports
[[any datatype platform channels support](https://flutter.dev/docs/development/platform-integration/platform-channels#codec)].

Custom classes and nested datatypes are also supported.

#### Enums

Pigeon currently supports enum generation in class fields only.
See issue: [87307](https://github.com/flutter/flutter/issues/87307).

### Synchronous and Asynchronous methods

While all calls across platform channel APIs (such as pigeon methods) are asynchronous,
pigeon methods can be written on the native side as synchronous methods,
to make it simpler to always reply exactly once.

Pigeon generates all the code that is needed to communicate between Flutter and
the host platform, there is no extra runtime requirement. A plugin author
doesn't need to worry about conflicting versions of Pigeon.
If asynchronous methods are needed, the `@async` annotation can be used. This will require
results or errors to be returned via a provided callback. [Example](./example/README.md#HostApi_Example).

### Error Handling

#### Kotlin, Java and Swift

All Host API exceptions are translated into Flutter `PlatformException`.
* For synchronous methods, thrown exceptions will be caught and translated.
* For asynchronous methods, there is no default exception handling; errors
should be returned via the provided callback.

To pass custom details into `PlatformException` for error handling,
use `FlutterError` in your Host API. [Example](./example/README.md#HostApi_Example).

To use `FlutterError` in Swift you must first extend a standard error.
[Example](./example/README.md#AppDelegate.swift).

#### Objective-C and C++

Host API errors can be sent using the provided `FlutterError` class (translated into `PlatformException`).

For synchronous methods:
* Objective-C - Set the `error` argument to a `FlutterError` reference.
* C++ - Return a `FlutterError`.

For async methods:
* Return a `FlutterError` through the provided callback.


### Task Queue

When targeting a Flutter version that supports the
[TaskQueue API](https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-kotlin-tab#channels-and-platform-threading)
the threading model for handling HostApi methods can be selected with the
`TaskQueue` annotation.

## Usage

1) Add Pigeon as a `dev_dependency`.
1) Add pigeon as a `dev_dependency`.
1) Make a ".dart" file outside of your "lib" directory for defining the
communication interface.
1) Run pigeon on your ".dart" file to generate the required Dart and
host-language code: `flutter pub get` then `flutter pub run pigeon`
with suitable arguments (see [example](./example)).
with suitable arguments. [Example](./example/README.md#Invocation).
1) Add the generated Dart code to `./lib` for compilation.
1) Implement the host-language code and add it to your build (see below).
1) Call the generated Dart methods.

### Rules for defining your communication interface
[Example](./example/README.md#HostApi_Example)

1) The file should contain no method or function definitions, only declarations.
1) Custom classes used by APIs are defined as classes with fields of the
supported datatypes (see the supported Datatypes section).
1) APIs should be defined as an `abstract class` with either `@HostApi()` or
`@FlutterApi()` as metadata. `@HostApi()` being for procedures that are defined
on the host platform and the `@FlutterApi()` for procedures that are defined in Dart.
1) Method declarations on the API classes should have arguments and a return
value whose types are defined in the file, are supported datatypes, or are
`void`.
1) Generics are supported, but can currently only be used with nullable types
(example: `List<int?>`).
1) Objc and Swift have special naming conventions that can be utilized with the
`@ObjCSelector` and `@SwiftFunction` respectively.

### Flutter calling into iOS steps

1) Add the generated Objective-C or Swift code to your Xcode project for compilation
Expand Down Expand Up @@ -58,214 +132,12 @@ doesn't need to worry about conflicting versions of Pigeon.

### Calling into Flutter from the host platform

Flutter also supports calling in the opposite direction. The steps are similar
Pigeon also supports calling in the opposite direction. The steps are similar
but reversed. For more information look at the annotation `@FlutterApi()` which
denotes APIs that live in Flutter but are invoked from the host platform.

### Rules for defining your communication interface

1) The file should contain no method or function definitions, only declarations.
1) Custom classes used by APIs are defined as classes with fields of the
supported datatypes (see the supported Datatypes section).
1) APIs should be defined as an `abstract class` with either `HostApi()` or
`FlutterApi()` as metadata. The former being for procedures that are defined
on the host platform and the latter for procedures that are defined in Dart.
1) Method declarations on the API classes should have arguments and a return
value whose types are defined in the file, are supported datatypes, or are
`void`.
1) Generics are supported, but can currently only be used with nullable types
(example: `List<int?>`).

## Supported Datatypes

Pigeon uses the `StandardMessageCodec` so it supports any datatype Platform
Channels supports
[[documentation](https://flutter.dev/docs/development/platform-integration/platform-channels#codec)].
Nested datatypes are supported, too.

## Features

### Asynchronous Handlers

By default Pigeon will generate synchronous handlers for messages and
asynchronous methods. If you want a handler to be able to respond to a message
asynchronously you can use the @async annotation as of version 0.1.20.

Example:

```dart
class Value {
int? number;
}

@HostApi()
abstract class Api2Host {
@async
Value calculate(Value value);
}
```

Generates:

```objc
// Objective-C
@protocol Api2Host
-(void)calculate:(nullable Value *)input
completion:(void(^)(Value *_Nullable, FlutterError *_Nullable))completion;
@end
```

```swift
// Swift

/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
protocol Api2Host {
func calculate(value: Value, completion: @escaping (Value) -> Void)
}
```

```java
// Java
public interface Result<T> {
void success(T result);
}

/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
public interface Api2Host {
void calculate(Value arg, Result<Value> result);
}
```

```kotlin
// Kotlin

/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
interface Api2Host {
fun calculate(value: Value, callback: (Result<Value>) -> Unit)
}
```

```c++
// C++

/** Generated class from Pigeon that represents a handler of messages from Flutter.*/
class Api2Host {
public:
virtual void calculate(Value value, flutter::MessageReply<Value> result) = 0;
}
```

### Null Safety (NNBD)

Pigeon supports generating null-safe code, but it doesn't yet support:

1) Nullable generics type arguments
1) Nullable enum arguments to methods

### Enums

Pigeon supports enum generation in class fields. For example:
```dart
enum State {
pending,
success,
error,
}

class StateResult {
String? errorMessage;
State? state;
}

@HostApi()
abstract class Api {
StateResult queryState();
}
```

### Primitive Data-types

Prior to version 1.0 all arguments to API methods had to be wrapped in a class, now they can be used directly. For example:

```dart
@HostApi()
abstract class Api {
Map<String?, int?> makeMap(List<String?> keys, List<String?> values);
}
```

### TaskQueues

When targeting a Flutter version that supports the
[TaskQueue API](https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-kotlin-tab#channels-and-platform-threading)
the threading model for handling HostApi methods can be selected with the
`TaskQueue` annotation:

```dart
@HostApi()
abstract class Api2Host {
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
int add(int x, int y);
}
```

### Error Handling

#### Kotlin, Java and Swift

All Host API exceptions are translated into Flutter `PlatformException`.
* For synchronous methods, thrown exceptions will be caught and translated.
* For asynchronous methods, there is no default exception handling; errors should be returned via the provided callback.

To pass custom details into `PlatformException` for error handling, use `FlutterError` in your Host API.
For example:

```kotlin
// Kotlin
class MyApi : GeneratedApi {
// For synchronous methods
override fun doSomething() {
throw FlutterError('error_code', 'message', 'details')
}

// For async methods
override fun doSomethingAsync(callback: (Result<Unit>) -> Unit) {
callback(Result.failure(FlutterError('error_code', 'message', 'details'))
}
}
```

#### Objective-C and C++

Likewise, Host API errors can be sent using the provided `FlutterError` class (translated into `PlatformException`).

For synchronous methods:
* Objective-C - Assign the `error` argument to a `FlutterError` reference.
* C++ - Return a `FlutterError` directly (for void methods) or within an `ErrorOr` instance.

For async methods:
* Both - Return a `FlutterError` through the provided callback.

#### Handling the errors

Then you can implement error handling on the Flutter side:

```dart
// Dart
void doSomething() {
try {
myApi.doSomething()
} catch (PlatformException e) {
if (e.code == 'error_code') {
// Perform custom error handling
assert(e.message == 'message')
assert(e.details == 'details')
}
}
}
```
denotes APIs that live in Flutter but are invoked from the host platform.
[Example](./example/README.md#FlutterApi_Example).

## Feedback

File an issue in [flutter/flutter](https://github.com/flutter/flutter) with the
word "pigeon" in the title.
File an issue in [flutter/flutter](https://github.com/flutter/flutter) with
"[pigeon]" at the start of the title.
Loading