├── Multipeer Chat.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcuserdata │ └── bugorbn.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── Multipeer Chat ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Models │ ├── PeerDevice.swift │ └── PermitionRequest.swift ├── Multipeer_ChatApp.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── ViewModels │ └── DeviceFinderViewModel.swift └── Views │ ├── ContentView.swift │ └── MessengerView.swift └── Multipeer-Chat-Info.plist /Multipeer Chat.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CC91E5B72A7C1347007810E8 /* Multipeer_ChatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC91E5B62A7C1347007810E8 /* Multipeer_ChatApp.swift */; }; 11 | CC91E5B92A7C1347007810E8 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC91E5B82A7C1347007810E8 /* ContentView.swift */; }; 12 | CC91E5BB2A7C1348007810E8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CC91E5BA2A7C1348007810E8 /* Assets.xcassets */; }; 13 | CC91E5BE2A7C1348007810E8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CC91E5BD2A7C1348007810E8 /* Preview Assets.xcassets */; }; 14 | CC91E5C72A7C377E007810E8 /* PeerDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC91E5C62A7C377E007810E8 /* PeerDevice.swift */; }; 15 | CC91E5C92A7C3794007810E8 /* PermitionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC91E5C82A7C3794007810E8 /* PermitionRequest.swift */; }; 16 | CC91E5CD2A7C37CC007810E8 /* DeviceFinderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC91E5CC2A7C37CC007810E8 /* DeviceFinderViewModel.swift */; }; 17 | CC91E5D22A7C38F9007810E8 /* MessengerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC91E5D12A7C38F9007810E8 /* MessengerView.swift */; }; 18 | CC91E5D52A7E5E3B007810E8 /* SwiftyChat in Frameworks */ = {isa = PBXBuildFile; productRef = CC91E5D42A7E5E3B007810E8 /* SwiftyChat */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | CC91E5B32A7C1347007810E8 /* Multipeer Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Multipeer Chat.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | CC91E5B62A7C1347007810E8 /* Multipeer_ChatApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Multipeer_ChatApp.swift; sourceTree = ""; }; 24 | CC91E5B82A7C1347007810E8 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 25 | CC91E5BA2A7C1348007810E8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | CC91E5BD2A7C1348007810E8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 27 | CC91E5C42A7C17F3007810E8 /* Multipeer-Chat-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Multipeer-Chat-Info.plist"; sourceTree = SOURCE_ROOT; }; 28 | CC91E5C62A7C377E007810E8 /* PeerDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerDevice.swift; sourceTree = ""; }; 29 | CC91E5C82A7C3794007810E8 /* PermitionRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermitionRequest.swift; sourceTree = ""; }; 30 | CC91E5CC2A7C37CC007810E8 /* DeviceFinderViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceFinderViewModel.swift; sourceTree = ""; }; 31 | CC91E5D12A7C38F9007810E8 /* MessengerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessengerView.swift; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | CC91E5B02A7C1347007810E8 /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | CC91E5D52A7E5E3B007810E8 /* SwiftyChat in Frameworks */, 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | CC91E5AA2A7C1347007810E8 = { 47 | isa = PBXGroup; 48 | children = ( 49 | CC91E5B52A7C1347007810E8 /* Multipeer Chat */, 50 | CC91E5B42A7C1347007810E8 /* Products */, 51 | ); 52 | sourceTree = ""; 53 | }; 54 | CC91E5B42A7C1347007810E8 /* Products */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | CC91E5B32A7C1347007810E8 /* Multipeer Chat.app */, 58 | ); 59 | name = Products; 60 | sourceTree = ""; 61 | }; 62 | CC91E5B52A7C1347007810E8 /* Multipeer Chat */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | CC91E5B62A7C1347007810E8 /* Multipeer_ChatApp.swift */, 66 | CC91E5C52A7C3777007810E8 /* Models */, 67 | CC91E5CA2A7C37A4007810E8 /* Views */, 68 | CC91E5CB2A7C37BE007810E8 /* ViewModels */, 69 | CC91E5C42A7C17F3007810E8 /* Multipeer-Chat-Info.plist */, 70 | CC91E5BA2A7C1348007810E8 /* Assets.xcassets */, 71 | CC91E5BC2A7C1348007810E8 /* Preview Content */, 72 | ); 73 | path = "Multipeer Chat"; 74 | sourceTree = ""; 75 | }; 76 | CC91E5BC2A7C1348007810E8 /* Preview Content */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | CC91E5BD2A7C1348007810E8 /* Preview Assets.xcassets */, 80 | ); 81 | path = "Preview Content"; 82 | sourceTree = ""; 83 | }; 84 | CC91E5C52A7C3777007810E8 /* Models */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | CC91E5C62A7C377E007810E8 /* PeerDevice.swift */, 88 | CC91E5C82A7C3794007810E8 /* PermitionRequest.swift */, 89 | ); 90 | path = Models; 91 | sourceTree = ""; 92 | }; 93 | CC91E5CA2A7C37A4007810E8 /* Views */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | CC91E5B82A7C1347007810E8 /* ContentView.swift */, 97 | CC91E5D12A7C38F9007810E8 /* MessengerView.swift */, 98 | ); 99 | path = Views; 100 | sourceTree = ""; 101 | }; 102 | CC91E5CB2A7C37BE007810E8 /* ViewModels */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | CC91E5CC2A7C37CC007810E8 /* DeviceFinderViewModel.swift */, 106 | ); 107 | path = ViewModels; 108 | sourceTree = ""; 109 | }; 110 | /* End PBXGroup section */ 111 | 112 | /* Begin PBXNativeTarget section */ 113 | CC91E5B22A7C1347007810E8 /* Multipeer Chat */ = { 114 | isa = PBXNativeTarget; 115 | buildConfigurationList = CC91E5C12A7C1348007810E8 /* Build configuration list for PBXNativeTarget "Multipeer Chat" */; 116 | buildPhases = ( 117 | CC91E5AF2A7C1347007810E8 /* Sources */, 118 | CC91E5B02A7C1347007810E8 /* Frameworks */, 119 | CC91E5B12A7C1347007810E8 /* Resources */, 120 | ); 121 | buildRules = ( 122 | ); 123 | dependencies = ( 124 | ); 125 | name = "Multipeer Chat"; 126 | packageProductDependencies = ( 127 | CC91E5D42A7E5E3B007810E8 /* SwiftyChat */, 128 | ); 129 | productName = "Multipeer Chat"; 130 | productReference = CC91E5B32A7C1347007810E8 /* Multipeer Chat.app */; 131 | productType = "com.apple.product-type.application"; 132 | }; 133 | /* End PBXNativeTarget section */ 134 | 135 | /* Begin PBXProject section */ 136 | CC91E5AB2A7C1347007810E8 /* Project object */ = { 137 | isa = PBXProject; 138 | attributes = { 139 | BuildIndependentTargetsInParallel = 1; 140 | LastSwiftUpdateCheck = 1430; 141 | LastUpgradeCheck = 1430; 142 | TargetAttributes = { 143 | CC91E5B22A7C1347007810E8 = { 144 | CreatedOnToolsVersion = 14.3.1; 145 | }; 146 | }; 147 | }; 148 | buildConfigurationList = CC91E5AE2A7C1347007810E8 /* Build configuration list for PBXProject "Multipeer Chat" */; 149 | compatibilityVersion = "Xcode 14.0"; 150 | developmentRegion = en; 151 | hasScannedForEncodings = 0; 152 | knownRegions = ( 153 | en, 154 | Base, 155 | ); 156 | mainGroup = CC91E5AA2A7C1347007810E8; 157 | packageReferences = ( 158 | CC91E5D32A7E5E3B007810E8 /* XCRemoteSwiftPackageReference "SwiftyChat" */, 159 | ); 160 | productRefGroup = CC91E5B42A7C1347007810E8 /* Products */; 161 | projectDirPath = ""; 162 | projectRoot = ""; 163 | targets = ( 164 | CC91E5B22A7C1347007810E8 /* Multipeer Chat */, 165 | ); 166 | }; 167 | /* End PBXProject section */ 168 | 169 | /* Begin PBXResourcesBuildPhase section */ 170 | CC91E5B12A7C1347007810E8 /* Resources */ = { 171 | isa = PBXResourcesBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | CC91E5BE2A7C1348007810E8 /* Preview Assets.xcassets in Resources */, 175 | CC91E5BB2A7C1348007810E8 /* Assets.xcassets in Resources */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXResourcesBuildPhase section */ 180 | 181 | /* Begin PBXSourcesBuildPhase section */ 182 | CC91E5AF2A7C1347007810E8 /* Sources */ = { 183 | isa = PBXSourcesBuildPhase; 184 | buildActionMask = 2147483647; 185 | files = ( 186 | CC91E5D22A7C38F9007810E8 /* MessengerView.swift in Sources */, 187 | CC91E5CD2A7C37CC007810E8 /* DeviceFinderViewModel.swift in Sources */, 188 | CC91E5B92A7C1347007810E8 /* ContentView.swift in Sources */, 189 | CC91E5C92A7C3794007810E8 /* PermitionRequest.swift in Sources */, 190 | CC91E5B72A7C1347007810E8 /* Multipeer_ChatApp.swift in Sources */, 191 | CC91E5C72A7C377E007810E8 /* PeerDevice.swift in Sources */, 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | /* End PBXSourcesBuildPhase section */ 196 | 197 | /* Begin XCBuildConfiguration section */ 198 | CC91E5BF2A7C1348007810E8 /* Debug */ = { 199 | isa = XCBuildConfiguration; 200 | buildSettings = { 201 | ALWAYS_SEARCH_USER_PATHS = NO; 202 | CLANG_ANALYZER_NONNULL = YES; 203 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 204 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 205 | CLANG_ENABLE_MODULES = YES; 206 | CLANG_ENABLE_OBJC_ARC = YES; 207 | CLANG_ENABLE_OBJC_WEAK = YES; 208 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_COMMA = YES; 211 | CLANG_WARN_CONSTANT_CONVERSION = YES; 212 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 213 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 214 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INFINITE_RECURSION = YES; 218 | CLANG_WARN_INT_CONVERSION = YES; 219 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 220 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 221 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 222 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 223 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 224 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 225 | CLANG_WARN_STRICT_PROTOTYPES = YES; 226 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 227 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 228 | CLANG_WARN_UNREACHABLE_CODE = YES; 229 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 230 | COPY_PHASE_STRIP = NO; 231 | DEBUG_INFORMATION_FORMAT = dwarf; 232 | ENABLE_STRICT_OBJC_MSGSEND = YES; 233 | ENABLE_TESTABILITY = YES; 234 | GCC_C_LANGUAGE_STANDARD = gnu11; 235 | GCC_DYNAMIC_NO_PIC = NO; 236 | GCC_NO_COMMON_BLOCKS = YES; 237 | GCC_OPTIMIZATION_LEVEL = 0; 238 | GCC_PREPROCESSOR_DEFINITIONS = ( 239 | "DEBUG=1", 240 | "$(inherited)", 241 | ); 242 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 243 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 244 | GCC_WARN_UNDECLARED_SELECTOR = YES; 245 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 246 | GCC_WARN_UNUSED_FUNCTION = YES; 247 | GCC_WARN_UNUSED_VARIABLE = YES; 248 | IPHONEOS_DEPLOYMENT_TARGET = 16.4; 249 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 250 | MTL_FAST_MATH = YES; 251 | ONLY_ACTIVE_ARCH = YES; 252 | SDKROOT = iphoneos; 253 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 254 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 255 | }; 256 | name = Debug; 257 | }; 258 | CC91E5C02A7C1348007810E8 /* Release */ = { 259 | isa = XCBuildConfiguration; 260 | buildSettings = { 261 | ALWAYS_SEARCH_USER_PATHS = NO; 262 | CLANG_ANALYZER_NONNULL = YES; 263 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 264 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_ENABLE_OBJC_WEAK = YES; 268 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 269 | CLANG_WARN_BOOL_CONVERSION = YES; 270 | CLANG_WARN_COMMA = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INFINITE_RECURSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 281 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 284 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 285 | CLANG_WARN_STRICT_PROTOTYPES = YES; 286 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 287 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 288 | CLANG_WARN_UNREACHABLE_CODE = YES; 289 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 292 | ENABLE_NS_ASSERTIONS = NO; 293 | ENABLE_STRICT_OBJC_MSGSEND = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu11; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 16.4; 303 | MTL_ENABLE_DEBUG_INFO = NO; 304 | MTL_FAST_MATH = YES; 305 | SDKROOT = iphoneos; 306 | SWIFT_COMPILATION_MODE = wholemodule; 307 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 308 | VALIDATE_PRODUCT = YES; 309 | }; 310 | name = Release; 311 | }; 312 | CC91E5C22A7C1348007810E8 /* Debug */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 316 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 317 | CODE_SIGN_STYLE = Automatic; 318 | CURRENT_PROJECT_VERSION = 1; 319 | DEVELOPMENT_ASSET_PATHS = "\"Multipeer Chat/Preview Content\""; 320 | DEVELOPMENT_TEAM = APYL42WZVK; 321 | ENABLE_PREVIEWS = YES; 322 | GENERATE_INFOPLIST_FILE = YES; 323 | INFOPLIST_FILE = "Multipeer-Chat-Info.plist"; 324 | INFOPLIST_KEY_NSLocalNetworkUsageDescription = "The app will ask your permission to discover nearby devices for chatting with them"; 325 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 326 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 327 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 328 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 329 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 330 | IPHONEOS_DEPLOYMENT_TARGET = 16.4; 331 | LD_RUNPATH_SEARCH_PATHS = ( 332 | "$(inherited)", 333 | "@executable_path/Frameworks", 334 | ); 335 | MARKETING_VERSION = 1.0; 336 | PRODUCT_BUNDLE_IDENTIFIER = "com.multipeer.Multipeer-Chat"; 337 | PRODUCT_NAME = "$(TARGET_NAME)"; 338 | SWIFT_EMIT_LOC_STRINGS = YES; 339 | SWIFT_VERSION = 5.0; 340 | TARGETED_DEVICE_FAMILY = "1,2"; 341 | }; 342 | name = Debug; 343 | }; 344 | CC91E5C32A7C1348007810E8 /* Release */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 348 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 349 | CODE_SIGN_STYLE = Automatic; 350 | CURRENT_PROJECT_VERSION = 1; 351 | DEVELOPMENT_ASSET_PATHS = "\"Multipeer Chat/Preview Content\""; 352 | DEVELOPMENT_TEAM = APYL42WZVK; 353 | ENABLE_PREVIEWS = YES; 354 | GENERATE_INFOPLIST_FILE = YES; 355 | INFOPLIST_FILE = "Multipeer-Chat-Info.plist"; 356 | INFOPLIST_KEY_NSLocalNetworkUsageDescription = "The app will ask your permission to discover nearby devices for chatting with them"; 357 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 358 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 359 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 360 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 361 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 362 | IPHONEOS_DEPLOYMENT_TARGET = 16.4; 363 | LD_RUNPATH_SEARCH_PATHS = ( 364 | "$(inherited)", 365 | "@executable_path/Frameworks", 366 | ); 367 | MARKETING_VERSION = 1.0; 368 | PRODUCT_BUNDLE_IDENTIFIER = "com.multipeer.Multipeer-Chat"; 369 | PRODUCT_NAME = "$(TARGET_NAME)"; 370 | SWIFT_EMIT_LOC_STRINGS = YES; 371 | SWIFT_VERSION = 5.0; 372 | TARGETED_DEVICE_FAMILY = "1,2"; 373 | }; 374 | name = Release; 375 | }; 376 | /* End XCBuildConfiguration section */ 377 | 378 | /* Begin XCConfigurationList section */ 379 | CC91E5AE2A7C1347007810E8 /* Build configuration list for PBXProject "Multipeer Chat" */ = { 380 | isa = XCConfigurationList; 381 | buildConfigurations = ( 382 | CC91E5BF2A7C1348007810E8 /* Debug */, 383 | CC91E5C02A7C1348007810E8 /* Release */, 384 | ); 385 | defaultConfigurationIsVisible = 0; 386 | defaultConfigurationName = Release; 387 | }; 388 | CC91E5C12A7C1348007810E8 /* Build configuration list for PBXNativeTarget "Multipeer Chat" */ = { 389 | isa = XCConfigurationList; 390 | buildConfigurations = ( 391 | CC91E5C22A7C1348007810E8 /* Debug */, 392 | CC91E5C32A7C1348007810E8 /* Release */, 393 | ); 394 | defaultConfigurationIsVisible = 0; 395 | defaultConfigurationName = Release; 396 | }; 397 | /* End XCConfigurationList section */ 398 | 399 | /* Begin XCRemoteSwiftPackageReference section */ 400 | CC91E5D32A7E5E3B007810E8 /* XCRemoteSwiftPackageReference "SwiftyChat" */ = { 401 | isa = XCRemoteSwiftPackageReference; 402 | repositoryURL = "https://github.com/EnesKaraosman/SwiftyChat"; 403 | requirement = { 404 | kind = exactVersion; 405 | version = 2.4.0; 406 | }; 407 | }; 408 | /* End XCRemoteSwiftPackageReference section */ 409 | 410 | /* Begin XCSwiftPackageProductDependency section */ 411 | CC91E5D42A7E5E3B007810E8 /* SwiftyChat */ = { 412 | isa = XCSwiftPackageProductDependency; 413 | package = CC91E5D32A7E5E3B007810E8 /* XCRemoteSwiftPackageReference "SwiftyChat" */; 414 | productName = SwiftyChat; 415 | }; 416 | /* End XCSwiftPackageProductDependency section */ 417 | }; 418 | rootObject = CC91E5AB2A7C1347007810E8 /* Project object */; 419 | } 420 | -------------------------------------------------------------------------------- /Multipeer Chat.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Multipeer Chat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Multipeer Chat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "gsplayer", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/wxxsw/GSPlayer.git", 7 | "state" : { 8 | "revision" : "aa6dad7943d52f5207f7fcc2ad3e4274583443b8", 9 | "version" : "0.2.26" 10 | } 11 | }, 12 | { 13 | "identity" : "kingfisher", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/onevcat/Kingfisher.git", 16 | "state" : { 17 | "revision" : "c75584ac759cbb16b204d0a7de3ebf53ea6b304d", 18 | "version" : "7.9.0" 19 | } 20 | }, 21 | { 22 | "identity" : "swiftuiektensions", 23 | "kind" : "remoteSourceControl", 24 | "location" : "https://github.com/EnesKaraosman/SwiftUIEKtensions.git", 25 | "state" : { 26 | "revision" : "21421df3266c99b83992face9e1523c8f1acb924", 27 | "version" : "0.2.0" 28 | } 29 | }, 30 | { 31 | "identity" : "swiftychat", 32 | "kind" : "remoteSourceControl", 33 | "location" : "https://github.com/EnesKaraosman/SwiftyChat", 34 | "state" : { 35 | "revision" : "be42d39ba63c54c7c83015b23e6ea3ba6793a0ea", 36 | "version" : "2.4.0" 37 | } 38 | }, 39 | { 40 | "identity" : "videoplayer", 41 | "kind" : "remoteSourceControl", 42 | "location" : "https://github.com/wxxsw/VideoPlayer.git", 43 | "state" : { 44 | "revision" : "3c5b9531ee20e34d3a86142ca2c076a5b99afdb2", 45 | "version" : "1.2.3" 46 | } 47 | }, 48 | { 49 | "identity" : "wrappinghstack", 50 | "kind" : "remoteSourceControl", 51 | "location" : "https://github.com/dkk/WrappingHStack.git", 52 | "state" : { 53 | "revision" : "f31b8fdc57759c5aa5fc98c70746db5556e73300", 54 | "version" : "2.2.10" 55 | } 56 | } 57 | ], 58 | "version" : 2 59 | } 60 | -------------------------------------------------------------------------------- /Multipeer Chat.xcodeproj/xcuserdata/bugorbn.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /Multipeer Chat.xcodeproj/xcuserdata/bugorbn.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Multipeer Chat.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Multipeer Chat/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Multipeer Chat/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Multipeer Chat/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Multipeer Chat/Models/PeerDevice.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeerDevice.swift 3 | // Multipeer Chat 4 | // 5 | // Created by Boris Bugor on 03/08/2023. 6 | // 7 | 8 | import Foundation 9 | import MultipeerConnectivity 10 | 11 | struct PeerDevice: Identifiable, Hashable { 12 | let id = UUID() 13 | let peerId: MCPeerID 14 | } 15 | -------------------------------------------------------------------------------- /Multipeer Chat/Models/PermitionRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PermitionRequest.swift 3 | // Multipeer Chat 4 | // 5 | // Created by Boris Bugor on 03/08/2023. 6 | // 7 | 8 | import Foundation 9 | import MultipeerConnectivity 10 | 11 | struct PermitionRequest: Identifiable { 12 | let id = UUID() 13 | let peerId: MCPeerID 14 | let onRequest: (Bool) -> Void 15 | } 16 | -------------------------------------------------------------------------------- /Multipeer Chat/Multipeer_ChatApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Multipeer_ChatApp.swift 3 | // Multipeer Chat 4 | // 5 | // Created by Boris Bugor on 03/08/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct Multipeer_ChatApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Multipeer Chat/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Multipeer Chat/ViewModels/DeviceFinderViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceFinderViewModel.swift 3 | // Multipeer Chat 4 | // 5 | // Created by Boris Bugor on 03/08/2023. 6 | // 7 | 8 | import MultipeerConnectivity 9 | import SwiftyChat 10 | import Combine 11 | 12 | class DeviceFinderViewModel: NSObject, ObservableObject { 13 | private let advertiser: MCNearbyServiceAdvertiser 14 | private let browser: MCNearbyServiceBrowser 15 | private let session: MCSession 16 | private let serviceType = "nearby-devices" 17 | 18 | @Published var permissionRequest: PermitionRequest? 19 | 20 | @Published var selectedPeer: PeerDevice? { 21 | didSet { 22 | connect() 23 | } 24 | } 25 | @Published var peers: [PeerDevice] = [] 26 | @Published var isAdvertised: Bool = false { 27 | didSet { 28 | isAdvertised ? advertiser.startAdvertisingPeer() : advertiser.stopAdvertisingPeer() 29 | } 30 | } 31 | 32 | @Published var messages: [MockMessages.ChatMessageItem] = [] 33 | let messagePublisher = PassthroughSubject() 34 | var subscriptions = Set() 35 | 36 | func send(draft: ChatMessageKind) { 37 | guard case let .text(string) = draft else { 38 | return 39 | } 40 | 41 | guard let data = string.data(using: .utf8) else { 42 | return 43 | } 44 | 45 | try? session.send(data, toPeers: [joinedPeer.last!.peerId], with: .reliable) 46 | 47 | messagePublisher.send( 48 | .init(user: MockMessages.sender, messageKind: draft, isSender: true) 49 | ) 50 | } 51 | 52 | @Published var joinedPeer: [PeerDevice] = [] 53 | 54 | override init() { 55 | let peer = MCPeerID(displayName: UIDevice.current.name) 56 | session = MCSession(peer: peer) 57 | 58 | advertiser = MCNearbyServiceAdvertiser( 59 | peer: peer, 60 | discoveryInfo: nil, 61 | serviceType: serviceType 62 | ) 63 | 64 | browser = MCNearbyServiceBrowser(peer: peer, serviceType: serviceType) 65 | 66 | super.init() 67 | 68 | advertiser.delegate = self 69 | browser.delegate = self 70 | session.delegate = self 71 | 72 | messagePublisher 73 | .receive(on: DispatchQueue.main) 74 | .sink { [weak self] in 75 | self?.messages.append($0) 76 | } 77 | .store(in: &subscriptions) 78 | } 79 | 80 | func startBrowsing() { 81 | browser.startBrowsingForPeers() 82 | } 83 | 84 | func finishBrowsing() { 85 | browser.stopBrowsingForPeers() 86 | } 87 | 88 | func show(peerId: MCPeerID) { 89 | guard let first = peers.first(where: { $0.peerId == peerId }) else { 90 | return 91 | } 92 | 93 | joinedPeer.append(first) 94 | } 95 | 96 | private func connect() { 97 | guard let selectedPeer else { 98 | return 99 | } 100 | 101 | if session.connectedPeers.contains(selectedPeer.peerId) { 102 | joinedPeer.append(selectedPeer) 103 | } else { 104 | browser.invitePeer(selectedPeer.peerId, to: session, withContext: nil, timeout: 60) 105 | } 106 | } 107 | } 108 | 109 | extension DeviceFinderViewModel: MCNearbyServiceAdvertiserDelegate { 110 | func advertiser( 111 | _ advertiser: MCNearbyServiceAdvertiser, 112 | didReceiveInvitationFromPeer peerID: MCPeerID, 113 | withContext context: Data?, 114 | invitationHandler: @escaping (Bool, MCSession?) -> Void 115 | ) { 116 | permissionRequest = PermitionRequest( 117 | peerId: peerID, 118 | onRequest: { [weak self] permission in 119 | invitationHandler(permission, permission ? self?.session : nil) 120 | } 121 | ) 122 | } 123 | } 124 | 125 | extension DeviceFinderViewModel: MCNearbyServiceBrowserDelegate { 126 | func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) { 127 | peers.append(PeerDevice(peerId: peerID)) 128 | } 129 | 130 | func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) { 131 | peers.removeAll(where: { $0.peerId == peerID }) 132 | } 133 | } 134 | 135 | extension DeviceFinderViewModel: MCSessionDelegate { 136 | func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { 137 | // 138 | } 139 | 140 | func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { 141 | guard let last = joinedPeer.last, last.peerId == peerID, let message = String(data: data, encoding: .utf8) else { 142 | return 143 | } 144 | 145 | messagePublisher.send( 146 | .init(user: MockMessages.sender, messageKind: .text(message), isSender: false) 147 | ) 148 | } 149 | 150 | func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { 151 | // 152 | } 153 | 154 | func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { 155 | // 156 | } 157 | 158 | func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { 159 | // 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /Multipeer Chat/Views/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Multipeer Chat 4 | // 5 | // Created by Boris Bugor on 03/08/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | @StateObject var model = DeviceFinderViewModel() 12 | 13 | var body: some View { 14 | NavigationStack(path: $model.joinedPeer) { 15 | List(model.peers) { peer in 16 | HStack { 17 | Image(systemName: "iphone.gen1") 18 | .imageScale(.large) 19 | .foregroundColor(.accentColor) 20 | 21 | Text(peer.peerId.displayName) 22 | .frame(maxWidth: .infinity, alignment: .leading) 23 | } 24 | .padding(.vertical, 5) 25 | .onTapGesture { 26 | model.selectedPeer = peer 27 | } 28 | } 29 | .alert(item: $model.permissionRequest, content: { request in 30 | Alert( 31 | title: Text("Do you want to join \(request.peerId.displayName)"), 32 | primaryButton: .default(Text("Yes"), action: { 33 | request.onRequest(true) 34 | model.show(peerId: request.peerId) 35 | }), 36 | secondaryButton: .cancel(Text("No"), action: { 37 | request.onRequest(false) 38 | }) 39 | ) 40 | }) 41 | .navigationDestination(for: PeerDevice.self, destination: { peer in 42 | MessengerView() 43 | .environmentObject(model) 44 | }) 45 | .onAppear { 46 | model.startBrowsing() 47 | } 48 | .onDisappear { 49 | model.finishBrowsing() 50 | } 51 | .toolbar { 52 | ToolbarItem(placement: .navigationBarTrailing) { 53 | Toggle("Press to be discoverable", isOn: $model.isAdvertised) 54 | .toggleStyle(.switch) 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | struct ContentView_Previews: PreviewProvider { 62 | static var previews: some View { 63 | ContentView() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Multipeer Chat/Views/MessengerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessengerView.swift 3 | // Multipeer Chat 4 | // 5 | // Created by Boris Bugor on 03/08/2023. 6 | // 7 | 8 | import SwiftUI 9 | import SwiftyChat 10 | 11 | struct MessengerView: View { 12 | @EnvironmentObject var model: DeviceFinderViewModel 13 | @State var message: String = "" 14 | @State var isEditing: Bool = false 15 | 16 | var inputBarView: some View { 17 | BasicInputView( 18 | message: $message, 19 | isEditing: $isEditing, 20 | placeholder: "Type something", 21 | onCommit: { messageKind in 22 | self.model.send(draft: messageKind) 23 | } 24 | ) 25 | .padding(8) 26 | .padding(.bottom, 50) 27 | .background(Color.primary.colorInvert()) 28 | .eraseToAnyView() 29 | } 30 | 31 | 32 | var body: some View { 33 | ChatView(messages: $model.messages) { 34 | inputBarView.eraseToAnyView() 35 | } 36 | .environmentObject( 37 | ChatMessageCellStyle() 38 | ) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Multipeer-Chat-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSBonjourServices 6 | 7 | _nearby-devices._tcp 8 | _nearby-devices._upd 9 | 10 | 11 | 12 | --------------------------------------------------------------------------------