diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.png b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.png new file mode 100644 index 00000000..c495d68b Binary files /dev/null and b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.png differ diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.xcodeproj/project.pbxproj b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..5f57eaff --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample.xcodeproj/project.pbxproj @@ -0,0 +1,355 @@ +// !$*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 = ""; }; + 41B7A2C2208F900100EBA53E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 41B7A2C4208F900100EBA53E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 41B7A2C5208F900100EBA53E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 41B7A2C8208F900100EBA53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 41B7A2CA208F900200EBA53E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 41B7A2CD208F900200EBA53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 41B7A2CF208F900200EBA53E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 41B7A2D0208F900200EBA53E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; +/* 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 = ""; + }; + 41B7A2BF208F900100EBA53E /* Products */ = { + isa = PBXGroup; + children = ( + 41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */, + ); + name = Products; + sourceTree = ""; + }; + 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 = ""; + }; +/* 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 = ""; + }; + 41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 41B7A2CD208F900200EBA53E /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* 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 */; +} diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.h b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.h new file mode 100644 index 00000000..c1b1202c --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.h @@ -0,0 +1,34 @@ +// +// 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 + +/** + * A delegate for NSApplication to handle notifications about app launch and + * shutdown. Owned by the application object. + */ +@interface AppDelegate : UIResponder + +/** + * Main screen window displayed to the user which contains any active view + * hierarchy. + */ +@property(nonatomic) UIWindow *window; + +@end diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.m b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.m new file mode 100644 index 00000000..69022507 --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/AppDelegate.m @@ -0,0 +1,23 @@ +// +// 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 diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "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" + } +} \ No newline at end of file diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/Contents.json b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/LaunchScreen.storyboard b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f83f6fd5 --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/Main.storyboard b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..b39e95c8 --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Base.lproj/Main.storyboard @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Info.plist b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Info.plist new file mode 100644 index 00000000..44aa9d70 --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + NSLocalNetworkUsageDescription + Exchange data with nearby devices running the NearbyConnectionsExmaple app. + NSBonjourServices + + _54167B379724._tcp + + + diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.h b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.h new file mode 100644 index 00000000..59960994 --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.h @@ -0,0 +1,24 @@ +// +// 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 + +/** View controller for demo by loading NearbyConnections lib for advertiser and discoverer. */ +@interface ViewController : UIViewController +@end diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.m b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.m new file mode 100644 index 00000000..f6c8b82a --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/ViewController.m @@ -0,0 +1,291 @@ +// +// 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 + +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 discInfo; +@property(nonatomic, nullable) id connection; +@end + +@implementation EndpointInfo +- (instancetype)initWithDiscoveredInfo:(id)discInfo { + self = [super init]; + if (self) { + _discInfo = discInfo; + } + return self; +} +@end + +@interface ViewController () +@property(nonatomic) GNCAdvertiser *advertiser; +@property(nonatomic) GNCDiscoverer *discoverer; +@property(nonatomic) NSMutableDictionary *endpoints; +@property(nonatomic, readonly) NSData *ping; +@property(nonatomic, readonly) NSData *pong; +@property(nonatomic) UITableView *tableView; +@property(nonatomic) UITextView *statusView; +@property(nonatomic) NSMutableDictionary > *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 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 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 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 discConnInfo, + GNCConnectionResponseHandler responseHandler) { + // Accept the auth token. + [self logStatus:@"Accepting auth token" final:NO]; + responseHandler(GNCConnectionResponseAccept); + return ^(id 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 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 diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/main.m b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/main.m new file mode 100644 index 00000000..3111188c --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/NearbyConnectionsExample/main.m @@ -0,0 +1,27 @@ +// +// 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 +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/README.md b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/README.md new file mode 100755 index 00000000..cb0d038d --- /dev/null +++ b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/README.md @@ -0,0 +1,198 @@ +# 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/tree/master/releases/new/, 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 +... +NSLocalNetworkUsageDescription +Exchange data with nearby devices running the NearbyConnectionsExmaple app. +NSBonjourServices + + _54167B379724._tcp + +... +``` + + +4.Add service id and import `` in your main view controller. + +``` +// NearbyConnectionsExample/ViewController.m + +static NSString *kServiceId = @"com.google.NearbyConnectionsExample"; + +#import +``` + +## 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 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 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 payload’s + // 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 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 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 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: local _307BEAB11028._tcp. IjFQWEUwe-oAAA 51898, errorDic: { + NSNetServicesErrorCode = "-72008"; + NSNetServicesErrorDomain = 10; +} +``` +--- +NOTE: The iOS simulator is unstable when advertising. We recommend using an real iOS device. diff --git a/cpp/platform/impl/ios/Example/NearbyConnectionsExample/XcodeSetup.png b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/XcodeSetup.png new file mode 100644 index 00000000..4f353ffe Binary files /dev/null and b/cpp/platform/impl/ios/Example/NearbyConnectionsExample/XcodeSetup.png differ