Delete deprecated connections/clients/ios package

PiperOrigin-RevId: 547960446
This commit is contained in:
Nick Bourdakos
2023-07-13 16:29:49 -07:00
committed by Copybara-Service
parent 96348b6ba7
commit 99de15aef5
33 changed files with 0 additions and 3284 deletions
-80
View File
@@ -1,80 +0,0 @@
# Copyright 2020-2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# TODO(b/240046236): Rename this package as `objc`.
load("//tools/build_defs/apple:ios.bzl", "ios_unit_test")
load("//tools/build_defs/swift:swift_library.bzl", "swift_library")
load("//third_party/nearby:minimum_os.bzl", "IOS_LATEST_TEST_RUNNER", "IOS_MINIMUM_OS")
licenses(["notice"])
objc_library(
name = "Connections",
srcs = [
"Internal/GNCAdvertiser.mm",
"Internal/GNCCore.h",
"Internal/GNCCore.mm",
"Internal/GNCCoreConnection.h",
"Internal/GNCCoreConnection.mm",
"Internal/GNCDiscoverer.mm",
"Internal/GNCPayload.mm",
"Internal/GNCPayload+Internal.h",
"Internal/GNCPayloadListener.h",
"Internal/GNCPayloadListener.mm",
"Internal/GNCUtils.h",
"Internal/GNCUtils.mm",
],
hdrs = [
"Public/NearbyConnections/GNCAdvertiser.h",
"Public/NearbyConnections/GNCConnection.h",
"Public/NearbyConnections/GNCConnections.h",
"Public/NearbyConnections/GNCDiscoverer.h",
"Public/NearbyConnections/GNCPayload.h",
],
deprecation = "Please use //third_party/nearby/connections/swift/NearbyConnections",
features = ["-layering_check"],
module_name = "NearbyConnections",
visibility = [
"//googlemac/iPhone/Nearby/HelloConnections:__pkg__",
"//googlemac/iPhone/Nearby/Sharing/Source/UI/HomeViewController:__pkg__",
],
deps = [
"//connections:core",
"//connections:core_types",
"//internal/platform/implementation/apple",
"//third_party/apple_frameworks:Foundation",
"//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
"@com_google_absl//absl/functional:bind_front",
],
)
swift_library(
name = "BuildTestsLib",
testonly = 1,
srcs = ["BuildTests/Test.swift"],
deps = [
":Connections",
"//third_party/apple_frameworks:XCTest",
],
)
ios_unit_test(
name = "BuildTests",
minimum_os_version = IOS_MINIMUM_OS,
runner = IOS_LATEST_TEST_RUNNER,
deps = [
":BuildTestsLib",
],
)
@@ -1,8 +0,0 @@
import NearbyConnections
import XCTest
class BuildTests: XCTestCase {
func testBuild() {
// At least one test case is needed.
}
}
@@ -1,326 +0,0 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "connections/clients/ios/Public/NearbyConnections/GNCAdvertiser.h"
#include <string>
#include "absl/functional/bind_front.h"
#include "connections/advertising_options.h"
#import "connections/clients/ios/Internal/GNCCore.h"
#import "connections/clients/ios/Internal/GNCCoreConnection.h"
#import "connections/clients/ios/Internal/GNCPayloadListener.h"
#import "connections/clients/ios/Internal/GNCUtils.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCConnection.h"
#include "connections/core.h"
#include "connections/listeners.h"
#include "connections/params.h"
#include "connections/status.h"
#include "internal/platform/byte_array.h"
#import "internal/platform/implementation/apple/utils.h"
#import "GoogleToolboxForMac/GTMLogger.h"
NS_ASSUME_NONNULL_BEGIN
using ::nearby::ByteArrayFromNSData;
using ::nearby::CppStringFromObjCString;
using ::nearby::ObjCStringFromCppString;
using ::nearby::connections::AdvertisingOptions;
using ::nearby::connections::ConnectionListener;
using ::nearby::connections::ConnectionRequestInfo;
using ::nearby::connections::ConnectionResponseInfo;
using ::nearby::connections::GNCStrategyToStrategy;
using ::nearby::connections::Medium;
using ResultListener = ::nearby::connections::ResultCallback;
using ::nearby::connections::Status;
/** This is a GNCAdvertiserConnectionInfo that provides storage for its properties. */
@interface GNCAdvertiserConnectionInfo : NSObject
/** Information advertised by the remote endpoint. */
@property(nonatomic, readonly, nonnull) NSData *endpointInfo;
/** This token can be used to verify the identity of the discoverer. */
@property(nonatomic, readonly, nonnull) NSString *authToken;
/**
* Initializes and returns a GNCAdvertiserConnectionInfo object from endpoint info and auth token.
*
* @param endpointInfo An arbitrary byte array of information advertised by the remote endpoint.
* @param authToken A string token that can be used to verify the identity of the discoverer.
*/
- (nonnull instancetype)initWithEndpointInfo:(nonnull NSData *)endpointInfo authToken:(nonnull NSString *)authToken NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)init NS_UNAVAILABLE;
@end
@implementation GNCAdvertiserConnectionInfo
- (instancetype)initWithEndpointInfo:(NSData *)endpointInfo authToken:(NSString *)authToken {
self = [super init];
if (self) {
_endpointInfo = [endpointInfo copy];
_authToken = [authToken copy];
}
return self;
}
@end
/** Information retained about an endpoint before and after requesting a connection. */
@interface GNCAdvertiserEndpointInfo : NSObject
@property(nonatomic) GNCAdvertiserConnectionInfo *connectionInfo;
@property(nonatomic) GNCConnectionResponse clientResponse;
@property(nonatomic) BOOL clientResponseReceived; // whether the client response has been received
@property(nonatomic, nullable) GNCConnectionResultHandlers *connectionResultHandlers;
@property(nonatomic, weak) GNCCoreConnection *connection;
@property(nonatomic) GNCConnectionHandlers *connectionHandlers;
@end
@implementation GNCAdvertiserEndpointInfo
+ (instancetype)infoWithEndpointConnectionInfo:(GNCAdvertiserConnectionInfo *)connInfo {
GNCAdvertiserEndpointInfo *info = [[GNCAdvertiserEndpointInfo alloc] init];
info.connectionInfo = connInfo;
return info;
}
@end
/** GNCAdvertiser members. */
@interface GNCAdvertiser ()
@property(nonatomic) GNCCore *core;
@property(nonatomic) GNCAdvertiserConnectionInitiationHandler initiationHandler;
@property(nonatomic, assign) Status status;
@property(nonatomic) NSMutableDictionary<GNCEndpointId, GNCAdvertiserEndpointInfo *> *endpoints;
@end
/** C++ classes passed to the core library by GNCAdvertiser. */
namespace nearby {
namespace connections {
/** This class contains the callbacks for establishing and severing a connection. */
class GNCAdvertiserConnectionListener {
public:
explicit GNCAdvertiserConnectionListener(GNCAdvertiser *advertiser) : advertiser_(advertiser) {}
void OnInitiated(const std::string &endpoint_id, const ConnectionResponseInfo &info) {
GNCAdvertiser *advertiser = advertiser_; // strongify
if (!advertiser) return;
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
if (endpointInfo) {
GTMLoggerError(@"Connection already initiated for endpoint: %@", endpointId);
} else {
NSData *data = NSDataFromByteArray(info.remote_endpoint_info);
if (!data) {
GTMLoggerError(@"Endpoint info is missing for endpoint: %@", endpointId);
return;
}
NSString *authToken = ObjCStringFromCppString(info.authentication_token);
GNCAdvertiserConnectionInfo *connInfo =
[[GNCAdvertiserConnectionInfo alloc] initWithEndpointInfo:data authToken:authToken];
endpointInfo = [GNCAdvertiserEndpointInfo infoWithEndpointConnectionInfo:connInfo];
// Call the connection initiation handler. Synchronous because it returns the connection
// result handlers.
dispatch_sync(dispatch_get_main_queue(), ^{
__weak __typeof__(advertiser) weakAdvertiser = advertiser;
endpointInfo.connectionResultHandlers = advertiser.initiationHandler(
endpointId, (id<GNCAdvertiserConnectionInfo>)connInfo,
^(GNCConnectionResponse response) {
__strong __typeof__(advertiser) strongAdvertiser = weakAdvertiser;
endpointInfo.clientResponse = response;
endpointInfo.clientResponseReceived = YES;
if (response == GNCConnectionResponseAccept) {
// The connection was accepted by the client.
if (payload_listener_ == nullptr) {
payload_listener_ = std::make_unique<GNCPayloadListener>(
strongAdvertiser.core,
^{
return endpointInfo.connectionHandlers;
},
^{
return endpointInfo.connection.payloads;
});
}
strongAdvertiser.core->_core->AcceptConnection(
CppStringFromObjCString(endpointId),
PayloadListener{
.payload_cb = absl::bind_front(&GNCPayloadListener::OnPayload,
payload_listener_.get()),
.payload_progress_cb = absl::bind_front(
&GNCPayloadListener::OnPayloadProgress, payload_listener_.get()),
},
ResultListener{});
} else {
// The connection was rejected by the client.
strongAdvertiser.core->_core->RejectConnection(CppStringFromObjCString(endpointId),
ResultListener{});
}
});
});
advertiser.endpoints[endpointId] = endpointInfo;
}
}
void OnAccepted(const std::string &endpoint_id) {
GNCAdvertiser *advertiser = advertiser_; // strongify
if (!advertiser) return;
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
if (!endpointInfo) {
GTMLoggerInfo(@"Connection result for unknown endpoint: %@", endpointId);
return;
}
// The connection has been accepted by both endpoints, so create the GNCConnection object
// and pass it to |successHandler| for the client to use. It will be removed from |endpoints|
// when the client disconnects (on dealloc of GNCConnection).
// Note: Use a local strong reference to the connection object; don't just assign to
// |endpointInfo.connection|. Without a strong reference, the connection object can be
// deallocated before |successHandler| is called in the Release build.
__weak __typeof__(advertiser) weakAdvertiser = advertiser;
id<GNCConnection> connection = [GNCCoreConnection
connectionWithEndpointId:endpointId
core:advertiser.core
deallocHandler:^{
__strong __typeof__(advertiser) strongAdvertiser = weakAdvertiser;
if (!strongAdvertiser) return;
[strongAdvertiser.endpoints removeObjectForKey:endpointId];
}];
endpointInfo.connection = connection;
// Callback is synchronous because it returns the connection handlers.
dispatch_sync(dispatch_get_main_queue(), ^{
endpointInfo.connectionHandlers =
endpointInfo.connectionResultHandlers.successHandler(connection);
});
}
void OnRejected(const std::string &endpoint_id, Status status) {
GNCAdvertiser *advertiser = advertiser_; // strongify
if (!advertiser) return;
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
if (!endpointInfo) {
GTMLoggerInfo(@"Connection result for unknown endpoint: %@", endpointId);
return;
}
// One side rejected, so call failureHandler with the connection status (we do this in all
// cases), and forget the endpoint.
dispatch_async(dispatch_get_main_queue(), ^{
endpointInfo.connectionResultHandlers.failureHandler(GNCConnectionFailureRejected);
});
[advertiser.endpoints removeObjectForKey:endpointId];
}
void OnDisconnected(const std::string &endpoint_id) {
GNCAdvertiser *advertiser = advertiser_; // strongify
if (!advertiser) return;
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
if (endpointInfo) {
if (endpointInfo.connection) {
GNCDisconnectedHandler disconnectedHandler =
endpointInfo.connectionHandlers.disconnectedHandler;
dispatch_async(dispatch_get_main_queue(), ^{
if (disconnectedHandler) disconnectedHandler(GNCDisconnectedReasonUnknown);
});
} else {
GTMLoggerInfo(@"Disconnect for unconnected endpoint: %@", endpointId);
}
[advertiser.endpoints removeObjectForKey:endpointId];
} else {
GTMLoggerInfo(@"Disconnect for unknown endpoint: %@", endpointId);
}
}
void OnBandwidthChanged(const std::string &endpoint_id, Medium medium) {
GNCAdvertiser *advertiser = advertiser_; // strongify
if (!advertiser) return;
// TODO(b/169292092): Implement.
}
private:
__weak GNCAdvertiser *advertiser_;
std::unique_ptr<GNCPayloadListener> payload_listener_;
};
} // namespace connections
} // namespace nearby
using ::nearby::connections::GNCAdvertiserConnectionListener;
@interface GNCAdvertiser () {
std::unique_ptr<GNCAdvertiserConnectionListener> advertiserListener;
};
@end
@implementation GNCAdvertiser
+ (instancetype)advertiserWithEndpointInfo:(NSData *)endpointInfo
serviceId:(NSString *)serviceId
strategy:(GNCStrategy)strategy
connectionInitiationHandler:
(GNCAdvertiserConnectionInitiationHandler)initiationHandler {
GNCAdvertiser *advertiser = [[GNCAdvertiser alloc] init];
advertiser.initiationHandler = initiationHandler;
advertiser.endpoints = [[NSMutableDictionary alloc] init];
advertiser.core = GNCGetCore();
advertiser->advertiserListener = std::make_unique<GNCAdvertiserConnectionListener>(advertiser);
ConnectionListener listener = {
.initiated_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnInitiated,
advertiser->advertiserListener.get()),
.accepted_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnAccepted,
advertiser->advertiserListener.get()),
.rejected_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnRejected,
advertiser->advertiserListener.get()),
.disconnected_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnDisconnected,
advertiser->advertiserListener.get()),
};
AdvertisingOptions advertising_options;
advertising_options.strategy = GNCStrategyToStrategy(strategy);
advertising_options.allowed = nearby::connections::BooleanMediumSelector();
advertising_options.auto_upgrade_bandwidth = true;
advertising_options.enforce_topology_constraints = true;
advertiser.core->_core->StartAdvertising(CppStringFromObjCString(serviceId), advertising_options,
ConnectionRequestInfo{
.endpoint_info = ByteArrayFromNSData(endpointInfo),
.listener = std::move(listener),
},
ResultListener{});
return advertiser;
}
- (void)dealloc {
GTMLoggerInfo(@"GNCAdvertiser deallocated");
_core->_core->StopAdvertising(ResultListener{});
}
@end
NS_ASSUME_NONNULL_END
@@ -1,40 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifdef __cplusplus
#import <Foundation/Foundation.h>
#include <memory>
#include "connections/core.h"
#include "connections/implementation/service_controller_router.h"
NS_ASSUME_NONNULL_BEGIN
/** This class contains the C++ Core object. */
@interface GNCCore : NSObject {
@public
std::unique_ptr<::nearby::connections::Core> _core;
std::unique_ptr<::nearby::connections::ServiceControllerRouter> _service_controller_router;
}
@end
/** This function returns the Core singleton, wrapped in an Obj-C object for lifetime management. */
GNCCore *GNCGetCore();
NS_ASSUME_NONNULL_END
#endif
@@ -1,70 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "connections/clients/ios/Internal/GNCCore.h"
#include <utility>
#include "absl/container/internal/common.h"
#include "connections/core.h"
#include "connections/implementation/service_controller_router.h"
#import "GoogleToolboxForMac/GTMLogger.h"
using ::nearby::connections::Core;
using ::nearby::connections::ServiceControllerRouter;
@implementation GNCCore {
}
- (instancetype)init {
GTMLoggerInfo(@"GNCCore created");
self = [super init];
if (self) {
_service_controller_router = std::make_unique<ServiceControllerRouter>();
_core = std::make_unique<Core>(_service_controller_router.get());
}
return self;
}
- (void)dealloc {
_core.reset();
_service_controller_router.reset();
GTMLoggerInfo(@"GNCCore deallocated");
}
@end
GNCCore *GNCGetCore() {
static NSObject *syncSingleton;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
syncSingleton = [[NSObject alloc] init];
});
// The purpose of keeping a weak reference to the GNCCore object is to ensure that it will be
// released when all external strong references are gone. I.e., when the app is no longer doing
// any NC operations, the core will be released.
static __weak GNCCore *core;
// Strongly reference the GNCCore object for the duration of this function to ensure it isn't
// prematurely deallocated by ARC after being created (which can happen in optimized builds).
GNCCore *strongCore = core;
@synchronized(syncSingleton) {
if (!strongCore) {
strongCore = [[GNCCore alloc] init];
core = strongCore;
}
}
return strongCore;
}
@@ -1,48 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifdef __cplusplus
#import <Foundation/Foundation.h>
#import "connections/clients/ios/Internal/GNCCore.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCConnection.h"
NS_ASSUME_NONNULL_BEGIN
/** This holds the progress and completion for a pending payload. */
@interface GNCPayloadInfo : NSObject
@property(nonatomic, nullable) NSProgress *progress;
@property(nonatomic, nullable) GNCPayloadResultHandler completion;
+ (instancetype)infoWithProgress:(nullable NSProgress *)progress
completion:(GNCPayloadResultHandler)completion;
- (void)callCompletion:(GNCPayloadResult)result;
@end
/** GNCConnection that interfaces with the Core library. */
@interface GNCCoreConnection : NSObject <GNCConnection>
@property(nonatomic) GNCCore *core;
@property(nonatomic, copy) GNCEndpointId endpointId;
@property(nonatomic) dispatch_block_t deallocHandler;
@property(nonatomic) NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *payloads;
+ (instancetype)connectionWithEndpointId:(GNCEndpointId)endpointId
core:(GNCCore *)core
deallocHandler:(dispatch_block_t)deallocHandler;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -1,187 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "connections/clients/ios/Internal/GNCCoreConnection.h"
#include <utility>
#include "connections/core.h"
#include "connections/payload.h"
#include "internal/platform/exception.h"
#include "internal/platform/file.h"
#include "internal/platform/implementation/input_file.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/payload_id.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCConnection.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCPayload.h"
#import "internal/platform/implementation/apple/utils.h"
using ::nearby::ByteArrayFromNSData;
using ::nearby::CppStringFromObjCString;
using ::nearby::InputFile;
using ::nearby::InputStream;
using ::nearby::PayloadId;
using ::nearby::connections::Payload;
using ResultListener = ::nearby::connections::ResultCallback;
namespace nearby {
namespace connections {
/**
* This InputStream subclass takes input from an NSInputStream. The update handler is called for
* each chunk of data sent, giving the client an opportunity to handle cancelation.
*/
class GNCInputStreamFromNSStream : public InputStream {
public:
explicit GNCInputStreamFromNSStream(NSInputStream *nsStream) : nsStream_(nsStream) {
[nsStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[nsStream open];
}
~GNCInputStreamFromNSStream() override { Close(); }
ExceptionOr<ByteArray> Read() { return Read(kMaxChunkSize); }
ExceptionOr<ByteArray> Read(std::int64_t size) override {
uint8_t *bytesRead = new uint8_t[size];
NSUInteger numberOfBytesToRead = [[NSNumber numberWithLongLong:size] unsignedIntegerValue];
NSInteger numberOfBytesRead = [nsStream_ read:bytesRead maxLength:numberOfBytesToRead];
if (numberOfBytesRead == 0) {
// Reached end of stream.
return ExceptionOr<ByteArray>();
} else if (numberOfBytesRead < 0) {
// Stream error.
return ExceptionOr<ByteArray>{Exception::kIo};
}
return ExceptionOr<ByteArray>(ByteArrayFromNSData([NSData dataWithBytes:bytesRead
length:numberOfBytesRead]));
}
Exception Close() override {
[nsStream_ close];
return {Exception::kSuccess};
}
private:
static const size_t kMaxChunkSize = 32 * 1024;
NSInputStream *nsStream_;
// dispatch_block_t update_handler_;
};
} // namespace connections
} // namespace nearby
@implementation GNCPayloadInfo
+ (instancetype)infoWithProgress:(nullable NSProgress *)progress
completion:(GNCPayloadResultHandler)completion {
GNCPayloadInfo *info = [[GNCPayloadInfo alloc] init];
info.progress = progress;
info.completion = completion;
return info;
}
- (void)callCompletion:(GNCPayloadResult)result {
if (_completion) _completion(result);
_completion = nil;
}
@end
@implementation GNCCoreConnection
+ (instancetype)connectionWithEndpointId:(GNCEndpointId)endpointId
core:(GNCCore *)core
deallocHandler:(dispatch_block_t)deallocHandler {
GNCCoreConnection *connection = [[GNCCoreConnection alloc] init];
connection.endpointId = endpointId;
connection.core = core;
connection.deallocHandler = deallocHandler;
connection.payloads = [[NSMutableDictionary alloc] init];
return connection;
}
- (void)dealloc {
_core->_core->DisconnectFromEndpoint(CppStringFromObjCString(_endpointId), ResultListener{});
_deallocHandler();
}
- (NSProgress *)sendBytesPayload:(GNCBytesPayload *)payload
completion:(GNCPayloadResultHandler)completion {
Payload corePayload(ByteArrayFromNSData(payload.bytes));
NSUInteger length = payload.bytes.length;
PayloadId payloadId = corePayload.GetId();
NSProgress *progress = [NSProgress progressWithTotalUnitCount:length];
__weak __typeof__(self) weakSelf = self;
progress.cancellationHandler = ^{
[weakSelf cancelPayloadWithId:payloadId];
};
return [self sendPayload:std::move(corePayload)
size:length
progress:progress
completion:completion];
}
- (NSProgress *)sendStreamPayload:(GNCStreamPayload *)payload
completion:(GNCPayloadResultHandler)completion {
NSProgress *progress = [NSProgress progressWithTotalUnitCount:-1];
PayloadId payloadId = payload.identifier;
Payload corePayload(payloadId, [payload]() -> InputStream & {
nearby::connections::GNCInputStreamFromNSStream *stream =
new nearby::connections::GNCInputStreamFromNSStream(payload.stream);
return *stream;
});
return [self sendPayload:std::move(corePayload) size:-1 progress:progress completion:completion];
}
- (NSProgress *)sendFilePayload:(GNCFilePayload *)payload
completion:(GNCPayloadResultHandler)completion {
NSProgress *progress = [NSProgress progressWithTotalUnitCount:0];
std::int64_t fileSize = 0;
NSURL *fileURL = payload.fileURL;
NSNumber *fileSizeValue = nil;
BOOL result = [fileURL getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:nil];
if (result == YES) {
fileSize = fileSizeValue.longValue;
}
PayloadId payloadId = payload.identifier;
InputFile inputFile(CppStringFromObjCString(fileURL.path), fileSize);
Payload corePayload(payloadId, std::move(inputFile));
progress.totalUnitCount = fileSize;
return [self sendPayload:std::move(corePayload)
size:fileSize
progress:progress
completion:completion];
}
#pragma mark Private
- (NSProgress *)sendPayload:(Payload)payload
size:(uint64_t)size
progress:(NSProgress *)progress
completion:(GNCPayloadResultHandler)completion {
_payloads[@(payload.GetId())] = [GNCPayloadInfo infoWithProgress:progress completion:completion];
_core->_core->SendPayload(std::vector<std::string>(1, CppStringFromObjCString(_endpointId)),
std::move(payload), ResultListener{});
return progress;
}
- (void)cancelPayloadWithId:(PayloadId)payloadId {
_core->_core->CancelPayload(payloadId, ResultListener{});
}
@end
@@ -1,395 +0,0 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "connections/clients/ios/Public/NearbyConnections/GNCDiscoverer.h"
#include <string>
#include <utility>
#include "absl/functional/bind_front.h"
#import "connections/clients/ios/Internal/GNCCore.h"
#import "connections/clients/ios/Internal/GNCCoreConnection.h"
#import "connections/clients/ios/Internal/GNCPayloadListener.h"
#import "connections/clients/ios/Internal/GNCUtils.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCConnection.h"
#include "connections/connection_options.h"
#include "connections/core.h"
#include "connections/discovery_options.h"
#include "connections/listeners.h"
#include "connections/status.h"
#include "internal/platform/byte_array.h"
#import "internal/platform/implementation/apple/utils.h"
#import "GoogleToolboxForMac/GTMLogger.h"
NS_ASSUME_NONNULL_BEGIN
using ::nearby::CppStringFromObjCString;
using ::nearby::connections::DiscoveryListener;
using ::nearby::connections::DiscoveryOptions;
using ::nearby::connections::GNCStrategyToStrategy;
using ResultListener = ::nearby::connections::ResultCallback;
using ::nearby::connections::Status;
/** This is a GNCDiscovererConnectionInfo that provides storage for its properties. */
@interface GNCDiscovererConnectionInfo : NSObject <GNCDiscovererConnectionInfo>
@property(nonatomic, copy) NSString *authToken;
/** Creates a GNCDiscovererConnectionInfo object. */
+ (instancetype)infoWithAuthToken:(NSString *)authToken;
@end
@implementation GNCDiscovererConnectionInfo
+ (instancetype)infoWithAuthToken:(NSString *)authToken {
GNCDiscovererConnectionInfo *info = [[GNCDiscovererConnectionInfo alloc] init];
info.authToken = authToken;
return info;
}
@end
/** This is a GNCDiscoveredEndpointInfo that provides storage for its properties. */
@interface GNCDiscoveredEndpointInfo : NSObject <GNCDiscoveredEndpointInfo>
@property(nonatomic, copy) NSString *endpointName;
@property(nonatomic, copy) NSData *endpointInfo;
@end
@implementation GNCDiscoveredEndpointInfo
@synthesize requestConnection = _requestConnection;
+ (instancetype)infoWithName:(NSString *)endpointName
endpointInfo:(NSData *)endpointInfo
requestConnection:(GNCConnectionRequester)requestConnection {
GNCDiscoveredEndpointInfo *info = [[GNCDiscoveredEndpointInfo alloc] init];
info.endpointName = endpointName;
info.endpointInfo = endpointInfo;
info->_requestConnection = requestConnection;
return info;
}
@end
/** Information retained by the discoverer about each discovered endpoint. */
@interface GNCDiscovererEndpointInfo : NSObject
/** Handles lostHandler once |onEndpointLost| has been callback. */
@property(nonatomic) GNCEndpointLostHandler lostHandler;
/** The connInitHandler is stored after requestConnection. */
@property(nonatomic, nullable) GNCDiscovererConnectionInitializationHandler connInitHandler;
/** The connFailureHandler is stored after requestConnection. */
@property(nonatomic, nullable) GNCConnectionFailureHandler connFailureHandler;
/** Client responses Accept or Reject. */
@property(nonatomic) GNCConnectionResponse clientResponse;
/** Whether the client response has been received. */
@property(nonatomic) BOOL clientResponseReceived;
/**
* The connectionhandler returned by connInitHandler. Stored here if the connection is accepted.
*/
@property(nonatomic, nullable) GNCConnectionHandler connectionHandler;
/** @c GNCCoreConnection is created and stored if connection is accepted. */
@property(nonatomic, weak) GNCCoreConnection *connection;
/** @c GNCConnectionHandlers object is returned by connectionHandler and stored here. */
@property(nonatomic) GNCConnectionHandlers *connectionHandlers;
@end
@implementation GNCDiscovererEndpointInfo
@end
/** GNCDiscoverer members. */
@interface GNCDiscoverer ()
@property(nonatomic) GNCCore *core;
@property(nonatomic) GNCEndpointFoundHandler endpointFoundHandler;
@property(nonatomic, assign) Status status;
@property(nonatomic) NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints;
@end
/** C++ classes passed to the core library by GNCDiscoverer. */
namespace nearby {
namespace connections {
/** This class contains the discoverer callbacks related to a connection. */
class GNCDiscovererConnectionListener {
public:
GNCDiscovererConnectionListener(GNCCore *core,
NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints)
: core_(core), endpoints_(endpoints) {}
void OnInitiated(const std::string &endpoint_id, const ConnectionResponseInfo &info) {
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
if (!endpointInfo) {
return;
}
// Call the connection initiation handler. Synchronous because it returns the connection
// handler.
NSString *authToken = ObjCStringFromCppString(info.authentication_token);
GNCCore *core = core_; // don't capture |this|
dispatch_sync(dispatch_get_main_queue(), ^{
endpointInfo.connectionHandler = endpointInfo.connInitHandler(
[GNCDiscovererConnectionInfo infoWithAuthToken:authToken],
^(GNCConnectionResponse response) {
endpointInfo.clientResponse = response;
endpointInfo.clientResponseReceived = YES;
if (response == GNCConnectionResponseAccept) {
// The connect was accepted by the client.
if (payload_listener_ == nullptr) {
payload_listener_ = std::make_unique<GNCPayloadListener>(
core,
^{
return endpointInfo.connectionHandlers;
},
^{
return endpointInfo.connection.payloads;
});
}
core->_core->AcceptConnection(
CppStringFromObjCString(endpointId),
PayloadListener{
.payload_cb =
absl::bind_front(&GNCPayloadListener::OnPayload, payload_listener_.get()),
.payload_progress_cb = absl::bind_front(
&GNCPayloadListener::OnPayloadProgress, payload_listener_.get()),
},
ResultListener{});
} else {
// The connect was rejected by the client.
core->_core->RejectConnection(CppStringFromObjCString(endpointId), ResultListener{});
}
});
});
}
void OnAccepted(const std::string &endpoint_id) {
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
if (!endpointInfo) {
return;
}
// The connection has been accepted by both endpoints, so create the GNCConnection object
// and pass it to |successHandler| for the client to use.
// Note: Use a local strong reference to the connection object; don't just assign to
// |endpointInfo.connection|. Without a strong reference, the connection object can be
// deallocated before |successHandler| is called in the Release build.
id<GNCConnection> connection = [GNCCoreConnection
connectionWithEndpointId:endpointId
core:core_
deallocHandler:^{
// Don't remove the remote endpoint (like GNCAdvertiser does) because that's
// done when the endpoint is lost.
}];
endpointInfo.connection = connection;
// Callback is synchronous because it returns the connection handlers.
dispatch_sync(dispatch_get_main_queue(), ^{
endpointInfo.connectionHandlers = endpointInfo.connectionHandler(endpointInfo.connection);
});
endpointInfo.clientResponseReceived = NO; // support reconnection after disconnection
}
void OnRejected(const std::string &endpoint_id, Status status) {
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
if (!endpointInfo) {
return;
}
// If either side rejected, call failureHandler with the connection status.
dispatch_async(dispatch_get_main_queue(), ^{
endpointInfo.connFailureHandler(GNCConnectionFailureRejected);
});
endpointInfo.clientResponseReceived = NO; // support reconnection after disconnection
}
void OnDisconnected(const std::string &endpoint_id) {
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
if (!endpointInfo) {
return;
}
if (endpointInfo.connection) {
GNCDisconnectedHandler disconnectedHandler =
endpointInfo.connectionHandlers.disconnectedHandler;
dispatch_async(dispatch_get_main_queue(), ^{
if (disconnectedHandler) disconnectedHandler(GNCDisconnectedReasonUnknown);
});
}
}
void OnBandwidthChanged(const std::string &endpoint_id, Medium medium) {
// TODO(b/169292092): Implement.
}
private:
GNCCore *core_;
NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints_;
std::unique_ptr<GNCPayloadListener> payload_listener_;
};
class GNCDiscoveryListener {
public:
explicit GNCDiscoveryListener(GNCDiscoverer *discoverer) : discoverer_(discoverer) {}
void OnEndpointFound(const std::string &endpoint_id, const ByteArray &endpoint_info,
const std::string &service_id) {
GNCDiscoverer *discoverer = discoverer_; // strongify
if (!discoverer) {
return;
}
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
if ([discoverer.endpoints objectForKey:endpointId] != nil) {
GTMLoggerError(@"Endpoint already discovered: %@", endpointId);
} else {
// The GNCDiscoveredEndpointInfo object created here lives as long as the client has strong
// reference to it. Here's the chain of strong references maintained here:
// client -> GNCDiscoveredEndpointInfo -> RequestConnection block ->
// GNCDiscovererEndpointInfo (stored weakly in the |endpoints| map table)
GNCDiscovererEndpointInfo *endpointInfo = [[GNCDiscovererEndpointInfo alloc] init];
[discoverer.endpoints setObject:endpointInfo forKey:endpointId];
NSString *name = ObjCStringFromCppString(std::string(endpoint_info));
NSData *info = NSDataFromByteArray(endpoint_info);
GNCCore *core = discoverer.core; // don't capture |this| or |discoverer|
NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints = discoverer.endpoints;
GNCDiscoveredEndpointInfo *discEndpointInfo = [GNCDiscoveredEndpointInfo
infoWithName:name
endpointInfo:info
requestConnection:^(NSData *info,
GNCDiscovererConnectionInitializationHandler connInitHandler,
GNCConnectionFailureHandler connFailureHandler) {
endpointInfo.connInitHandler = connInitHandler;
endpointInfo.connFailureHandler = connFailureHandler;
if (discoverer_connection_listener_ == nullptr) {
discoverer_connection_listener_ =
std::make_unique<GNCDiscovererConnectionListener>(core, endpoints);
}
ConnectionListener listener = {
.initiated_cb = absl::bind_front(&GNCDiscovererConnectionListener::OnInitiated,
discoverer_connection_listener_.get()),
.accepted_cb = absl::bind_front(&GNCDiscovererConnectionListener::OnAccepted,
discoverer_connection_listener_.get()),
.rejected_cb = absl::bind_front(&GNCDiscovererConnectionListener::OnRejected,
discoverer_connection_listener_.get()),
.disconnected_cb =
absl::bind_front(&GNCDiscovererConnectionListener::OnDisconnected,
discoverer_connection_listener_.get()),
};
core->_core->RequestConnection(
CppStringFromObjCString(endpointId),
ConnectionRequestInfo{.endpoint_info = ByteArrayFromNSData(info),
.listener = std::move(listener)},
ConnectionOptions{},
ResultListener{.result_cb = [connFailureHandler](Status status) {
if (!status.Ok()) {
dispatch_sync(dispatch_get_main_queue(), ^{
connFailureHandler(GNCConnectionFailureUnknown);
});
}
}});
}];
// Call the client endpoint-found handler. Tail call for reentrancy.
dispatch_sync(dispatch_get_main_queue(), ^{
endpointInfo.lostHandler = discoverer.endpointFoundHandler(endpointId, discEndpointInfo);
});
}
}
void OnEndpointLost(const std::string &endpoint_id) {
GNCDiscoverer *discoverer = discoverer_; // strongify
if (!discoverer) {
return;
}
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
GNCDiscovererEndpointInfo *info = [discoverer.endpoints objectForKey:endpointId];
if (!info) {
GTMLoggerError(@"Endpoint already lost: %@", endpointId);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
info.lostHandler();
});
}
[discoverer.endpoints removeObjectForKey:endpointId];
}
void OnEndpointDistanceChanged_cb(const std::string &endpoint_id, DistanceInfo info) {
// TODO(b/169292092): Implement.
}
private:
__weak GNCDiscoverer *discoverer_;
std::unique_ptr<GNCDiscovererConnectionListener> discoverer_connection_listener_;
};
} // namespace connections
} // namespace nearby
using ::nearby::connections::GNCDiscoveryListener;
@interface GNCDiscoverer () {
std::unique_ptr<GNCDiscoveryListener> discoveryListener;
};
@end
@implementation GNCDiscoverer
+ (instancetype)discovererWithServiceId:(NSString *)serviceId
strategy:(GNCStrategy)strategy
endpointFoundHandler:(GNCEndpointFoundHandler)endpointFoundHandler {
GNCDiscoverer *discoverer = [[GNCDiscoverer alloc] init];
discoverer.endpointFoundHandler = endpointFoundHandler;
discoverer.endpoints = [NSMapTable strongToWeakObjectsMapTable];
discoverer.core = GNCGetCore();
discoverer->discoveryListener = std::make_unique<GNCDiscoveryListener>(discoverer);
DiscoveryListener listener = {
.endpoint_found_cb = absl::bind_front(&GNCDiscoveryListener::OnEndpointFound,
discoverer->discoveryListener.get()),
.endpoint_lost_cb = absl::bind_front(&GNCDiscoveryListener::OnEndpointLost,
discoverer->discoveryListener.get()),
};
DiscoveryOptions discovery_options;
discovery_options.strategy = GNCStrategyToStrategy(strategy);
discoverer.core->_core->StartDiscovery(CppStringFromObjCString(serviceId), discovery_options,
std::move(listener), ResultListener{});
return discoverer;
}
- (void)dealloc {
GTMLoggerInfo(@"GNCDiscoverer deallocated");
_core->_core->StopDiscovery(ResultListener{});
}
@end
NS_ASSUME_NONNULL_END
@@ -1,34 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "connections/clients/ios/Public/NearbyConnections/GNCPayload.h"
NS_ASSUME_NONNULL_BEGIN
/** This category adds the ability to specify a payload ID. */
@interface GNCBytesPayload (Internal)
+ (instancetype)payloadWithBytes:(NSData *)bytes identifier:(int64_t)identifier;
@end
/** This category adds the ability to specify a payload ID. */
@interface GNCStreamPayload (Internal)
+ (instancetype)payloadWithStream:(NSInputStream *)stream identifier:(int64_t)identifier;
@end
/** This category adds the ability to specify a payload ID. */
@interface GNCFilePayload (Internal)
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier;
@end
NS_ASSUME_NONNULL_END
@@ -1,94 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "connections/clients/ios/Public/NearbyConnections/GNCPayload.h"
#include "connections/payload.h"
#include "internal/platform/payload_id.h"
#include <stdlib.h>
using ::nearby::PayloadId;
using ::nearby::connections::Payload;
NS_ASSUME_NONNULL_BEGIN
PayloadId GenerateId() {
return Payload::GenerateId();
}
@implementation GNCBytesPayload
- (instancetype)initWithBytes:(NSData *)bytes identifier:(int64_t)identifier {
self = [super init];
if (self) {
_identifier = identifier;
_bytes = bytes;
}
return self;
}
+ (instancetype)payloadWithBytes:(NSData *)bytes {
return [[self alloc] initWithBytes:bytes identifier:GenerateId()];
}
+ (instancetype)payloadWithBytes:(NSData *)bytes identifier:(int64_t)identifier {
return [[self alloc] initWithBytes:bytes identifier:identifier];
}
@end
@implementation GNCStreamPayload
- (instancetype)initWithStream:(NSInputStream *)stream identifier:(int64_t)identifier {
self = [super init];
if (self) {
_identifier = identifier;
_stream = stream;
}
return self;
}
+ (instancetype)payloadWithStream:(NSInputStream *)stream {
return [[self alloc] initWithStream:stream identifier:GenerateId()];
}
+ (instancetype)payloadWithStream:(NSInputStream *)stream identifier:(int64_t)identifier {
return [[self alloc] initWithStream:stream identifier:identifier];
}
@end
@implementation GNCFilePayload
- (instancetype)initWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier {
self = [super init];
if (self) {
_identifier = identifier;
_fileURL = [fileURL copy];
}
return self;
}
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL {
return [[self alloc] initWithFileURL:fileURL identifier:GenerateId()];
}
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier {
return [[self alloc] initWithFileURL:fileURL identifier:identifier];
}
@end
NS_ASSUME_NONNULL_END
@@ -1,58 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifdef __cplusplus
#import <Foundation/Foundation.h>
#include <string>
#import "connections/clients/ios/Internal/GNCCore.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCConnection.h"
NS_ASSUME_NONNULL_BEGIN
@class GNCPayloadInfo;
namespace nearby {
namespace connections {
/** This fetches a GNCConnectionHandlers object. */
typedef GNCConnectionHandlers *_Nonnull (^GNCConnectionHandlersProvider)();
/** This fetches a payload dictionary. */
typedef NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *_Nonnull (^GNCPayloadsProvider)();
/** This is the payload handler for an advertiser or discoverer. */
class GNCPayloadListener : public PayloadListener {
public:
GNCPayloadListener(GNCCore *core, GNCConnectionHandlersProvider handlersProvider,
GNCPayloadsProvider payloadsProvider)
: core_(core), handlers_provider_(handlersProvider), payloads_provider_(payloadsProvider) {}
void OnPayload(absl::string_view endpoint_id, Payload payload);
void OnPayloadProgress(absl::string_view endpoint_id, const PayloadProgressInfo &info);
private:
GNCCore *core_;
GNCConnectionHandlersProvider handlers_provider_;
GNCPayloadsProvider payloads_provider_;
};
} // namespace connections
} // namespace nearby
NS_ASSUME_NONNULL_END
#endif
@@ -1,216 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "connections/clients/ios/Internal/GNCPayloadListener.h"
#include <string>
#import "connections/clients/ios/Internal/GNCCore.h"
#import "connections/clients/ios/Internal/GNCCoreConnection.h"
#import "connections/clients/ios/Internal/GNCPayload+Internal.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCConnection.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCPayload.h"
#include "connections/core.h"
#include "connections/listeners.h"
#include "connections/payload.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/file.h"
#include "internal/platform/implementation/apple/utils.h"
#include "internal/platform/input_stream.h"
NS_ASSUME_NONNULL_BEGIN
namespace nearby {
namespace connections {
void GNCPayloadListener::OnPayload(absl::string_view endpoint_id, Payload payload) {
GNCConnectionHandlers *handlers = handlers_provider_();
int64_t payloadId = payload.GetId();
// Note: The payload must be destroyed by each individual payload type handler below, because in
// the Stream payload case, it runs an asynchronous read-write loop, which needs the payload
// and its stream to live until the stream ends.
NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *payloads = payloads_provider_();
switch (payload.GetType()) {
case PayloadType::kBytes: {
NSData *data = NSDataFromByteArray(payload.AsBytes()); // don't capture C++ object
// Wait for the payload transfer update to arrive before calling the Bytes payload handler.
GNCPayloadInfo *info = [GNCPayloadInfo
infoWithProgress:nil
completion:^(GNCPayloadResult result) {
NSCAssert(result == GNCPayloadResultSuccess, @"Expected success");
if (handlers.bytesPayloadHandler) {
// Call the Bytes payload handler.
dispatch_async(dispatch_get_main_queue(), ^{
handlers.bytesPayloadHandler([GNCBytesPayload payloadWithBytes:data
identifier:payloadId]);
});
}
}];
payloads[@(payloadId)] = info;
break;
}
case PayloadType::kStream:
if (handlers.streamPayloadHandler) {
// Make a pair of bound streams so data pumped into the output stream becomes
// available for reading from the input stream.
NSInputStream *clientInputStream;
NSOutputStream *clientOutputStream;
// TODO(b/169292092): Base on medium's bandwidth?
[NSStream getBoundStreamsWithBufferSize:1024
inputStream:&clientInputStream
outputStream:&clientOutputStream];
NSProgress *progress = [NSProgress progressWithTotalUnitCount:-1]; // indeterminate
progress.cancellable = YES;
// Pass the payload to the stream payload handler, receiving the completion handler from it.
// Since it returns a value, it must be called synchronously.
__block GNCPayloadResultHandler completion;
dispatch_sync(dispatch_get_main_queue(), ^{
completion = handlers.streamPayloadHandler(
[GNCStreamPayload payloadWithStream:clientInputStream identifier:payloadId],
progress);
});
GNCPayloadInfo *info = [GNCPayloadInfo infoWithProgress:progress completion:completion];
payloads[@(payloadId)] = info;
// This is a loop that reads data from the C++ input stream and writes it to the output
// stream that feeds it to the client input stream.
__block InputStream *payloadInputStream = payload.AsStream();
dispatch_queue_t queue =
dispatch_queue_create("StreamReceiverQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[clientOutputStream open];
while (true) {
if (progress.isCancelled) {
// Payload was canceled by the client.
core_->_core->CancelPayload(payloadId, ResultCallback{.result_cb = [](Status status) {
// TODO(b/148640962): Implement.
}});
break;
}
ExceptionOr<ByteArray> readResult = payloadInputStream->Read(1024);
if (!readResult.ok()) {
// Error reading from stream.
// TODO(b/169292092): Tell core an error has occurred?
dispatch_async(dispatch_get_main_queue(), ^{
[info callCompletion:GNCPayloadResultFailure];
});
break;
}
ByteArray byteArray = readResult.GetResult();
if (byteArray.Empty()) {
// End of stream.
break;
}
// Loop until it's all been consumed by the client output stream.
NSData *data = NSDataFromByteArray(byteArray);
NSUInteger totalLength = data.length;
NSUInteger totalNumberWritten = 0;
while (totalNumberWritten < totalLength) {
NSInteger numberWritten =
[clientOutputStream write:&((const uint8_t *)data.bytes)[totalNumberWritten]
maxLength:totalLength - totalNumberWritten];
if (numberWritten <= 0) { // stream error or reached end of stream
// TODO(b/169292092): Tell core an error has occurred?
dispatch_async(dispatch_get_main_queue(), ^{
[info callCompletion:GNCPayloadResultFailure];
});
break;
}
totalNumberWritten += numberWritten;
}
}
});
}
break;
case PayloadType::kFile:
if (handlers.filePayloadHandler) {
InputFile *payloadInputFile = payload.AsFile();
NSString *fileString = ObjCStringFromCppString(payloadInputFile->GetFilePath());
NSURL *fileURL = [NSURL fileURLWithPath:fileString];
int64_t fileSize = payloadInputFile->GetTotalSize();
NSProgress *progress = [NSProgress progressWithTotalUnitCount:fileSize];
progress.cancellable = YES;
progress.cancellationHandler = ^{
// Payload was canceled by the client.
core_->_core->CancelPayload(payloadId, ResultCallback{.result_cb = [](Status status) {
// TODO(b/148640962): Implement.
}});
};
// Pass the payload to the file payload handler, receiving the completion handler from it.
// Since it returns a value, it must be called synchronously.
__block GNCPayloadResultHandler completion;
void (^passPayloadBlock)(void) = ^{
completion = handlers.filePayloadHandler(
[GNCFilePayload payloadWithFileURL:fileURL identifier:payloadId], progress);
};
if ([NSThread isMainThread]) {
passPayloadBlock();
} else {
dispatch_sync(dispatch_get_main_queue(), passPayloadBlock);
}
GNCPayloadInfo *info = [GNCPayloadInfo infoWithProgress:progress completion:completion];
payloads[@(payloadId)] = info;
}
break;
default:
;// fall through
}
}
void GNCPayloadListener::OnPayloadProgress(absl::string_view endpoint_id,
const PayloadProgressInfo &info) {
// Note: The logic in this callback for handling progress updates and payload completion is
// identical for Bytes, Stream and File payloads.
NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *payloads = payloads_provider_();
NSNumber *payloadId = @(info.payload_id);
GNCPayloadInfo *payloadInfo = payloads[payloadId];
if (payloadInfo) {
// Update the progress.
if (payloadInfo.progress) {
payloadInfo.progress.completedUnitCount = info.bytes_transferred;
}
// Call the completion handler for success/failure/canceled, but not in-progress.
if (info.status == PayloadProgressInfo::Status::kInProgress) {
return;
}
GNCPayloadResult result =
(info.status == PayloadProgressInfo::Status::kSuccess) ? GNCPayloadResultSuccess
: (info.status == PayloadProgressInfo::Status::kCanceled) ? GNCPayloadResultCanceled
: GNCPayloadResultFailure;
dispatch_async(dispatch_get_main_queue(), ^{
payloadInfo.completion(result);
});
// Release the payload info.
[payloads removeObjectForKey:payloadId];
}
}
} // namespace connections
} // namespace nearby
NS_ASSUME_NONNULL_END
@@ -1,44 +0,0 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifdef __cplusplus
#import <Foundation/Foundation.h>
#include <string>
#import "connections/clients/ios/Public/NearbyConnections/GNCAdvertiser.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCConnection.h"
#include "connections/listeners.h"
NS_ASSUME_NONNULL_BEGIN
namespace nearby {
namespace connections {
/** Converts GNCStrategy to Strategy. */
const Strategy& GNCStrategyToStrategy(GNCStrategy strategy);
} // namespace connections
} // namespace nearby
/** Internal-only properties of the connection result handlers class. */
@interface GNCConnectionResultHandlers ()
@property(nonatomic) GNCConnectionHandler successHandler;
@property(nonatomic) GNCConnectionFailureHandler failureHandler;
@end
NS_ASSUME_NONNULL_END
#endif
@@ -1,75 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "connections/clients/ios/Internal/GNCUtils.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCAdvertiser.h"
#import "connections/clients/ios/Public/NearbyConnections/GNCConnection.h"
#include "connections/strategy.h"
NS_ASSUME_NONNULL_BEGIN
namespace nearby {
namespace connections {
const Strategy& GNCStrategyToStrategy(GNCStrategy strategy) {
switch (strategy) {
case GNCStrategyCluster:
return Strategy::kP2pCluster;
case GNCStrategyStar:
return Strategy::kP2pStar;
case GNCStrategyPointToPoint:
return Strategy::kP2pPointToPoint;
}
}
} // namespace connections
} // namespace nearby
@implementation GNCConnectionHandlers
- (instancetype)initWithBuilderBlock:(void (^)(GNCConnectionHandlers*))builderBlock {
self = [super init];
if (self) {
builderBlock(self);
}
return self;
}
+ (instancetype)handlersWithBuilder:(void (^)(GNCConnectionHandlers * _Nonnull))builderBlock {
return [[self alloc] initWithBuilderBlock:builderBlock];
}
@end
@implementation GNCConnectionResultHandlers
- (instancetype)initWithSuccessHandler:(GNCConnectionHandler)successHandler
failureHandler:(GNCConnectionFailureHandler)failureHandler {
self = [super init];
if (self) {
_successHandler = successHandler;
_failureHandler = failureHandler;
}
return self;
}
+ (instancetype)successHandler:(GNCConnectionHandler)successHandler
failureHandler:(GNCConnectionFailureHandler)failureHandler {
return [[self alloc] initWithSuccessHandler:successHandler failureHandler:failureHandler];
}
@end
NS_ASSUME_NONNULL_END
Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

@@ -1,355 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
41B7A2C3208F900100EBA53E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B7A2C2208F900100EBA53E /* AppDelegate.m */; };
41B7A2C6208F900100EBA53E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B7A2C5208F900100EBA53E /* ViewController.m */; };
41B7A2C9208F900100EBA53E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 41B7A2C7208F900100EBA53E /* Main.storyboard */; };
41B7A2CB208F900200EBA53E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 41B7A2CA208F900200EBA53E /* Assets.xcassets */; };
41B7A2CE208F900200EBA53E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */; };
41B7A2D1208F900200EBA53E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B7A2D0208F900200EBA53E /* main.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NearbyConnectionsExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
41B7A2C1208F900100EBA53E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
41B7A2C2208F900100EBA53E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
41B7A2C4208F900100EBA53E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
41B7A2C5208F900100EBA53E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
41B7A2C8208F900100EBA53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
41B7A2CA208F900200EBA53E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
41B7A2CD208F900200EBA53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
41B7A2CF208F900200EBA53E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
41B7A2D0208F900200EBA53E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
41B7A2BB208F900100EBA53E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
41B7A2B5208F900000EBA53E = {
isa = PBXGroup;
children = (
41B7A2C0208F900100EBA53E /* NearbyConnectionsExample */,
41B7A2BF208F900100EBA53E /* Products */,
);
sourceTree = "<group>";
};
41B7A2BF208F900100EBA53E /* Products */ = {
isa = PBXGroup;
children = (
41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */,
);
name = Products;
sourceTree = "<group>";
};
41B7A2C0208F900100EBA53E /* NearbyConnectionsExample */ = {
isa = PBXGroup;
children = (
41B7A2C1208F900100EBA53E /* AppDelegate.h */,
41B7A2C2208F900100EBA53E /* AppDelegate.m */,
41B7A2C4208F900100EBA53E /* ViewController.h */,
41B7A2C5208F900100EBA53E /* ViewController.m */,
41B7A2C7208F900100EBA53E /* Main.storyboard */,
41B7A2CA208F900200EBA53E /* Assets.xcassets */,
41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */,
41B7A2CF208F900200EBA53E /* Info.plist */,
41B7A2D0208F900200EBA53E /* main.m */,
);
path = NearbyConnectionsExample;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
41B7A2BD208F900100EBA53E /* NearbyConnectionsExample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 41B7A2D4208F900200EBA53E /* Build configuration list for PBXNativeTarget "NearbyConnectionsExample" */;
buildPhases = (
41B7A2BA208F900100EBA53E /* Sources */,
41B7A2BB208F900100EBA53E /* Frameworks */,
41B7A2BC208F900100EBA53E /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = NearbyConnectionsExample;
productName = NearbyConnectionsExample;
productReference = 41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
41B7A2B6208F900000EBA53E /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = Google;
TargetAttributes = {
41B7A2BD208F900100EBA53E = {
CreatedOnToolsVersion = 9.3;
};
};
};
buildConfigurationList = 41B7A2B9208F900000EBA53E /* Build configuration list for PBXProject "NearbyConnectionsExample" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 41B7A2B5208F900000EBA53E;
productRefGroup = 41B7A2BF208F900100EBA53E /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
41B7A2BD208F900100EBA53E /* NearbyConnectionsExample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
41B7A2BC208F900100EBA53E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
41B7A2CE208F900200EBA53E /* LaunchScreen.storyboard in Resources */,
41B7A2CB208F900200EBA53E /* Assets.xcassets in Resources */,
41B7A2C9208F900100EBA53E /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
41B7A2BA208F900100EBA53E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
41B7A2C6208F900100EBA53E /* ViewController.m in Sources */,
41B7A2D1208F900200EBA53E /* main.m in Sources */,
41B7A2C3208F900100EBA53E /* AppDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
41B7A2C7208F900100EBA53E /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
41B7A2C8208F900100EBA53E /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
41B7A2CD208F900200EBA53E /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
41B7A2D2208F900200EBA53E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
41B7A2D3208F900200EBA53E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
41B7A2D5208F900200EBA53E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
INFOPLIST_FILE = NearbyConnectionsExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.google.NearbyConnectionsExample;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
41B7A2D6208F900200EBA53E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
INFOPLIST_FILE = NearbyConnectionsExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.google.NearbyConnectionsExample;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
41B7A2B9208F900000EBA53E /* Build configuration list for PBXProject "NearbyConnectionsExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
41B7A2D2208F900200EBA53E /* Debug */,
41B7A2D3208F900200EBA53E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
41B7A2D4208F900200EBA53E /* Build configuration list for PBXNativeTarget "NearbyConnectionsExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
41B7A2D5208F900200EBA53E /* Debug */,
41B7A2D6208F900200EBA53E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 41B7A2B6208F900000EBA53E /* Project object */;
}
@@ -1,34 +0,0 @@
//
// Copyright (c) 2021 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// AppDelegate.h
// NearbyConnectionsExample
//
#import <UIKit/UIKit.h>
/**
* A delegate for NSApplication to handle notifications about app launch and
* shutdown. Owned by the application object.
*/
@interface AppDelegate : UIResponder <UIApplicationDelegate>
/**
* Main screen window displayed to the user which contains any active view
* hierarchy.
*/
@property(nonatomic) UIWindow *window;
@end
@@ -1,23 +0,0 @@
//
// Copyright (c) 2021 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// AppDelegate.m
// NearbyConnectionsExample
//
#import "AppDelegate.h"
@implementation AppDelegate
@end
@@ -1,98 +0,0 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="fos-Uz-R9B">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="BVh-fg-s4x">
<objects>
<viewController id="Tuj-3T-cB2" customClass="ViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="9NC-2J-1xS">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="wwJ-gb-kZn"/>
</view>
<navigationItem key="navigationItem" id="ega-4i-ebx"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xJQ-q8-wWg" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="433" y="-133"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="Jcj-tp-czJ">
<objects>
<navigationController id="fos-Uz-R9B" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="x6x-US-muI">
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="Tuj-3T-cB2" kind="relationship" relationship="rootViewController" id="Xa2-HD-mUn"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="99I-fh-ObP" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-455" y="-132"/>
</scene>
</scenes>
</document>
@@ -1,51 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>Exchange data with nearby devices running the NearbyConnectionsExmaple app.</string>
<key>NSBonjourServices</key>
<array>
<string>_54167B379724._tcp</string>
</array>
</dict>
</plist>
@@ -1,24 +0,0 @@
//
// Copyright (c) 2021 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ViewController.h
// NearbyConnectionsExample
//
#import <UIKit/UIKit.h>
/** View controller for demo by loading NearbyConnections lib for advertiser and discoverer. */
@interface ViewController : UIViewController
@end
@@ -1,291 +0,0 @@
//
// Copyright (c) 2021 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ViewController.m
// NearbyConnectionsExample
//
#import "ViewController.h"
#import <NearbyConnections/NearbyConnections.h>
NS_ASSUME_NONNULL_BEGIN
static NSString *kServiceId = @"com.google.NearbyConnectionsExample";
static NSString *kCellIdentifier = @"endpointCell";
// Simplified version of dispatch_after.
void delay(NSTimeInterval delay, dispatch_block_t block) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)),
dispatch_get_main_queue(), block);
}
// This class contains info about a discovered endpoint.
@interface EndpointInfo : NSObject
@property(nonatomic, readonly) id<GNCDiscoveredEndpointInfo> discInfo;
@property(nonatomic, nullable) id<GNCConnection> connection;
@end
@implementation EndpointInfo
- (instancetype)initWithDiscoveredInfo:(id<GNCDiscoveredEndpointInfo>)discInfo {
self = [super init];
if (self) {
_discInfo = discInfo;
}
return self;
}
@end
@interface ViewController () <UITableViewDataSource, UITableViewDelegate>
@property(nonatomic) GNCAdvertiser *advertiser;
@property(nonatomic) GNCDiscoverer *discoverer;
@property(nonatomic) NSMutableDictionary<GNCEndpointId, EndpointInfo *> *endpoints;
@property(nonatomic, readonly) NSData *ping;
@property(nonatomic, readonly) NSData *pong;
@property(nonatomic) UITableView *tableView;
@property(nonatomic) UITextView *statusView;
@property(nonatomic) NSMutableDictionary<GNCEndpointId, id<GNCConnection> > *incomingConnections;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self makeViews];
// Enable "info" log messages in the release build.
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"GTMVerboseLogging"];
self.title = [[UIDevice currentDevice] name];
NSData *endpointInfo = [self.title dataUsingEncoding:NSUTF8StringEncoding];
_endpoints = [NSMutableDictionary dictionary];
_ping = [@"ping" dataUsingEncoding:NSUTF8StringEncoding];
_pong = [@"pong" dataUsingEncoding:NSUTF8StringEncoding];
_incomingConnections = [NSMutableDictionary dictionary];
// The advertiser.
_advertiser = [GNCAdvertiser
advertiserWithEndpointInfo:endpointInfo
serviceId:kServiceId
strategy:GNCStrategyCluster
connectionInitiationHandler:^(GNCEndpointId endpointId,
id<GNCAdvertiserConnectionInfo> advConnInfo,
GNCConnectionResponseHandler responseHandler) {
// Show a status that a discoverer has requested a connection.
[self logStatus:@"Accepting connection request" final:NO];
// Accept the connection request.
responseHandler(GNCConnectionResponseAccept);
return [GNCConnectionResultHandlers
successHandler:^(id<GNCConnection> connection) {
// Save the connection until the ping-pong sequence is done.
self.incomingConnections[endpointId] = connection;
__block BOOL receivedPong = NO;
// Send a ping, expecting the remote endpoint to send a pong.
[self logStatus:@"Connection established; sending ping" final:NO];
[connection sendBytesPayload:[GNCBytesPayload payloadWithBytes:self.ping]
completion:^(GNCPayloadResult result) {
if (result == GNCPayloadResultSuccess) {
[self logStatus:@"Sent ping; waiting for pong" final:NO];
// Show an error if the pong isn't received in the expected
// timeframe.
delay(3.0, ^{
if (receivedPong) {
[self logStatus:@"Error: Didn't receive pong" final:YES];
[self.incomingConnections
removeObjectForKey:endpointId]; // close the connection
}
});
} else {
[self logStatus:@"Error: Failed to send ping" final:YES];
}
}];
// Return handlers for incoming payloads.
return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
handlers.bytesPayloadHandler = ^(GNCBytesPayload *payload) {
receivedPong = NO;
[self.incomingConnections removeObjectForKey:endpointId]; // close the connection
// Show a status of whether the pong was received.
[self logStatus:[payload.bytes isEqual:self.pong] ? @"Received pong"
: @"Error: Payload is not pong"
final:YES];
};
}];
}
failureHandler:^(GNCConnectionFailure result) {
[self
logStatus:(result == GNCConnectionFailureRejected) ? @"Error: Connection rejected"
: @"Error: Connection failed"
final:YES];
}];
}];
// The discoverer.
__weak typeof(self) weakSelf = self;
_discoverer = [GNCDiscoverer
discovererWithServiceId:kServiceId
strategy:GNCStrategyCluster
endpointFoundHandler:^(GNCEndpointId endpointId,
id<GNCDiscoveredEndpointInfo> endpointInfo) {
typeof(self) self = weakSelf;
// An endpoint was discovered; add it to the endpoint list and UI.
self.endpoints[endpointId] = [[EndpointInfo alloc] initWithDiscoveredInfo:endpointInfo];
[self.tableView reloadData];
// Return the lost handler for this endpoint.
return ^{
typeof(self) self = weakSelf; // shadow
// Endpoint disappeared; remove it from the endpoint list and UI.
[self.endpoints removeObjectForKey:endpointId];
[self.tableView reloadData];
};
}];
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// The user tapped on a cell; request a connection with it.
EndpointInfo *info = _endpoints[_endpoints.allKeys[indexPath.row]];
if (!info) return;
void (^deselectRow)(void) = ^{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
};
[self logStatus:@"Requesting connection" final:NO];
info.discInfo.requestConnection(
self.title,
^(id<GNCDiscovererConnectionInfo> discConnInfo,
GNCConnectionResponseHandler responseHandler) {
// Accept the auth token.
[self logStatus:@"Accepting auth token" final:NO];
responseHandler(GNCConnectionResponseAccept);
return ^(id<GNCConnection> connection) {
// Save the connection until the ping-pong sequence is done.
info.connection = connection;
__block BOOL receivedPing = NO;
[self logStatus:@"Connection established; waiting for ping" final:NO];
// Show an error if the ping isn't received in the expected timeframe.
delay(3.0, ^{
if (!receivedPing) {
deselectRow();
[self logStatus:@"Error: Didn't receive ping" final:YES];
info.connection = nil; // close the connection
}
});
// Return handlers for incoming payloads.
__weak id<GNCConnection> weakConnection = connection; // avoid a retain cycle
return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
handlers.bytesPayloadHandler = ^(GNCBytesPayload *payload) {
receivedPing = YES;
// If a ping was received, send a pong back to the advertiser.
if ([payload.bytes isEqual:self.ping]) {
[self logStatus:@"Received ping; sending pong" final:NO];
[weakConnection sendBytesPayload:[GNCBytesPayload payloadWithBytes:self.pong]
completion:^(GNCPayloadResult result) {
deselectRow();
[self logStatus:(result == GNCPayloadResultSuccess)
? @"Sent pong"
: @"Error: Failed to send pong"
final:YES];
// Pong was sent, so close the connection.
info.connection = nil;
}];
} else {
deselectRow();
[self logStatus:@"Error: Payload is not ping" final:YES];
}
};
}];
};
},
^(GNCConnectionFailure result) {
// Connection failed.
deselectRow();
[self logStatus:(result == GNCConnectionFailureRejected) ? @"Connection rejected"
: @"Connection failed"
final:YES];
});
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier
forIndexPath:indexPath];
cell.textLabel.text = _endpoints[_endpoints.allKeys[indexPath.row]].discInfo.name;
return cell;
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_endpoints.allKeys count];
}
#pragma mark - Private
- (void)makeViews {
_tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kCellIdentifier];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.rowHeight = 48;
_tableView.scrollEnabled = YES;
_tableView.showsVerticalScrollIndicator = YES;
_tableView.userInteractionEnabled = YES;
_tableView.bounces = YES;
_tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:_tableView];
// Make the status view.
UITextView * (^newTextView)(CGRect) = ^(CGRect frame) {
UITextView *textView = [[UITextView alloc] initWithFrame:frame];
textView.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
textView.layer.borderColor = [UIColor blackColor].CGColor;
textView.layer.borderWidth = 1;
textView.editable = NO;
textView.textContainerInset = UIEdgeInsetsZero;
return textView;
};
CGRect selfFrame = self.view.frame;
static const int kStatusHeight = 144;
CGRect statusFrame = (CGRect){{selfFrame.origin.x + 4, selfFrame.size.height - kStatusHeight},
{selfFrame.size.width - 8, kStatusHeight - 4}};
_statusView = newTextView(statusFrame);
[self.view addSubview:_statusView];
}
- (void)logStatus:(NSString *)status final:(BOOL)final {
_statusView.text = [NSString stringWithFormat:@"%@\n%@%@", _statusView.text, status,
final ? @"\n–––––––––––––––––––––––––" : @""];
[_statusView scrollRangeToVisible:NSMakeRange(_statusView.text.length - 1, 1)];
}
@end
NS_ASSUME_NONNULL_END
@@ -1,27 +0,0 @@
//
// Copyright (c) 2021 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// main.m
// NearbyConnectionsExample
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
@@ -1,198 +0,0 @@
# Nearby Connections Sample App for iOS
This is a sample app for third party developers using the Nearby Connections
library. On startup, it advertises and discovers. Discovered advertisers are
added to the list in the UI. When the user taps on an advertiser in the list,
the discoverer requests a connection with it. When the connection is
established, the advertiser sends a "ping" payload to the discoverer, which
sends "pong" payload back to the advertiser. The connection is then closed.
## Setup
1.Get the NearbyConnections_framework.zip from https://github.com/google/nearby/releases/tag/v0.0.1-ios, unzip it,
and put your unzipped folder under your project folder. The directory structure
looks like:
```
/NearbyConnectionsExample
/NearbyConnectionsExample
NearbyConnectionsExample.xcodeproj
README.md
/NearbyConnections.framework
```
2.Import NearbyConnections.framework
- In Xcode, click the NearbyConnectionsExample in the left pane. And click the one in TARGETS-NearbyConnectionsExample at the left of right pane and the Build Phases at the right.
- See the **Link Binary With Libraries**, and press **+** to import the file - **libc++.tbd**.
- In Add **Other…**, import the NearbyConnections.framework folder which was unzipped.
![Import framework in Xcode](./XcodeSetup.png)
3.Update info.plist:
- add **NSLocalNetworkUsageDescription** key with a description of your usage of Nearby Connections.
- add **NSBonjourServices** key.
for NSBonjourServices key, add the bonjour type name: `_54167B379724._tcp`
> **54167B379724** is the 6 byte hash of service id **com.google.NearbyConnectionsExample**
```
// NearbyConnectionsExample/info.plist
...
<key>NSLocalNetworkUsageDescription</key>
<string>Exchange data with nearby devices running the NearbyConnectionsExmaple app.</string>
<key>NSBonjourServices</key>
<array>
<string>_54167B379724._tcp</string>
</array>
...
```
4.Add service id and import `<NearbyConnections/NearbyConnections.h>` in your main view controller.
```
// NearbyConnectionsExample/ViewController.m
static NSString *kServiceId = @"com.google.NearbyConnectionsExample";
#import <NearbyConnections/NearbyConnections.h>
```
## Code Snippets
Note: All of the callbacks in this library use blocks rather than delegates. Be careful to avoid retain cycles in your block implementations. See the [Apple documentation](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW1) describing how to avoid retain cycles.
Here is the skeleton code for an advertiser:
```objc
_advertiser = [GNCAdvertiser
advertiserWithEndpointInfo:endpointInfo
serviceId:myServiceId
strategy:GNCStrategyCluster
connectionInitiationHandler:^(GNCEndpointId endpointId,
id<GNCAdvertiserConnectionInfo> connectionInfo,
GNCConnectionResponseHandler responseHandler) {
// Decide whether to accept or reject the connection. The following code would normally
// exist in the callback for an alert, for instance.
if (/* user rejected */) {
responseHandler(GNCConnectionResponseReject); // the user rejected the invitation
} else {
responseHandler(GNCConnectionResponseAccept); // the user accepted the invitation
// Return connection result handlers, one of which is called depending on
// whether a successful connection was made.
return [GNCConnectionResultHandlers successHandler:^(id<GNCConnection> connection) {
// A successful connection was made. Save the connection somewhere, which can
// be used to send payloads to the remote endpoint.
// Return the incoming payload and disconnect handlers.
return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
// Optionally set the Bytes payload handler.
handlers.bytesPayloadHandler = ^(GNCBytesPayload *payload) {
// Process the payload received from the remote endpoint.
};
// Optionally set the Stream payload handler.
handlers.streamPayloadHandler = ^(GNCStreamPayload *payload, NSProgress *progress) {
// Receipt of a Stream payload has started. Input can be read from the payloads
// NSInputStream, and progress/cancellation is handled via the NSProgress object.
return ^(GNCPayloadResult result) {
if (result == GNCPayloadResultSuccess) {
// The payload has been successfully received.
}
};
};
// Optionally set the disconnected handler.
handlers.disconnectedHandler = ^(GNCDisconnectedReason reason) {
// The connection was severed by either endpoint or lost.
};
}
failureHandler:^(GNCConnectionFailure result) {
// Failed to make the connection.
}]);
}
}];
```
Here is the skeleton code for a discoverer:
```objc
_discoverer =
[GNCDiscoverer discovererWithServiceId:myServiceId
strategy:GNCStrategyCluster
endpointFoundHandler:^(GNCEndpointId endpointId,
id<GNCDiscoveredEndpointInfo> discEndpointInfo) {
// An endpoint was found. Typically you would add it to a list of nearby endpoints
// displayed in a UITableView, for instance.
// The following code shows how to request a connection with the endpoint. This code
// would normally exist in the -didSelectRowAtIndexPath: of UITableViewDelegate.
if (/* user wants to request a connection */) {
requestHandler(myName,
// This block is called once an authentication string is generated between the endpoints.
^(id<GNCDiscovererConnectionInfo> discConnInfo, GNCConnectionResponseHandler responseHandler) {
// Ask the user to confirm the authentication string.
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Accept auth?" ...];
[alert addAction:[UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
responseHandler(GNCConnectionResponseAccept);
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
responseHandler(GNCConnectionResponseReject);
}]];
[self presentViewController:alert animated:YES completion:^{}];
// Return a block that's called if the connection was successful.
return ^(id<GNCConnection> connection) {
// A successful connection was made. Save the connection somewhere, which can
// be used to send payloads to the remote endpoint.
// Return incoming data handlers as in the advertiser example above.
return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
// Set up payload and disconnect handlers here as in the advertiser example above.
}];
};
},
// This block is called if the connection failed for any reason.
^(GNCConnectionFailure result) {
// Typically an alert would be shown here explaining why the connection failed.
});
}
// Return the endpoint-lost handler, which is called when the endpoint goes out of range
// or stops advertising.
return ^{
// The endpoint disappeared.
};
}];
```
Here is an example of how to send a Bytes payload. The returned NSProgress object can be passed to UIProgressView to display a progress bar.
```objc
NSProgress *progress = [connection
sendBytesPayload:[GNCBytesPayload payloadWithBytes:someData]
completion:^(GNCPayloadResult result) {
// Check status to see if it was successfully sent.
}];
```
## Build and run
![Sucessful running screenshot](./NearbyConnectionsExample.png)
If you meet the following error in the debug panel of Xcode, you likely need to set up the keys listed in step 3 **Update info.plist**, as well as the service type.
```
1970-01-01 00:00:00.000 NearbyConnectionsExample[1383/0x16d87b000] [lvl=1] -[GNCMBonjourService netService:didNotPublish:] Error publishing: service: <NSNetService 0x283a18920> local _307BEAB11028._tcp. IjFQWEUwe-oAAA 51898, errorDic: {
NSNetServicesErrorCode = "-72008";
NSNetServicesErrorDomain = 10;
}
```
---
NOTE: The iOS simulator is unstable when advertising. We recommend using a real iOS device.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

@@ -1,84 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
#import "GNCConnection.h"
NS_ASSUME_NONNULL_BEGIN
/** This contains info about a discoverer endpoint intitiating a connection with an advertiser. */
@protocol GNCAdvertiserConnectionInfo <NSObject>
/** Information advertised by the remote endpoint. */
@property(nonatomic, readonly, copy) NSData *endpointInfo;
/** This token can be used to verify the identity of the discoverer. */
@property(nonatomic, readonly, copy) NSString *authToken;
@end
/** This class contains success and failure handlers for the connection request. */
@interface GNCConnectionResultHandlers : NSObject
/**
* This factory method creates a pair of handlers for a successful or failed connection.
*
* @param successHandler This handler is called if both endpoints accept the connection.
* A @c GNCConnection object is passed, meaning that the connection has
* been established and you may start sending and receiving payloads.
* @param failureHandler This handler is called if either endpoint rejects the connection.
*/
+ (instancetype)successHandler:(GNCConnectionHandler)successHandler
failureHandler:(GNCConnectionFailureHandler)failureHandler;
@end
/**
* This handler is called when a discoverer requests a connection with an advertiser. In
* response, the advertiser should accept or reject via @c responseHandler.
*
* @param endpointId The ID of the endpoint.
* @param connectionInfo Information about the discoverer.
* @param responseHandler Handler for the connection response, which is either an acceptance or
* rejection of the connection request.
* @return Handlers for the final connection result. This will be called as soon as the final
* connection result is known, when either side rejects or both sides accept.
*/
typedef GNCConnectionResultHandlers *_Nonnull (^GNCAdvertiserConnectionInitiationHandler)(
GNCEndpointId endpointId, id<GNCAdvertiserConnectionInfo> connectionInfo,
GNCConnectionResponseHandler responseHandler);
/**
* An advertiser broadcasts a service that can be seen by discoverers, which can then make
* requests to connect to it. Release the advertiser object to stop advertising.
*/
@interface GNCAdvertiser : NSObject
/**
* Factory method that creates an advertiser.
*
* @param endpointInfo A data for endpoint info which contains readable name of this endpoint,
* to be displayed on other endpoints.
* @param serviceId A string that uniquely identifies the advertised service.
* @param strategy The connection topology to use.
* @param connectionInitiationHandler A handler that is called when a discoverer requests a
* connection with this endpoint.
*/
+ (instancetype)advertiserWithEndpointInfo:(NSData *)endpointInfo
serviceId:(NSString *)serviceId
strategy:(GNCStrategy)strategy
connectionInitiationHandler:
(GNCAdvertiserConnectionInitiationHandler)connectionInitiationHandler;
@end
NS_ASSUME_NONNULL_END
@@ -1,167 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class GNCBytesPayload, GNCStreamPayload, GNCFilePayload;
/** Response to a connection request. */
typedef NS_ENUM(NSInteger, GNCConnectionResponse) {
GNCConnectionResponseReject, // reject the connection request
GNCConnectionResponseAccept, // accept the connection request
};
/** Reason for a failed connection request. */
typedef NS_ENUM(NSInteger, GNCConnectionFailure) {
GNCConnectionFailureRejected, // an endpoint rejected the connection request
GNCConnectionFailureUnknown, // there was an error while attempting to make the connection
};
/** Handler for a @c GNCConnectionFailure value. */
typedef void (^GNCConnectionFailureHandler)(GNCConnectionFailure);
/** Reasons that a connection can be severed by either endpoint. */
typedef NS_ENUM(NSInteger, GNCDisconnectedReason) {
GNCDisconnectedReasonUnknown, // the endpoint can no longer be reached
};
/** Handler for a @c GNCDisconnectedReason value. */
typedef void (^GNCDisconnectedHandler)(GNCDisconnectedReason);
/** Result of a payload transfer. */
typedef NS_ENUM(NSInteger, GNCPayloadResult) {
GNCPayloadResultSuccess, // Payload delivery was successful.
GNCPayloadResultFailure, // An error occurred during payload delivery.
GNCPayloadResultCanceled, // Payload delivery was canceled.
};
/** Handler for a @c GNCPayloadResult value. */
typedef void (^GNCPayloadResultHandler)(GNCPayloadResult);
/** Connection topology. See https://developers.google.com/nearby/connections/strategies. */
typedef NS_ENUM(NSInteger, GNCStrategy) {
GNCStrategyCluster, // M-to-N
GNCStrategyStar, // 1-to-N
GNCStrategyPointToPoint, // 1-to-1
};
/** Every endpoint has a unique identifier. */
typedef NSString *GNCEndpointId;
/** This handler receives a Bytes payload. It is called when the payload data is fully received. */
typedef void (^GNCBytesPayloadHandler)(GNCBytesPayload *payload);
/**
* This handler receives a Stream payload, signifying the start of receipt of a stream. The payload
* data should be read from the supplied input stream. The progress object can be used to monitor
* progress or cancel the operation. This handler must return a completion handler, which is
* called when the operation is finished.
*/
typedef GNCPayloadResultHandler _Nonnull (^GNCStreamPayloadHandler)(GNCStreamPayload *payload,
NSProgress *progress);
/**
* This handler receives a File payload, signifying the start of receipt of a file. The
* progress object can be used to monitor progress or cancel the operation. This handler must
* return a completion handler, which is called when the operation finishes successfully or if
* there is an error. The file will be stored in a temporary location. If an error occurs or the
* operation is canceled, the file will contain all data that was received. It is the client's
* responsibility to delete the file when it is no longer needed.
*/
typedef GNCPayloadResultHandler _Nonnull (^GNCFilePayloadHandler)(GNCFilePayload *payload,
NSProgress *progress);
/** This class contains optional handlers for a connection. */
@interface GNCConnectionHandlers : NSObject
/**
* This handler receives Bytes payloads. It is optional; apps that don't send and receive Bytes
* payloads need not supply this handler.
*/
@property(nonatomic, nullable) GNCBytesPayloadHandler bytesPayloadHandler;
/**
* This handler receives a stream that delivers a payload in chunks. It is optional; apps that
* don't send and receive Stream payloads need not supply this handler.
*/
@property(nonatomic, nullable) GNCStreamPayloadHandler streamPayloadHandler;
/**
* This handler receives a File payload. It is optional; apps that don't send and receive File
* payloads need not supply this handler.
* Note: File payloads are not yet supported.
*/
@property(nonatomic, nullable) GNCFilePayloadHandler filePayloadHandler;
/**
* This handler is called when the connection is ended, whether due to the endpoint disconnecting
* or moving out of range. It is optional.
*/
@property(nonatomic, nullable) GNCDisconnectedHandler disconnectedHandler;
/**
* This factory method lets you specify a subset of the connection handlers in a single expression.
*
* @param builderBlock Set up the handlers in this block.
*/
+ (instancetype)handlersWithBuilder:(void (^)(GNCConnectionHandlers *))builderBlock;
@end
/**
* This represents a connection with an endpoint. Use it to send payloads to the endpoint, and
* release it to disconnect.
*/
@protocol GNCConnection <NSObject>
/**
* Send a Bytes payload. A progress object is returned, which can be used to monitor
* progress or cancel the operation. |completion| will be called when the operation completes
* (in all cases, even if failed or was canceled).
*/
- (NSProgress *)sendBytesPayload:(GNCBytesPayload *)payload
completion:(GNCPayloadResultHandler)completion;
/**
* Send a Stream payload. A progress object is returned, which can be used to monitor
* progress or cancel the operation. The stream data is read from the supplied NSInputStream.
* |completion| will be called when the operation completes.
*/
- (NSProgress *)sendStreamPayload:(GNCStreamPayload *)payload
completion:(GNCPayloadResultHandler)completion;
/**
* Send a File payload. A progress object is returned, which can be used to monitor progress or
* cancel the operation. |completion| will be called when the operation completes.
* Note: File payloads are not yet supported.
*/
- (NSProgress *)sendFilePayload:(GNCFilePayload *)payload
completion:(GNCPayloadResultHandler)completion;
@end
/**
* This handler takes a @c GNCConnection object and returns a @c GNCConnectionHandlers
* object containing the desired payload and connection-ended handlers.
*/
typedef GNCConnectionHandlers *_Nonnull (^GNCConnectionHandler)(id<GNCConnection> connection);
/**
* This handler takes a response to a connection request. Pass @c GNCConnectionResponseAccept to
* accept the request and @c GNCConnectionResponseReject to reject it.
*/
typedef void (^GNCConnectionResponseHandler)(GNCConnectionResponse response);
NS_ASSUME_NONNULL_END
@@ -1,20 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Umbrella header file for Nearby Connections library.
#import "GNCAdvertiser.h"
#import "GNCConnection.h"
#import "GNCDiscoverer.h"
#import "GNCPayload.h"
@@ -1,96 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
#import "GNCConnection.h"
NS_ASSUME_NONNULL_BEGIN
/** This is info about an advertiser endpoint with which the discoverer has requested a connection.
*/
@protocol GNCDiscovererConnectionInfo <NSObject>
/** This token can be used to verify the identity of the advertiser. */
@property(nonatomic, readonly, copy) NSString *authToken;
@end
/**
* This handler is called to establish authorization with the advertiser. In response,
* @c responseHandler should be called to accept or reject the connection.
*
* @param connectionInfo Information about the advertiser.
* @param responseHandler Handler for the connection response, which is either an acceptance or
* rejection of the connection request.
* @return Handler for the connection if it was successful.
*/
typedef GNCConnectionHandler _Nonnull (^GNCDiscovererConnectionInitializationHandler)(
id<GNCDiscovererConnectionInfo> connectionInfo, GNCConnectionResponseHandler responseHandler);
/**
* This handler should be called to request a connection with an advertiser.
*
* @param endpointInfo A data for endpoint info which contains readable name of this endpoint,
* to be displayed on other endpoints.
* @param authorizationHandler This handler is called to establish authorization.
* @param failureHandler This handler is called if there was an error making the connection.
*/
typedef void (^GNCConnectionRequester)(
NSData *endpointInfo,
GNCDiscovererConnectionInitializationHandler connectionAuthorizationHandler,
GNCConnectionFailureHandler failureHandler);
/** Information about an endpoint when it's discovered. */
@protocol GNCDiscoveredEndpointInfo <NSObject>
/** The human readable name of the remote endpoint. */
@property(nonatomic, readonly, copy) NSString *endpointName;
/** Information advertised by the remote endpoint. */
@property(nonatomic, readonly, copy) NSData *endpointInfo;
/** Call this block to request a connection with the advertiser. */
@property(nonatomic, readonly) GNCConnectionRequester requestConnection;
@end
/** This handler is called when a previously discovered advertiser endpoint is lost. */
typedef void (^GNCEndpointLostHandler)(void);
/**
* This handler is called when an advertiser endpoint is discovered.
*
* @param endpointId The ID of the endpoint.
* @param connectionInfo Information about the endpoint.
* @return Block that is called when the endpoint is lost.
*/
typedef GNCEndpointLostHandler _Nonnull (^GNCEndpointFoundHandler)(
GNCEndpointId endpointId, id<GNCDiscoveredEndpointInfo> endpointInfo);
/**
* A discoverer searches for endpoints advertising the specified service, and allows connection
* requests to be sent to them. Release the discoverer object to stop discovering.
*/
@interface GNCDiscoverer : NSObject
/**
* Factory method that creates a discoverer.
*
* @param serviceId A string that uniquely identifies the advertised service to search for.
* @param strategy The connection topology to use.
* @param endpointFoundHandler This handler is called when an endpoint advertising the service is
* discovered.
*/
+ (instancetype)discovererWithServiceId:(NSString *)serviceId
strategy:(GNCStrategy)strategy
endpointFoundHandler:(GNCEndpointFoundHandler)endpointFoundHandler;
@end
NS_ASSUME_NONNULL_END
@@ -1,64 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** This class encapsulates a Bytes payload. */
@interface GNCBytesPayload : NSObject
/** The unique identifier of the payload. */
@property(nonatomic, readonly) int64_t identifier;
/** The content of the payload. */
@property(nonatomic, readonly) NSData *bytes;
/**
* Creates a Bytes payload object.
* Note: To maximize performance, @c bytes is strongly referenced, not copied.
*/
+ (instancetype)payloadWithBytes:(NSData *)bytes;
@end
/** This class encapsulates a Stream payload. */
@interface GNCStreamPayload : NSObject
/** The unique identifier of the payload. */
@property(nonatomic, readonly) int64_t identifier;
/** The payload data is read from this input stream. */
@property(nonatomic, readonly) NSInputStream *stream;
+ (instancetype)payloadWithStream:(NSInputStream *)stream;
@end
/** This class encapsulates a File payload. */
@interface GNCFilePayload : NSObject
/** The unique identifier of the payload. */
@property(nonatomic, readonly) int64_t identifier;
/** A URL that identifies the file. */
@property(nonatomic, readonly, copy) NSURL *fileURL;
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL;
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier;
@end
NS_ASSUME_NONNULL_END