Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@

#define FML_USED_ON_EMBEDDER

#import <TargetConditionals.h>

// NSNetService works fine on physical devices, but doesn't expose the services to regular mDNS
// queries on the Simulator. We can work around this by using the lower level C API, but that's
// only available from iOS 9.3+/macOS 10.11.4+.
#if TARGET_IPHONE_SIMULATOR
#include <dns_sd.h> // nogncheck
#include <net/if.h> // nogncheck
#endif // TARGET_IPHONE_SIMLUATOR

#import "FlutterObservatoryPublisher.h"

#include "flutter/fml/logging.h"
Expand All @@ -26,7 +36,11 @@ @interface FlutterObservatoryPublisher () <NSNetServiceDelegate>
@end

@implementation FlutterObservatoryPublisher {
#if TARGET_IPHONE_SIMULATOR
DNSServiceRef _dnsServiceRef;
#else // TARGET_IPHONE_SIMULATOR
fml::scoped_nsobject<NSNetService> _netService;
#endif // TARGET_IPHONE_SIMULATOR

blink::DartServiceIsolate::CallbackHandle _callbackHandle;
std::unique_ptr<fml::WeakPtrFactory<FlutterObservatoryPublisher>> _weakFactory;
Expand Down Expand Up @@ -54,50 +68,91 @@ - (instancetype)init {
}

- (void)dealloc {
#if TARGET_IPHONE_SIMULATOR
if (_dnsServiceRef) {
DNSServiceRefDeallocate(_dnsServiceRef);
_dnsServiceRef = NULL;
}
#else // TARGET_IPHONE_SIMULATOR
[_netService.get() stop];
#endif // TARGET_IPHONE_SIMULATOR

blink::DartServiceIsolate::RemoveServerStatusCallback(std::move(_callbackHandle));
[super dealloc];
}

- (void)publishServiceProtocolPort:(std::string)uri {
#if TARGET_IPHONE_SIMULATOR
if (_dnsServiceRef) {
DNSServiceRefDeallocate(_dnsServiceRef);
_dnsServiceRef = NULL;
}
#else // TARGET_IPHONE_SIMULATOR
[_netService.get() stop];
#endif // TARGET_IPHONE_SIMULATOR
if (uri.empty()) {
[_netService.get() stop];
return;
}
// uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port
// number.
NSURL* url =
[[[NSURL alloc] initWithString:[NSString stringWithUTF8String:uri.c_str()]] autorelease];

// DNS name has to be a max of 63 bytes. Prefer to cut off the app name rather than
// the device hostName. e.g. 'io.flutter.example@someones-iphone', or
// 'ongAppNameBecauseThisCouldHappenAtSomePoint@somelongname-iphone'
NSString* serviceName = [NSString
stringWithFormat:@"%@@%@",
[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"],
[NSProcessInfo processInfo].hostName];
if ([serviceName length] > 63) {
serviceName = [serviceName substringFromIndex:[serviceName length] - 63];
}
NSString* serviceName =
[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];

#if TARGET_IPHONE_SIMULATOR
DNSServiceFlags flags = kDNSServiceFlagsDefault;
uint32_t interfaceIndex = if_nametoindex("lo0");
const char* registrationType = "_dartobservatory._tcp";
const char* domain = "local."; // default domain
uint16_t port = [[url port] intValue];

int err = DNSServiceRegister(&_dnsServiceRef, flags, interfaceIndex, [serviceName UTF8String],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These methods seem to be introduced in iOS 9.3+ which is higher than our minimum supported version on 8.0. Unfortunately, you will have to have one implementation for the device and one for the simulator (with an overall guard against having any of this is release mode). Sorry.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oof, good catch.

This will get a little ugly. And if someone happens to be running an iOS 8.0 simulator (if that's even still possible?), we won't be able to make flutter attach work automatically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure which is uglier - having repeated code, or having guards sprinkled throughout (still with a little repeated code).

registrationType, domain, NULL, htons(port), 0, NULL,
registrationCallback, NULL);

if (err != 0) {
FML_LOG(ERROR) << "Failed to register observatory port with mDNS.";
} else {
DNSServiceProcessResult(_dnsServiceRef);
}
#else // TARGET_IPHONE_SIMULATOR
_netService.reset([[NSNetService alloc] initWithDomain:@"local."
type:@"_dartobservatory._tcp."
name:serviceName
port:[[url port] intValue]]);

[_netService.get() setDelegate:self];
[_netService.get() publish];
#endif // TARGET_IPHONE_SIMULATOR
}

- (void)netServiceDidPublish:(NSNetService*)sender {
FML_DLOG(INFO) << "FlutterObservatoryPublisher is ready!";
FML_LOG(INFO) << "FlutterObservatoryPublisher is ready!";
}

- (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict {
FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your "
"network settings and relaunch the application.";
}

#if TARGET_IPHONE_SIMULATOR
static void DNSSD_API registrationCallback(DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char* name,
const char* regType,
const char* domain,
void* context) {
if (errorCode == kDNSServiceErr_NoError) {
FML_LOG(ERROR) << "FlutterObservatoryPublisher is ready!";
} else {
FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your "
"network settings and relaunch the application.";
}
}
#endif // TARGET_IPHONE_SIMULATOR

#endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE && FLUTTER_RUNTIME_MODE !=
// FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE

Expand Down