Giter VIP home page Giter VIP logo

skyost / bonsoir Goto Github PK

View Code? Open in Web Editor NEW
100.0 7.0 43.0 3.9 MB

A Zeroconf library that allows you to discover network services and to broadcast your own. Based on Apple Bonjour and Android NSD.

Home Page: http://bonsoir.skyost.eu/

License: Other

Kotlin 12.03% Swift 8.71% Objective-C 0.01% Dart 52.77% Ruby 1.17% CMake 6.58% C++ 17.97% C 0.61% Shell 0.16%
network zeroconf bonjour nsd flutter package broadcast discover android-nsd zeroconf-library

bonsoir's Introduction

 

Bonsoir is a Zeroconf library that allows you to discover network services and to broadcast your own. It's based on Android NSD and on Apple's popular framework Bonjour. In fact, Bonsoir can be translated into Good evening (and Bonjour into Good morning or Good afternoon depending on the current moment of the day).

Pub Likes Pub Popularity Pub Points Maintained with Melos

Preview

Bonsoir preview

Installation

See how to install on the Bonsoir website.

Code snippet

Here is how you can broadcast your service using Bonsoir :

// Let's create our service !
BonsoirService service = BonsoirService(
  name: 'My wonderful service', // Put your service name here.
  type: '_wonderful-service._tcp', // Put your service type here. Syntax : _ServiceType._TransportProtocolName. (see http://wiki.ros.org/zeroconf/Tutorials/Understanding%20Zeroconf%20Service%20Types).
  port: 3030, // Put your service port here.
);

// And now we can broadcast it :
BonsoirBroadcast broadcast = BonsoirBroadcast(service: service);
await broadcast.ready;
await broadcast.start();

// ...

// Then if you want to stop the broadcast :
await broadcast.stop();

And here is how you can search for a broadcasted service :

// This is the type of service we're looking for :
String type = '_wonderful-service._tcp';

// Once defined, we can start the discovery :
BonsoirDiscovery discovery = BonsoirDiscovery(type: type);
await discovery.ready;

// If you want to listen to the discovery :
discovery.eventStream!.listen((event) { // `eventStream` is not null as the discovery instance is "ready" !
  if (event.type == BonsoirDiscoveryEventType.discoveryServiceFound) {
    print('Service found : ${event.service.toJson()}')
    event.service!.resolve(discovery.serviceResolver); // Should be called when the user wants to connect to this service.
  } else if (event.type == BonsoirDiscoveryEventType.discoveryServiceResolved) {
    print('Service resolved : ${event.service.toJson()}')
  } else if (event.type == BonsoirDiscoveryEventType.discoveryServiceLost) {
    print('Service lost : ${event.service.toJson()}')
  }
});

// Start the discovery **after** listening to discovery events :
await discovery.start();

// Then if you want to stop the discovery :
await discovery.stop();

If you want a full example, don't hesitate to check this one on Github.

Contributions

You have a lot of options to contribute to this project ! You can :

bonsoir's People

Contributors

adidevsoft avatar dominikstarke avatar heiha100 avatar lsegal avatar nicholasspencer avatar nikolaj808 avatar piero512 avatar raghavsatyadev avatar ryoheitomiyama avatar skyost avatar tonym128 avatar victorkifer avatar woody-lwd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

bonsoir's Issues

Crash when pushing the app into the background and pulling it back into the foreground

Describe the bug
Crash when pushing the app into the background and pulling it back into the foreground

To Reproduce
Steps to reproduce the behavior:

  1. build a simple app with the following code, I use Bonsoir 4.0.0
import 'package:bonsoir/bonsoir.dart'; // 4.0.0
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';

final logger = Logger();

void main() {
  runApp(const BonsoirApp());
}

class BonsoirApp extends StatelessWidget {
  const BonsoirApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Simple Flutter App'),
        ),
        body: FutureBuilder(
          future: BonsoirDiscoverRepo.instance.init(),
          builder: (ctx, snapShot) {
            if (snapShot.connectionState == ConnectionState.done) {
              return const Text('discovering');
            } else {
              return const Text('waiting');
            }
          },
        ),
      ),
    );
  }
}

class BonsoirDiscoverRepo {
  static BonsoirDiscoverRepo get instance {
    _instance ??= BonsoirDiscoverRepo._internal();
    return _instance!;
  }

  static BonsoirDiscoverRepo? _instance;
  BonsoirDiscovery? _discover;

  BonsoirDiscoverRepo._internal();

  Future<void> init() async {
    if (_discover == null) {
      _discover = BonsoirDiscovery(type: '_http._tcp');
      await _discover!.ready;
      if (_discover!.eventStream == null) {
        return;
      }
      _discover!.eventStream!
          .where((event) => event.service?.name.contains('IGD-') ?? false)
          .listen(
        (event) async {
          if (event.type == BonsoirDiscoveryEventType.discoveryServiceFound) {
            event.service?.resolve(_discover!.serviceResolver);
          } else if (event.type ==
              BonsoirDiscoveryEventType.discoveryServiceResolved) {
            logger.d('resolve: ${event.service?.toJson()}');
          } else if (event.type ==
              BonsoirDiscoveryEventType.discoveryServiceLost) {
            logger.d('lost: ${event.service?.toJson()}');
          }
        },
      );
      await _discover!.start();
      logger.d('start discover and put event to lower stream');
    } else {
      logger.d('already start discover');
    }
  }
}
  1. run the app with this command
fvm flutter run -d <my_iPhone_device_id> --release
  1. Push the app into the background
  2. Pulling the app into the foreground
  3. The App Crashed.

Expected behavior
Should not crash, when I comment out the code inside Future<void> init() async {}, it did not crash

Screenshots
N/A

Desktop (please complete the following information):
Not Testing

Smartphone (please complete the following information):

  • Device: iPhone11
  • OS: iOS16.6.1

Additional context
I am not sure if I'm using Bonsoir in the correct way. My use case is the following:

  1. When a user holds my app and enters a room, the app can display devices broadcast its service name with the prefix MY_DEVICE_PREFIX
  2. When a user holds my app and leaves a room, the app can display the remaining devices broadcast its service name with the prefix MY_DEVICE_PREFIX (which could be none)

If I understand Bonsoir's behavior correctly, I think the above requirements can be imp. as a singleton. However, even if I update
Bonsoir to 4.0.0, the discovering behavior seems still not stable. I may open another issue to describe it. Thank you.

Phone asking for network permission, but I'm not using Bonsoir

I could be wrong about this, but I'm trying to track down this problem.

I have Bonsoir included in a shared lib. In one app I do use Bonsoir, but in another I never use it. But iOS is still asking for permission to view the local network? Does that make any sense? I'll keep investigating.

Unable to compile when using master containing bug fix #17

Describe the bug
The fix for bug #17 does not compile in Kotlin 1.4.32-release-Studio4.1-1 with compile errors.

To Reproduce
Steps to reproduce the behavior:

  1. Created new Flutter app using Android Studio.
  2. Install flutter_cast (https://github.com/jonathantribouharet/flutter_cast) with updated pubspec.yaml file to use commit 58ca71f of the Bonsoir library which contains the #17 bug fix.
  3. Build the Flutter app
  4. See error;
    e: /Users/dev/Developer/flutter/.pub-cache/git/Bonsoir-58ca71fc769725fff4346caa7422a66ff3a62e20/bonsoir/android/src/main/kotlin/fr/skyost/bonsoir/SuccessObject.kt: (62, 33): None of the following functions can be called with the arguments supplied:
    @InlineOnly public inline fun String(stringBuffer: StringBuffer): String defined in kotlin.text
    @InlineOnly public inline fun String(stringBuilder: StringBuilder): String defined in kotlin.text
    @InlineOnly public inline fun String(bytes: ByteArray): String defined in kotlin.text
    @InlineOnly public inline fun String(chars: CharArray): String defined in kotlin.text

Expected behavior
Expect the Flutter app to compile and build successfully.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: MacOS Big Sur version 11.4
  • Browser:
  • Version:

Smartphone (please complete the following information):

  • Device: Google Pixel 5
  • OS: Android
  • Browser:
  • Version:

Additional context

Flutter Doctor:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.2.2, on macOS 11.4 20F71 darwin-x64, locale en-NZ)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.1)
[✓] VS Code (version 1.56.2)

Discovery fails on iOS 17

Describe the bug
Fails to discover services found by the same code on iOS 15.

To Reproduce
Steps to reproduce the behavior:

  1. Create a browser
  2. Start discovery

Expected behavior
Services discovered the same way as on previous iOS versions

Smartphone (please complete the following information):

  • Device: iPhone SE 2020
  • OS: iOS 17.1.1

Additional context
Debug logs:

nw_txt_record_create_with_bytes txt_record cannot have length 0, dumping backtrace:
        [arm64] libnetcore-3762.42.2
    0   Network                             0x00000001b5eef370 __nw_create_backtrace_string + 192
    1   Network                             0x00000001b5fe09ec nw_txt_record_create_with_bytes + 1780
    2   Network                             0x00000001b5fdc548 95F61053-D76D-3530-919D-316D4B349BD6 + 7255368
    3   libsystem_dnssd.dylib               0x000000021ed6a254 FC5FADFF-0991-3401-8C8F-C9F585E203B9 + 4692
    4   libsystem_dnssd.dylib               0x000000021ed6b000 DNSServiceProcessResult + 728
    5   libdispatch.dylib                   0x000000010142ab34 _dispatch_client_callout + 20
    6   libdispatch.dylib                   0x000000010142dc20 _dispatch_continuation_pop + 676
    7   libdispatch.dylib                   0x0000000101445078 _dispatch_source_latch_and_call + 480
    8   libdispatch.dylib                   0x0000000101443acc _dispatch_source_invoke + 860
    9   libdispatch.dylib                   0x0000000101434d6c _dispatch_workloop_invoke + 2336
    10  libdispatch.dylib                   0x00000001014405f8 _dispatch_root_queue_drain_deferred_wlh + 328
    11  libdispatch.dylib                   0x000000010143fc2c _dispatch_workloop_worker_thread + 444
    12  libsystem_pthread.dylib             0x000000021ee1d964 _pthread_wqthread + 288
    13  libsystem_pthread.dylib             0x000000021ee1da04 start_wqthread + 8

BonsoirDiscovery has null eventStream even after ready

Describe the bug
The code from
https://pub.dev/packages/bonsoir

BonsoirDiscovery discovery = BonsoirDiscovery(type: type);
await discovery.ready;
discovery.eventStream!.listen((event) {
  ...
}

is sometimes giving me a null eventStream on Android

To Reproduce
Steps to reproduce the behavior:
1 - Do not create any services
2 - Create a discovery
3 - check the value of eventStream after ready on android.

Expected behavior
eventStream is never null
Screenshots
If applicable, add screenshots to help explain your problem.

Smartphone (please complete the following information):

  • Device: Pixel 3
  • OS: Android 12

It is not possible to display only the video supported devices in discovery

Question: In my Flutter application, I am using Bonsoir 2.0.0 to fetch the available devices. I use service type as _googlecast._tcp. This displays the available devices which are running under the same network. The problem is, it displays both the video and audio supported devices. But I wanted only the video supported devices. How to filter only the video devices?

The listed audio devices service info has shown below. Which is same a video device service info. So it became hard to filter audio devices and list only video devices.

{
   "service.name":"Google-Nest-Mini-f085fd9042490e01a04233c8bf333778",
   "service.type":"_googlecast._tcp",
   "service.port":8009,
   "service.attributes":{
      "bs":"FA8FCA78A8D0",
      "cd":"5810A9FF37B193370398E3678764CF83",
      "rs":"",
      "st":"0",
      "md":"Google Nest Mini",
      "fn":"Office speaker",
      "nf":"1",
      "ic":"/setup/icon.png",
      "id":"f085fd9042490e01a04233c8bf333778",
      "rm":"",
      "ca":"199172",
      "ve":"05"
   },
   "service.ip":"192.168.2.66"
}

Appreciate your response with suggestion.

Library version

  • bonsoir: ^2.0.0
  • Flutter 3.7.6
  • Dart 2.19.3

Google Pixel 7 Pro with 2.0.0 cannot found devices.

Describe the bug
Pixel 7 Pro never found bonjour service.

Smartphone (please complete the following information):

  • Device: Google Pixel 7 Pro
  • OS: Android 14
  • bonsoir version: "bonsoir: ^2.0.0"

Additional context
There is sample code in my app.

BonsoirDiscovery _discovery = BonsoirDiscovery(type: "_dp_Example._tcp."); await _discovery.ready; await _discovery.start(); _discovery.eventStream?.listen((BonsoirDiscoveryEvent event) async { debugPrint('bonjour. type: ${event.type}, service: ${event.service}, isResolved: ${event.isServiceResolved}.'); if (event.type == BonsoirDiscoveryEventType.discoveryServiceResolved) { final service = event.service; _action(service); } }, onError: (e) { debugPrint(e.toString()); });

The debugPrint only print: bonjour. type: BonsoirDiscoveryEventType.discoveryStarted, service: null, isResolved: false. Even other devices can correctly catch right services.

Federated plugin.

Is your feature request related to a problem? Please describe.
I am working on a solution powered by Flutter where I use this plugin to discover services in network. I would like to port my app to desktop platforms

Describe the solution you'd like
A refactoring of this plugin where the interface is consistent with the Federated Plugin specification.

Describe alternatives you've considered
I've considered the posibility of just using the package multicast_dns on platforms where the is no Zeroconf daemon running (like on Linuxes without Avahi, or Windows), and on Linux try to locate an Avahi daemon. But I would like to keep my code the same across platforms.

Additional context
This would help a lot in my work, and I expect it would help me by allowing me to write a Linux-specific federated plugin. (one that uses Avahi directly, or falls back to multicast_dns, instead of the deprecated and not always working libdns_sd compat layer ).

About transition to federated plugin

This issue has been created to keep talking about #9. So, as I said in #9 (comment), I have done refactoring in cea115a and 5556a7b :

  • Moved all source files into the src directory.
  • Renamed BonsoirPlatformEvents into BonsoirAction (to avoid confusions with BonsoirEvent).
  • Merged MethodChannelBonsoir and BonsoirPlatformInterface because I thought it was not needed to have an abstraction for BonsoirPlatformInterface (but I'm not that sure about this one).

What do you think about it @Piero512 ?

Crash in `onStartDiscoveryFailed`

Describe the bug

java.lang.IllegalArgumentException: listener not registered
  at com.android.internal.util.Preconditions.checkArgument (Preconditions.java:52)
  at android.net.nsd.NsdManager.getListenerKey (NsdManager.java:467)
  at android.net.nsd.NsdManager.stopServiceDiscovery (NsdManager.java:601)
  at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener.dispose (BonsoirDiscoveryListener.kt:160)
  at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener.dispose$default (BonsoirDiscoveryListener.kt:158)
  at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener.onStartDiscoveryFailed (BonsoirDiscoveryListener.kt:79)
  at android.net.nsd.NsdManager$ServiceHandler.handleMessage (NsdManager.java:383)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:246)
  at android.os.HandlerThread.run (HandlerThread.java:67)

Issue #26 still exists even after my previous fix. Although I find that fix still useful.

Services no longer being discovered after restarting/re-creating BonsoirDiscovery

Describe the bug
Services no longer being discovered after restarting/re-creating BonsoirDiscovery

To Reproduce
Steps to reproduce the behavior:

  1. Create BonsoirDiscovery
  2. Listen to eventStream
  3. Disconnect device from internet
  4. Reconnect device to internet
  5. Re-create/restart discovery by calling .stop() on the existing instance and creating a new one

Expected behavior
I'm expecting to see services getting discovered by Bonsoir upon restarting the discovery but nothing happens. When not restarting discovery the services are also not discovered upon reconnecting to the network.

Screenshots
If applicable, add screenshots to help explain your problem.

Smartphone (please complete the following information):

  • Device: Motorola e6 play, huawei p20 pro, oneplus 8 pro
  • OS: Android 8, 9, 10

Additional context
Add any other context about the problem here.

Unable to build to Android in Bonsoir version 1.0.1

Describe the bug
Unable to build to Android in the new version (1.0.1) of Bonsoir.

To Reproduce
Steps to reproduce the behavior:

  1. flutter create bug_test
  2. cd bug_test
  3. flutter pub add bonsoir
  4. flutter run

Screenshot
image

Device

  • OnePlus A6003 (android-arm64)

Can't get ip address from discovery service

Hi

I have a problem with discovery services with this plugin. If I use Debug, I see all attributes of discoveredServices, but if I try to get ip I can't. If Try to save this to variable with var ipAddress = discoveredServices[0].ip I can't use, but can use name, type, port and attributes, but can't use ip.

I don't know if this is a bug or not. Why? Can help me? Thanks.

Apps need NSBluetoothPeripheralUsageDescription?

I have Bonsoir as part of a shared lib I use in my apps. Just now, I uploaded to TestFlight and I got an email asking for NSBluetoothPeripheralUsageDescription. No big deal, I can add it, but I was wondering what's triggering that.

I'm not calling Bonsoir in this project. It's just included in my lib. Is there something you can do to avoid this?

Better error handling when discovery fails because type ends with .local

I'm currently transitioning from "multicast_dns" where the type needed to end with "._tcp.local" for the service we are using.

The discovery with that type failed with error code 0, which wasn't really helpful to figure out the issue.

People might try this package because of referrals like in this comment, and if they have the same problem as me it could be helpful for them to know that they have to drop the ".local" suffix for discovery to succeed :)

java.lang.IllegalArgumentException: listener not registered

Describe the bug

I tried to run my app (which uses broadcast) but I got this issue:

E/AndroidRuntime(16031): FATAL EXCEPTION: NsdManager
E/AndroidRuntime(16031): Process: be.epireyn.werewolf, PID: 16031
E/AndroidRuntime(16031): java.lang.IllegalArgumentException: listener not registered
E/AndroidRuntime(16031): 	at com.android.internal.util.Preconditions.checkArgument(Preconditions.java:50)
E/AndroidRuntime(16031): 	at android.net.nsd.NsdManager.getListenerKey(NsdManager.java:467)
E/AndroidRuntime(16031): 	at android.net.nsd.NsdManager.unregisterService(NsdManager.java:541)
E/AndroidRuntime(16031): 	at fr.skyost.bonsoir.broadcast.BonsoirRegistrationListener.dispose(BonsoirRegistrationListener.kt:90)
E/AndroidRuntime(16031): 	at fr.skyost.bonsoir.broadcast.BonsoirRegistrationListener.dispose$default(BonsoirRegistrationListener.kt:88)
E/AndroidRuntime(16031): 	at fr.skyost.bonsoir.broadcast.BonsoirRegistrationListener.onRegistrationFailed(BonsoirRegistrationListener.kt:66)
E/AndroidRuntime(16031): 	at android.net.nsd.NsdManager$ServiceHandler.handleMessage(NsdManager.java:405)
E/AndroidRuntime(16031): 	at android.os.Handler.dispatchMessage(Handler.java:107)
E/AndroidRuntime(16031): 	at android.os.Looper.loop(Looper.java:214)
E/AndroidRuntime(16031): 	at android.os.HandlerThread.run(HandlerThread.java:67)

To Reproduce

Steps to reproduce the behavior:
Run this code on Android:

void _toggleBroadcast({bool kill = false}) async {
    if (_broadcast == null && !kill) {
      _broadcast = BonsoirBroadcast(
          service: BonsoirService(
              name: 'Werewolves', type: BROADCAST_TYPE, port: 6665));
      await _broadcast.ready;
      await _broadcast.start();
    } else {
      if (_broadcast != null) await _broadcast.stop();
      _broadcast = null;
    }
  }

Expected behavior

To be able to send broadcast messages without crashes

Desktop (please complete the following information):

Windows 10

Smartphone (please complete the following information):

HMD Global Nokia 7 plus

Crash reported in Play Store

Describe the bug

java.lang.IllegalArgumentException: listener not registered
  at com.android.internal.util.Preconditions.checkArgument (Preconditions.java:52)
  at android.net.nsd.NsdManager.getListenerKey (NsdManager.java:467)
  at android.net.nsd.NsdManager.stopServiceDiscovery (NsdManager.java:601)
  at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener.dispose (BonsoirDiscoveryListener.kt:160)
  at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener.dispose$default (BonsoirDiscoveryListener.kt:158)
  at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener.onStartDiscoveryFailed (BonsoirDiscoveryListener.kt:79)
  at android.net.nsd.NsdManager$ServiceHandler.handleMessage (NsdManager.java:383)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:246)
  at android.os.HandlerThread.run (HandlerThread.java:67)

Lost service not removed from list

This part of discovery.dart doesn't actually remove a lost service from the _resolvedServices list :
if (event.type == BonsoirDiscoveryEventType.DISCOVERY_SERVICE_LOST) { _resolvedServices.remove(event.service); notifyListeners(); }

can't build for ios

/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.2+1/ios/Classes/BonsoirServiceBrowserDelegate.swift:1:1: Expressions are not allowed at the top level

/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.2+1/ios/Classes/BonsoirServiceBrowserDelegate.swift:1:1: Use of unresolved operator '../../'

Windows support

Is your feature request related to a problem? Please describe.
No.

Describe the solution you'd like
Be able to use this package in my flutter desktop windows app

Describe alternatives you've considered
...
Additional context
I'll be very grateful if you add windows support for this package.

Thanks!!!

Does not work on Google Pixel 3 XL Phone

Describe the bug
I tried running the example on my android phone, but it wasn't able to receive any broadcast messages. Searching around online, I think this needs to also use the WIFI multicast permission and acquire a multicast lock. This stack overflow answer seems like the issue I'm having: https://stackoverflow.com/a/53630778

To Reproduce
Steps to reproduce the behavior:

  1. Try example on Google Pixel 3 phone
  2. Never receive any broadcast messages

Expected behavior
To be able to receive broadcast messages

Desktop (please complete the following information):
Macbook pro as the source of the broadcast

Smartphone (please complete the following information):
Google Pixel 3 XL running Android 10

Additional context
The link above has code that should fix the problem. Another commenter stated that it fixed the issue for them. They were also on a Google Pixel 3 phone.

Use of undeclared identifier 'BonsoirPlugin'

Describe the bug
The GeneratedPluginRegistrant for an iOS or macOS app cannot locate the BonsoirPlugin class. It looks like, as of two days ago, the previous objc BonsoirPlugin class was removed and only the SwiftBonsoirPlugin class exists now. I believe the plugin information in the pubspec of bonsoir_darwin needs to be updated to reflect the correct plugin class name.

To Reproduce
Steps to reproduce the behavior:

  1. Add bonsoir to an iOS or macOS flutter app.
  2. Run the app.
  3. Observe the generated registrant.

Expected behavior
The correct SwiftBonsoirPlugin is added to the plugin registry.

Desktop (please complete the following information):

  • OS: macOS

Smartphone (please complete the following information):

  • OS: iOS (any version)

App crashes when using Bonsoir package

Describe the bug
I installed the package via pubspec.yaml and after running the app it crashes with the following results:
(lldb) 2023-10-18 21:23:08.597088+0530 Runner[338:5105] [VERBOSE-2:FlutterDarwinContextMetalImpeller.mm(37)] Using the Impeller rendering backend.

  • thread 1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x0000000000000000
    error: memory read failed for 0x0
    Target 0: (Runner) stopped.

Smartphone:

  • Device: iPhone 7
  • OS: iOS 15.7.9

Xcode

  • Version: 15

Flutter

  • Version: 3.13.7

Dart

  • Version: 3.1.3

Screenshot

Screenshot 2023-10-18 at 10 31 52 PM

iPhone debug NSNetServicesErrorCode -72008

Describe the bug

  1. “NSNetServicesErrorCode": -72008” occurs when i debug in iphone real machine
  2. Android real machine and iOS simulator work fine

To Reproduce
Steps to reproduce the behavior:

  1. debug in iPhone
  2. start discovery
  3. error will occurs: [discovery] [45898] Bonsoir has encountered an error during discovery : ["NSNetServicesErrorDomain": 10, "NSNetServicesErrorCode": -72008]

error info
[discovery] [45898] Bonsoir has encountered an error during discovery : ["NSNetServicesErrorDomain": 10, "NSNetServicesErrorCode": -72008]

code

import 'package:get/get.dart';
import 'package:flutter/services.dart';
import 'package:logger/logger.dart';
import 'package:bonsoir/bonsoir.dart';
import 'package:device_info_plus/device_info_plus.dart';

var log = Logger(
  printer: PrettyPrinter(),
);

class MdnsSevice {
  String name;
  String type;
  int port;
  String ip;
  MdnsSevice(
    this.name,
    this.type,
    this.port,
    this.ip,
  );

  MdnsSevice.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        type = json['type'],
        port = json['port'],
        ip = json['ip'];

  Map<String, dynamic> toJson() => {
        'name': name,
        'type': type,
        'port': port,
        'ip': ip,
      };
}

class DeviceUserPageController extends GetxController {
  late BonsoirDiscovery discovery;
  late BonsoirBroadcast broadcast;
  late Map deviceInfo;

  final services = <MdnsSevice?>[];
  bool initialStart = true;
  bool scanning = false;

  init() async {
    await getDeviceInfo();
    await newMdnsDevice();
    await startDiscovery();
  }

  getDeviceInfo () async {
    final deviceInfoPlugin = DeviceInfoPlugin();
    final deviceInfoRaw = await deviceInfoPlugin.deviceInfo;
    deviceInfo = deviceInfoRaw.toMap();
    update();
  }

  newMdnsDevice() async {
    BonsoirService service = BonsoirService(
      name: "${deviceInfo['device'] ?? deviceInfo['name']}",
      type: '_mydevice._tcp',
      port: 3030,
    );
    broadcast = BonsoirBroadcast(service: service);
    await broadcast.ready;
    await broadcast.start();
  }

  stopMdnsDevice() async {
    await broadcast.stop();
  }

  Future<void> startDiscovery() async {
    log.d('startDiscovery', scanning);
    if (scanning) return;
    services.clear();
    scanning = true;

    String type = '_mydevice._tcp';
    discovery = BonsoirDiscovery(type: type);
    await discovery.ready;
    discovery.eventStream?.listen((event) {
      if (event.type == BonsoirDiscoveryEventType.DISCOVERY_SERVICE_RESOLVED) {
        var service = MdnsSevice.fromJson(event.service!.toJson(prefix: ''));
        services.add(service);
        update();
      } else if (event.type == BonsoirDiscoveryEventType.DISCOVERY_SERVICE_LOST) {
        var service = event.service!.toJson(prefix: '');
        services.removeWhere((item) => item?.name == service['name']);
        update();
      } else {
        log.d('other event',  event.type);
      }
    });
    await discovery.start();
    update();
  }

  Future<void> stopDiscovery() async {
    if (!scanning) return;
    services.clear();
    scanning = false;
    await discovery.stop();
    update();
  }

  @override
  void onInit() {
    init();
    super.onInit();
  }
}

Linux version

Hi thanks for this package,
do you thinks it's possible to have a linux version of this package ?

Invalid service port returned since v4

Describe the bug
I've upgraded from v3 to latest version and try the github version, since the service port received is not valid.
The service port is fixed, I use Bonsoir to detect my Chromecast and the service port is either 8008 or 8009.

To Reproduce
Steps to reproduce the behavior:

import 'dart:async';

import 'package:bonsoir/bonsoir.dart';

import 'device.dart';

const _domain = '_googlecast._tcp';

class CastDiscoveryService {
  static final CastDiscoveryService _instance = CastDiscoveryService._();
  CastDiscoveryService._();

  factory CastDiscoveryService() {
    return _instance;
  }

  Future<List<CastDevice>> search({Duration timeout = const Duration(seconds: 5)}) async {
    final results = <CastDevice>[];

    final discovery = BonsoirDiscovery(type: _domain);
    await discovery.ready;

    discovery.eventStream!.listen((event) {
      if (event.type == BonsoirDiscoveryEventType.discoveryServiceFound) {
        event.service?.resolve(discovery.serviceResolver);
      } else if (event.type == BonsoirDiscoveryEventType.discoveryServiceResolved) {
        if (event.service == null || event.service?.attributes == null) {
          return;
        }

        final port = event.service?.port;
        final host = event.service?.toJson()['service.ip'] ?? event.service?.toJson()['service.host'];

        String name = [
          event.service?.attributes?['md'],
          event.service?.attributes?['fn'],
        ].whereType<String>().join(' - ');
        if (name.isEmpty) {
          name = event.service!.name;
        }

        if (port == null || host == null) {
          return;
        }

        results.add(
          CastDevice(
            serviceName: event.service!.name,
            name: name,
            port: port,
            host: host,
            extras: event.service!.attributes ?? {},
          ),
        );
      }
    }, onError: (error) {
      print('[CastDiscoveryService] error ${error.runtimeType} - $error');
    });

    await discovery.start();
    await Future.delayed(timeout);
    await discovery.stop();

    return results.toSet().toList();
  }
}

Expected behavior
To return the right port.

Desktop (please complete the following information):

  • Device: Mac Application
  • OS: MacOS 13.4
  • Version 4.0.0 and master

Additional context

Here the data receive with v3

{
service.name: Chromecast-Ultra-f6d7e30603081403bac91241a432ea99,
service.type: _googlecast._tcp,
service.port: 8009,
service.attributes: {ve: 05, cd: 3AB1A3FADCE0590F5307B485A9AA6EA4, rm: 6E287B10CA7872D3, ic: /setup/icon.png, ca: 201221, nf: 2, st: 1, md: Chromecast Ultra, bs: FA8FCA770C52, fn: My Cast, id: f6d7e30603081403bac91241a432ea99, rs: My Delivery KDS}, service.ip: 192.168.1.54
}

Here the data receive with v4

{
service.name: Chromecast-Ultra-f6d7e30603081403bac91241a432ea99,
service.type: _googlecast._tcp,
service.port: 18719,
service.attributes: {rm: 6E287B10CA7872D3, ve: 05, ca: 201221, md: Chromecast Ultra, bs: FA8FCA770C52, id: f6d7e30603081403bac91241a432ea99, ic: /setup/icon.png, nf: 2, cd: 3AB1A3FADCE0590F5307B485A9AA6EA4, rs: My Delivery KDS, fn: My Cast, st: 1}, service.host: f6d7e306-0308-1403-bac9-1241a432ea99.local.
}

_TypeError (type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, String>')

Getting this error: Turn on all exceptions.

Exception has occurred.
_TypeError (type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, String>')

/// Creates a new resolved Bonsoir service instance from the given JSON map.
ResolvedBonsoirService.fromJson(Map<String, dynamic> json,
{String prefix = 'service.'})
: this(
name: json['${prefix}name'],
type: json['${prefix}type'],
port: json['${prefix}port'],
attributes: json['${prefix}attributes'],
ip: json['${prefix}ip'],
);

Crash on discovery

Describe the bug
The app crashes on discovery.start();
I call the function to discovery from InitState().

Screenshots
image

Additional context
The aditional Log is
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: PlatformException(discovery_error, Bonsoir failed to start discovery, 0, null)

Im using a Xiaomi Note 10 Pro to test with VSCode on Windows.

[Android] Crash: Methods marked with @UiThread must be executed on the main thread

Describe the bug
Firstly, thank you for your time :)

Crash when executing

broadcast.ready.whenComplete(broadcast.start)
        .whenComplete(() => setState(() => /*...*/));

in callback onChange of Widget Switch,~~ along with package Camera.~~

Crashed with widget:

StreamBuilder<BonsoirBroadcastEvent>(
  stream: broadcast?.eventStream,
  builder: (context, snapshot) {
    if (snapshot.data?.type ==
        BonsoirBroadcastEventType.BROADCAST_STARTED) {
      var port = snapshot.data?.service?.port;
      return Text("Service running at port: $port");
    }
    return SizedBox();
  },
)

~~
Here's the log:

java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread. Current thread: NsdManager
        at io.flutter.embedding.engine.FlutterJNI.ensureRunningOnMainThread(FlutterJNI.java:1415)
        at io.flutter.embedding.engine.FlutterJNI.dispatchPlatformMessage(FlutterJNI.java:1036)
        at io.flutter.embedding.engine.dart.DartMessenger.send(DartMessenger.java:282)
        at io.flutter.embedding.engine.dart.DartExecutor$DefaultBinaryMessenger.send(DartExecutor.java:452)
        at io.flutter.embedding.engine.dart.DartExecutor.send(DartExecutor.java:212)
        at io.flutter.plugin.common.EventChannel$IncomingStreamRequestHandler$EventSinkImplementation.success(EventChannel.java:251)
        at fr.skyost.bonsoir.broadcast.BonsoirRegistrationListener.onServiceRegistered(BonsoirRegistrationListener.kt:65)
        at android.net.nsd.NsdManager$ServiceHandler.handleMessage(NsdManager.java:439)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:236)
        at android.os.HandlerThread.run(HandlerThread.java:67)
2022-04-19 19:30:17.118 8176-8176/com.**.** D/DecorView[]: onWindowFocusChanged hasWindowFocus false
2022-04-19 19:30:17.119 8176-8176/com.**.** I/DecorView: showOrHideHighlightView: hasFocus=false; winMode=4; isMrgNull=false
2022-04-19 19:30:17.179 8176-8267/com.**.** I/Process: Sending signal. PID: 8176 SIG: 9

To Reproduce
Steps to reproduce the behavior:

The code crashed: (might be some help)

import 'package:bonsoir/bonsoir.dart';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

List<CameraDescription>? cameras;

/// main.dart
void main() {
  // ...
  initCameras();
  runApp(const MyApp());
}
/// end of main.dart

Future<String?> initCameras() async {
  cameras = await availableCameras();
  if (cameras!.isEmpty) {
    return null;
  } else if (cameras!.length == 1) {
    return "camera";
  } else {
    return "${cameras!.length}x-camera";
  }
}

class CameraDemoPage extends StatefulWidget {
  const CameraDemoPage({Key? key}) : super(key: key);

  @override
  _CameraDemoPageState createState() => _CameraDemoPageState();
}

class _CameraDemoPageState extends State<CameraDemoPage>
    with WidgetsBindingObserver {
  bool _switchingOn = false;

  BonsoirService service = const BonsoirService(
    name: 'streamlab2',
    type: '_stream-lab2._tcp',
    port: 11451,
  );
  late BonsoirBroadcast broadcast;

  final _uriTextController = TextEditingController();

  int _cameraId = 0;
  CameraController? _cameraController;
  Future<void>? _initializeControllerFuture;

  @override
  void initState() {
    super.initState();

    // broadcast = BonsoirBroadcast(service: service);
    // broadcast!.ready.whenComplete(broadcast!.start);

    if (cameras != null) {
      onNewCameraSelected(cameras![_cameraId]);
    }
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    // App state changed before we got the chance to initialize.
    if (_cameraController == null || !_cameraController!.value.isInitialized) {
      return;
    }
    if (state == AppLifecycleState.inactive) {
      _cameraController?.dispose();
    } else if (state == AppLifecycleState.resumed) {
      if (_cameraController != null) {
        onNewCameraSelected(_cameraController!.description);
      }
    }
  }

  @override
  void dispose() {
    _cameraController?.dispose();
    broadcast?.stop();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            Expanded(
              // CAMERA 0
              child: Padding(
                padding: const EdgeInsets.all(10),
                child: Stack(
                    alignment: AlignmentDirectional.bottomStart,
                    children: [
                      // Overlapping Tips
                      Padding(
                          padding: const EdgeInsets.all(5),
                          child: StreamBuilder<BonsoirBroadcastEvent>(
                            stream: broadcast?.eventStream,
                            builder: (context, snapshot) {
                              if (snapshot.data?.type ==
                                  BonsoirBroadcastEventType.BROADCAST_STARTED) {
                                var port = snapshot.data?.service?.port;
                                return Text("Service running at port: $port");
                              }
                              return SizedBox();
                            },
                          )),
                      Container(
                          color: Colors.grey,
                          child: FutureBuilder<void>(
                            future: _initializeControllerFuture,
                            builder: (context, snapshot) {
                              if (snapshot.connectionState ==
                                  ConnectionState.done) {
                                // If the Future is complete, display the preview.
                                return CameraPreview(_cameraController!);
                              } else {
                                // Otherwise, display a loading indicator.
                                return const Center(
                                  child: CircularProgressIndicator(),
                                );
                              }
                            },
                          )),
                    ]),
              ),
            ),
            Row(
              children: [
                Expanded(
                  child: TextField(
                    decoration: const InputDecoration(
                        icon: Icon(Icons.link_rounded), hintText: "video uri"),
                    controller: _uriTextController,
                  ),
                ),
                Column(
                  children: [
                    Row(
                      children: [
                        Switch.adaptive(
                          value: !broadcast.isStopped,
                          onChanged: (switchOn) {
                            if (_switchingOn) {
                              return;
                            }
                            if (switchOn) {
                              _switchingOn = true;
                              broadcast = BonsoirBroadcast(service: service);
                              broadcast!.ready
                                  .whenComplete(broadcast!.start)
                                  .whenComplete(() =>
                                      setState(() => _switchingOn = false));
                            } else {
                              broadcast
                                  ?.stop()
                                  .whenComplete(() => setState(() {}));
                            }
                          },
                        ),
                        const Text("Stream"),
                      ],
                    ),
                    // MaterialButton(
                    //   onPressed: () {
                    //     setState(() {
                    //       _isStreaming = !_isStreaming;
                    //     });
                    //
                    //   },
                    //   child: const Text("Stream"),
                    //   elevation: _isStreaming ? 0.8 : 2,
                    // ),
                  ],
                )
              ],
            ),
            Expanded(
              child: Padding(
                padding: const EdgeInsets.all(10),
                child: Container(
                  color: Colors.grey,
                ),
              ),
            )
          ],
        ),
      ),
    );
  }

  void onNewCameraSelected(CameraDescription description) async {
    final previousCameraController = _cameraController;
    // Instantiating the camera controller
    final CameraController cameraController = CameraController(
      description,
      ResolutionPreset.high,
      imageFormatGroup: ImageFormatGroup.jpeg,
    );
    // Dispose the previous controller
    await previousCameraController?.dispose();
    // Replace with the new controller
    if (mounted) {
      setState(() {
        _cameraController = cameraController;
      });
    }
    // Update UI if controller updated
    cameraController.addListener(() {
      if (mounted) setState(() {});
    });
    // Initialize controller
    try {
      _initializeControllerFuture = cameraController.initialize();
      await _initializeControllerFuture;
    } on CameraException catch (e) {
      assert(() {
        print('Error initializing camera: $e');
        return true;
      }());
    }
  }
}

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

ENV

[✓] Flutter (Channel beta, 2.13.0-0.1.pre, on macOS 12.0.1 21A559 darwin-arm,
    locale en-CN)
...
[✓] Android Studio (version 2021.1)

Test device: some tablet with Android 12

android13 can not find any device. android12 11 10 work well

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

entry.value must not be null in SuccessObject

First of all, thanks for the lib!

Describe the bug

I'm getting this crash when discovering devices.

E/AndroidRuntime( 6015): Process: XXX, PID: 6015
E/AndroidRuntime( 6015): java.lang.IllegalStateException: entry.value must not be null
E/AndroidRuntime( 6015): 	at fr.skyost.bonsoir.SuccessObject.getAttributes(SuccessObject.kt:62)
E/AndroidRuntime( 6015): 	at fr.skyost.bonsoir.SuccessObject.serviceToJson(SuccessObject.kt:44)
E/AndroidRuntime( 6015): 	at fr.skyost.bonsoir.SuccessObject.toJson(SuccessObject.kt:25)
E/AndroidRuntime( 6015): 	at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener$onServiceResolved$1.run(BonsoirDiscoveryListener.kt:138)
E/AndroidRuntime( 6015): 	at android.os.Handler.handleCallback(Handler.java:938)
E/AndroidRuntime( 6015): 	at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 6015): 	at android.os.Looper.loop(Looper.java:223)
E/AndroidRuntime( 6015): 	at android.app.ActivityThread.main(ActivityThread.java:7660)
E/AndroidRuntime( 6015): 	at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 6015): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
E/AndroidRuntime( 6015): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

I'm seeing devices being discovered in the logs, so it seems to be a parsing issue with one of those specific devices.

To Reproduce

Just started exploring devices on my network, not sure if it'd be reproducible in others.

Smartphone (please complete the following information):

  • Device: Pixel 3
  • OS: Android 11

Using Flutter and cast on top of Bonsoir.

Exception when discovering an iOS service broadcasted by Android phone

Describe the bug
I created a simple app that either broadcasts an HTTP service or discovers it.
If I broadcast on iOS and discover on Android, everything is smooth, the service is found and resolved.
If I broadcast on Android and discover on iOS, the service is found but an exception is thrown internally when resolving with very little details.

To Reproduce
Steps to reproduce the behavior:

  1. Use the sample project there
  2. Customize the signing organization in the iOS project
  3. Run the app on both an Android and an iOS device, both connected to the same wifi
  4. Click the broadcast button on iOS
  5. Click the discover button on Android
  6. Look at the logs and see the line that says: "Connecting to server ... at ...:8080", indicating that the service has been found and resolved
  7. Restart both apps
  8. Click broadcast on Android
  9. Click discover on iOS

Expected behavior
The same log as before, indicating that the service has been resolved

Actual behavior
An exception is thrown from within Bonsoir's internal when calling resolve, but doesn't seem to be thrown by resolve itself:

PlatformException(discoveryError, Bonsoir has failed to resolve a service : 0, 0, null)

Smartphone (please complete the following information):

  • Device: iPhone 15 Pro and Samsung Galaxy S21 5G
  • OS: iOS 17.1 and Android 13
  • Version 5.0.3

Additional context
At first, I tried to just do the discovery without running any actual server on the broadcaster, but then I wasn't really sure of how far the resolver would actually go, so I ran a small shelf websocket server, just like in my real app, but it didn't change anything. I also thought that maybe iOS had a problem with the HTTP server instead of HTTPS, which is why I added NSAllowsArbitraryLoads:YESto my iOS configuration, but to no avail.
Note that this is a sample project trying to replicate an issue I have in a real project where I don't see any exception but the resolution event is just not received on the discovery side.
Did I forget something?

Example link in the documentation leads to a 404

Describe the bug
I have a weird behavior in iOS discovery and I wanted to test it on a simple example, but the link in the package documentation there to the example does not seem to exist (anymore?).

To Reproduce
Steps to reproduce the behavior:

  1. Go to the package documentation
  2. Click the link to the example
  3. See the 404

Expected behavior
I would like to find a reference example that I can use to test the package on both iOS and Android

Error "Duplicate class kotlin. ... ... ..." after after this package added to flutter application

Summary
Errors of kind "Duplicate class kotlin....." appears when this library is added to the newly created flutter app.

To Reproduce

flutter create mycast
cd mycast
flutter pub add bonsoir
flutter run

Full log

PS C:\1> flutter create mycast
Creating project mycast...
Resolving dependencies in mycast... (1.3s)
Got dependencies in mycast.
Wrote 122 files.

All done!
You can find general documentation for Flutter at: https://docs.flutter.dev/
Detailed API documentation is available at: https://api.flutter.dev/
If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev

In order to run your application, type:

  $ cd mycast
  $ flutter run

Your application code is in mycast\lib\main.dart.

PS C:\1> cd .\mycast\
PS C:\1\mycast> flutter pub add bonsoir
Resolving dependencies... 
+ bonsoir 2.2.0+1
+ bonsoir_android 2.2.0
+ bonsoir_darwin 2.2.0+1
+ bonsoir_platform_interface 2.2.0
  collection 1.17.1 (1.17.2 available)
  matcher 0.12.15 (0.12.16 available)
  material_color_utilities 0.2.0 (0.5.0 available)
+ plugin_platform_interface 2.1.4
  source_span 1.9.1 (1.10.0 available)
  test_api 0.5.1 (0.6.0 available)
Changed 5 dependencies!
PS C:\1\mycast> flutter run
Resolving dependencies... 
  collection 1.17.1 (1.17.2 available)
  matcher 0.12.15 (0.12.16 available)
  material_color_utilities 0.2.0 (0.5.0 available)
  source_span 1.9.1 (1.10.0 available)
  test_api 0.5.1 (0.6.0 available)
Got dependencies!
Launching lib\main.dart on SM A336E in debug mode...

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:checkDebugDuplicateClasses'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
   > Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
     Duplicate class kotlin.internal.jdk8.JDK8PlatformImplementations found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
     Duplicate class kotlin.random.jdk8.PlatformThreadLocalRandom found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
     Duplicate class kotlin.streams.jdk8.StreamsKt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
     Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$1 found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
     Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$2 found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
     Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$3 found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
     Duplicate class kotlin.streams.jdk8.StreamsKt$asSequence$$inlined$Sequence$4 found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
     Duplicate class kotlin.text.jdk8.RegexExtensionsJDK8Kt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
     Duplicate class kotlin.time.jdk8.DurationConversionsJDK8Kt found in modules jetified-kotlin-stdlib-1.8.0 (org.jetbrains.kotlin:kotlin-stdlib:1.8.0) and jetified-kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)

     Go to the documentation to learn how to <a href="d.android.com/r/tools/classpath-sync-errors">[Fix dependency resolution errors](https://github.com/Skyost/Bonsoir/issues/d.android.com/r/tools/classpath-sync-errors)</a>.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 17s
Running Gradle task 'assembleDebug'...                             17.8s
Exception: Gradle task assembleDebug failed with exit code 1

Flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.10.4, on Microsoft Windows [Version 10.0.22621.1778], locale en-US)
[✓] Windows Version (Installed version of Windows is version 10 or higher)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0-rc4)
[✓] Visual Studio - develop for Windows (Visual Studio Community 2022 17.6.2)
[✓] Android Studio (version 2022.2)
[✓] VS Code (version 1.79.0)
[✓] Connected device (2 available)
[✓] Network resources

• No issues found!

Attributes are empty

Describe the bug
The attributes for a discovered service are empty. When the broadcast is started, however, the attributes are available.

To Reproduce
Steps to reproduce the behavior:

  1. Broadcast any service with attributes in it
  2. Discover on another device
  3. Try to access attributes of the discovered service, and print it; it will show as {}

Expected behavior
Attributes should not be empty.

Desktop (please complete the following information):

  • OS: MacOS
  • Version: Sonoma

Smartphone (please complete the following information):

  • Device: OnePlus 7T
  • OS: Android 13

Additional context
This wasn't an issue back in older versions like ^1.0.1+2.

Sending and Receiving Data?

Describe the bug
I don't see any way to exchange data between the broadcaster and the discoverer.

I can successfully set up a broadcaster and a discoverer, but after the initial handshake, I can find no functions or methods that allow the passage of data.

What am I missing?

Timeout control

Is your feature request related to a problem? Please describe.
I don't want discovery service to be disabled unless I call stop.

However in code I've found line:

service.resolve(withTimeout: 10.0)

Describe the solution you'd like
Timeout as optional parameter, with ability to set it unbounded (withTimeout: 0.0 works according to docs)

Describe alternatives you've considered
Modifying the source)

Why don't you stop Discovery when dispose in BonsoirServiceBrowserDelegate?

Describe the bug

If I repeat bonsoirDiscovery.start and stop, NetServiceBrowser will increase.

スクリーンショット 2021-09-21 23 47 57

To Reproduce
Steps to reproduce the behavior:

  1. await bonsoirDiscovery.start();
  2. awaitbonsoirDiscovery.stop();
  3. Repeat 1 and 2.
  4. See error

Expected behavior
When bonsoirDiscovery.stop(), you should run browser.stop().

I resolved the problem in the following way.

BonsoirServiceBrowserDelegate.swift

    /// Disposes the current class instance.
-   public func dispose(stopDiscovery: Bool = false) {
+   public func dispose(stopDiscovery: Bool = true) {
        services.removeAll()
        onDispose(stopDiscovery)
    }

but, I'm not sure if I can change that.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: iPhone 11 pro, iPhone SE2[e.g. iPhone6]
  • OS: iOS13, 14
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Plugin crashes App on refresh [ANDROID]

Describe the bug
Crasches entire app on RESTART

To Reproduce
Launch a series of discoveries
Then click on RESTART,

Expected behavior
The app should restart without crashes.

Smartphone :

  • Device: Pixel 4a
  • OS:Android 12

Additional context
This is the output:

E/AndroidRuntime(13414): FATAL EXCEPTION: NsdManager
E/AndroidRuntime(13414): Process: generic.package.name, PID: 13414
E/AndroidRuntime(13414): java.lang.IllegalArgumentException: listener not registered
E/AndroidRuntime(13414): 	at com.android.internal.util.Preconditions.checkArgument(Preconditions.java:59)
E/AndroidRuntime(13414): 	at android.net.nsd.NsdManager.getListenerKey(NsdManager.java:496)
E/AndroidRuntime(13414): 	at android.net.nsd.NsdManager.stopServiceDiscovery(NsdManager.java:636)
E/AndroidRuntime(13414): 	at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener.dispose(BonsoirDiscoveryListener.kt:167)
E/AndroidRuntime(13414): 	at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener.dispose$default(BonsoirDiscoveryListener.kt:165)
E/AndroidRuntime(13414): 	at fr.skyost.bonsoir.discovery.BonsoirDiscoveryListener.onStartDiscoveryFailed(BonsoirDiscoveryListener.kt:86)
E/AndroidRuntime(13414): 	at android.net.nsd.NsdManager$ServiceHandler.handleMessage(NsdManager.java:412)
E/AndroidRuntime(13414): 	at android.os.Handler.dispatchMessage(Handler.java:106)
E/AndroidRuntime(13414): 	at android.os.Looper.loopOnce(Looper.java:201)
E/AndroidRuntime(13414): 	at android.os.Looper.loop(Looper.java:288)
E/AndroidRuntime(13414): 	at android.os.HandlerThread.run(HandlerThread.java:67)
I/Process (13414): Sending signal. PID: 13414 SIG: 9
Lost connection to device.
Exited (sigterm)

Crash on Android KitKat (4.4.2)

Describe the bug
Trying to test a Flutter app using Bonsoir that targets Android KitKat (supported by the framework) results in an exception on startup and app crash.

E/flutter ( 3691): [ERROR:flutter/lib/ui/ui_dart_state.cc(213)] Unhandled Exception: MissingPluginException(No implementation found for method discovery.initialize on channel fr.skyost.bonsoir) E/flutter ( 3691): #0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:154:7) E/flutter ( 3691): <asynchronous suspension> E/flutter ( 3691): #1 MethodChannelBonsoirAction.ready (package:bonsoir_platform_interface/src/actions/action.dart:67:5) E/flutter ( 3691): <asynchronous suspension> E/flutter ( 3691): #2 AutodiscoveryService.start (package:xx/services/autodiscovery.dart:18:5) E/flutter ( 3691): <asynchronous suspension> E/flutter ( 3691): #3 main (package:xx/main.dart:13:3) E/flutter ( 3691): <asynchronous suspension> E/flutter ( 3691):

To Reproduce
Steps to reproduce the behavior:

final BonsoirDiscovery discovery = BonsoirDiscovery(type: '_xx._udp', printLogs: false); await discovery.ready;

Expected behavior
No crash, discovery starts as normal.

Smartphone (please complete the following information):

  • Samsung Galaxy Pocket Neo S5310 (Android 4.4.2)

Error "Namespace not specified. Please specify a namespace in the module's build.gradle file like so" when building example

Summary
When trying to build the example, error occurs:

PS C:\sources\Bonsoir\bonsoir\example> flutter run
Launching lib\main.dart on SM A336E in debug mode...

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring project ':device_info'.
> Could not create an instance of type com.android.build.api.variant.impl.LibraryVariantBuilderImpl.
   > Namespace not specified. Please specify a namespace in the module's build.gradle file like so:

     android {
         namespace 'com.example.namespace'
     }

     If the package attribute is specified in the source AndroidManifest.xml, it can be migrated automatically to the namespace value in the build.gradle file using the AGP Upgrade Assistant; please refer to https://developer.android.com/studio/build/agp-upgrade-assistant for more information.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s
Running Gradle task 'assembleDebug'...                           1,997ms
Exception: Gradle task assembleDebug failed with exit code 1

To Reproduce

  1. Clone repository
  2. Go to bosoir/example
  3. Execute flutter run
  4. See error

Flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.10.4, on Microsoft Windows [Version 10.0.22621.1778], locale en-US)
[✓] Windows Version (Installed version of Windows is version 10 or higher)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0-rc4)
[✓] Visual Studio - develop for Windows (Visual Studio Community 2022 17.6.2)
[✓] Android Studio (version 2022.2)
[✓] VS Code (version 1.79.0)
[✓] Connected device (2 available)
[✓] Network resources

Running multiple instances of BonsoirDisvoery on Android

Describe the bug
When multiple instances of BonsoirDiscovery with different type are running at the same time, the Android implementation doesn't correctly synchronize the resolution process. Two resolution requests can be issued to NsdManager (even for the same device name) which always fails with error 3. Resolver class is properly synchronized only when one instance is present.

To Reproduce
Steps to reproduce the behavior:

  1. Edit example project to use two instances of BonsoirDiscovery with two different types.
  2. Broadcast both of the types (tested in a way that one device is broadcasting both types).
  3. Build & run on Android.
  4. One resolve of the device will work, the other will result in error 3.

Expected behavior
Both instances of BonsoirDiscovery should resolve the devices correctly.

Desktop (please complete the following information):

  • OS: Ubuntu
  • Browser: Brave
  • Version: 22.04

Smartphone (please complete the following information):

  • Device: OnePlus 5
  • OS: Android 10
  • Browser: Brave
  • Version: 10

Additional context
Looking at the code, each instance of BonsoirDiscovery creates it's own Resolver instance. This is fine but the call to nsdManager.resolve() have to be synchronized globally - only one instance can call nsdManager.resolveService() at a time.

Can't build for ios (new version bad)

/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.3+1/ios/Classes/SuccessObject.swift:1:1: error: expressions are not allowed at the top level
../../darwin/Classes/SuccessObject.swift
^
/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.3+1/ios/Classes/SwiftBonsoirPlugin.swift:1:1: error: expressions are not allowed at the top level
../../darwin/Classes/SwiftBonsoirPlugin.swift
^
/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.3+1/ios/Classes/BonsoirServiceBrowserDelegate.swift:1:1: error: cannot find operator '../../' in scope
../../darwin/Classes/Discovery/BonsoirServiceBrowserDelegate.swift
^~~~~~
/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.3+1/ios/Classes/BonsoirServiceBrowserDelegate.swift:1:7: error: cannot find 'darwin' in scope
../../darwin/Classes/Discovery/BonsoirServiceBrowserDelegate.swift
      ^~~~~~
/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.3+1/ios/Classes/BonsoirServiceBrowserDelegate.swift:1:14: error: cannot find 'Classes' in scope
../../darwin/Classes/Discovery/BonsoirServiceBrowserDelegate.swift
             ^~~~~~~
/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.3+1/ios/Classes/BonsoirServiceBrowserDelegate.swift:1:22: error: cannot find 'Discovery' in scope
../../darwin/Classes/Discovery/BonsoirServiceBrowserDelegate.swift
                     ^~~~~~~~~
/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.3+1/ios/Classes/BonsoirServiceBrowserDelegate.swift:1:32: error: cannot find 'BonsoirServiceBrowserDelegate' in scope
../../darwin/Classes/Discovery/BonsoirServiceBrowserDelegate.swift
                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.3+1/ios/Classes/BonsoirServiceDelegate.swift:1:1: error: cannot find operator '../../' in scope
../../darwin/Classes/Broadcast/BonsoirServiceDelegate.swift
^~~~~~
/Users/steve/.pub-cache/hosted/pub.dartlang.org/bonsoir-0.1.3+1/ios/Classes/BonsoirServiceDelegate.swift:1:7: error: cannot find 'darwin' in scope
../../darwin/Classes/Broadcast/BonsoirServiceDelegate.swift
      ^~~~~~

When passing attributes on iOS and MacOS will cause value to cover to Optional() string

First of all, thanks for the great package.
All the value pass through the attributes will convert to Optional string.
To fix this you may change the code for both iOS and MacOS on :

SwiftBonsoirPlugin.swift
From :
for (key, value) in attributes {
if (value != nil) {
result[key] = String(describing: value).data(using: .utf8)
}
}

To:
for (key, value) in attributes {
if let value = value {
result[key] = value.data(using: .utf8)
}
}

SuccessObject.swift
From :
for (key, value) in attributes {
if(value != nil) {
result[key] = String(decoding: value!, as: UTF8.self)
}
}

To :
for (key, value) in attributes {
if let value = value {
result[key] = String(decoding: value, as: UTF8.self)
}
}

A way to start discovery and broadcast with timeout...

Is your feature request related to a problem? Please describe.
When I am starting the broadcast I want it to stop broadcasting in 5 mins if no device connect to my service.
When I am starting the discovery I want it to stop discovering in 5 mins if no service found.

So basically something like

    action = BonsoirBroadcast(service: service);
    await action!.ready;
    action!.eventStream?.listen((event) {});
    await action!.start(timeout: 5 mins); 
    BonsoirDiscovery discovery = BonsoirDiscovery(type: BroadcastProtocal.type);
    await discovery.ready;
   discovery.eventStream!.listen((event) async {});
   await discovery.start(timeout: 5 mins);

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
Use Completer and timeouts somehow to call discovery.stop() if its not stooped already.
Or BonsoirBroadcast.stop() if still broadcasting.

Additional context
Add any other context or screenshots about the feature request here.

Totally bugged after version 2.2.0+1

After version 2.2.0+1, we have to use event.service !.resolve(discovery.serviceResolver); manually, but 99% of the time this call triggers BonsoirDiscoveryEventType.discoveryStopped directly afterwards. The service is never resolved (except in 1% of cases by magic).

Bonsoir has found a service : <NSNetService 0x28267fbc0> local. _my-server._udp. My Serveur -1
Event received: event.type == BonsoirDiscoveryEventType.discoveryServiceFound
I call: event.service?.resolve(this._discovery!.serviceResolver);

Event received:  event.type == BonsoirDiscoveryEventType.discoveryStopped

Another problem is that when BonsoirDiscoveryEventType.discoveryServiceFound occurs, the service type is correct but the port is always equal to -1, except in 1% of cases by magic (the problem is also present in version 2.2.0+1, but the service is resolved in this version and handles BonsoirDiscoveryEventType.discoveryServiceResolved correctly afterwards, event if port == -1).

The problem is present on both IOS and Android.

I'm going back to version 2.2.0+1 while waiting for a solution to my problem.

Thank you

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.