├── README.md └── SwiftUIChatDemo ├── SwiftUIChatDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcuserdata │ └── jeroenleenarts.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── SwiftUIChatDemo ├── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── ContentView.swift ├── CustomAttachment.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json └── SwiftUIChatDemoApp.swift ├── SwiftUIChatDemoTests └── SwiftUIChatDemoTests.swift └── SwiftUIChatDemoUITests ├── SwiftUIChatDemoUITests.swift └── SwiftUIChatDemoUITestsLaunchTests.swift /README.md: -------------------------------------------------------------------------------- 1 | # SwiftUI Chat Tutorial Sample 2 | 3 | This repository allows you to check the result after completing each step described in the [SwiftUI Chat Tutorial](https://getstream.io/tutorials/swiftui-chat/). 4 | 5 | In order to run this application you need an account on Stream Chat [here](https://getstream.io/chat/) and a valid user token which you can generate manually from [here](https://getstream.io/chat/docs/ios-swift/token_generator/). 6 | 7 | ## Quick start 8 | 9 | 1. Clone the repository 10 | 2. Open the project in Xcode 11 | 3. Replace `API_KEY`, `USER_ID` and `USER_TOKEN` placeholders with your own 12 | 4. Run the project 13 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A9408AB2754F2F50026FAD4 /* CustomAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A9408AA2754F2F50026FAD4 /* CustomAttachment.swift */; }; 11 | 4AC0167227511264009D8E63 /* SwiftUIChatDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC0167127511264009D8E63 /* SwiftUIChatDemoApp.swift */; }; 12 | 4AC0167427511264009D8E63 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC0167327511264009D8E63 /* ContentView.swift */; }; 13 | 4AC0167627511264009D8E63 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AC0167527511264009D8E63 /* Assets.xcassets */; }; 14 | 4AC0167927511264009D8E63 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4AC0167827511264009D8E63 /* Preview Assets.xcassets */; }; 15 | 4AC0168327511265009D8E63 /* SwiftUIChatDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC0168227511265009D8E63 /* SwiftUIChatDemoTests.swift */; }; 16 | 4AC0168D27511265009D8E63 /* SwiftUIChatDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC0168C27511265009D8E63 /* SwiftUIChatDemoUITests.swift */; }; 17 | 4AC0168F27511265009D8E63 /* SwiftUIChatDemoUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC0168E27511265009D8E63 /* SwiftUIChatDemoUITestsLaunchTests.swift */; }; 18 | 4AC0169D2751130A009D8E63 /* StreamChatSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4AC0169C2751130A009D8E63 /* StreamChatSwiftUI */; }; 19 | 4AC0169F27511371009D8E63 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AC0169E27511371009D8E63 /* AppDelegate.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 4AC0167F27511265009D8E63 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 4AC0166627511264009D8E63 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 4AC0166D27511264009D8E63; 28 | remoteInfo = SwiftUIChatDemo; 29 | }; 30 | 4AC0168927511265009D8E63 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 4AC0166627511264009D8E63 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 4AC0166D27511264009D8E63; 35 | remoteInfo = SwiftUIChatDemo; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 4A9408AA2754F2F50026FAD4 /* CustomAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomAttachment.swift; sourceTree = ""; }; 41 | 4AC0166E27511264009D8E63 /* SwiftUIChatDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUIChatDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 4AC0167127511264009D8E63 /* SwiftUIChatDemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIChatDemoApp.swift; sourceTree = ""; }; 43 | 4AC0167327511264009D8E63 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 44 | 4AC0167527511264009D8E63 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45 | 4AC0167827511264009D8E63 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 46 | 4AC0167E27511264009D8E63 /* SwiftUIChatDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftUIChatDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 4AC0168227511265009D8E63 /* SwiftUIChatDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIChatDemoTests.swift; sourceTree = ""; }; 48 | 4AC0168827511265009D8E63 /* SwiftUIChatDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftUIChatDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 4AC0168C27511265009D8E63 /* SwiftUIChatDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIChatDemoUITests.swift; sourceTree = ""; }; 50 | 4AC0168E27511265009D8E63 /* SwiftUIChatDemoUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIChatDemoUITestsLaunchTests.swift; sourceTree = ""; }; 51 | 4AC0169E27511371009D8E63 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 4AC0166B27511264009D8E63 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | 4AC0169D2751130A009D8E63 /* StreamChatSwiftUI in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | 4AC0167B27511264009D8E63 /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | 4AC0168527511265009D8E63 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | /* End PBXFrameworksBuildPhase section */ 78 | 79 | /* Begin PBXGroup section */ 80 | 4AC0166527511264009D8E63 = { 81 | isa = PBXGroup; 82 | children = ( 83 | 4AC0167027511264009D8E63 /* SwiftUIChatDemo */, 84 | 4AC0168127511265009D8E63 /* SwiftUIChatDemoTests */, 85 | 4AC0168B27511265009D8E63 /* SwiftUIChatDemoUITests */, 86 | 4AC0166F27511264009D8E63 /* Products */, 87 | ); 88 | sourceTree = ""; 89 | }; 90 | 4AC0166F27511264009D8E63 /* Products */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 4AC0166E27511264009D8E63 /* SwiftUIChatDemo.app */, 94 | 4AC0167E27511264009D8E63 /* SwiftUIChatDemoTests.xctest */, 95 | 4AC0168827511265009D8E63 /* SwiftUIChatDemoUITests.xctest */, 96 | ); 97 | name = Products; 98 | sourceTree = ""; 99 | }; 100 | 4AC0167027511264009D8E63 /* SwiftUIChatDemo */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 4AC0169E27511371009D8E63 /* AppDelegate.swift */, 104 | 4AC0167127511264009D8E63 /* SwiftUIChatDemoApp.swift */, 105 | 4AC0167327511264009D8E63 /* ContentView.swift */, 106 | 4A9408AA2754F2F50026FAD4 /* CustomAttachment.swift */, 107 | 4AC0167527511264009D8E63 /* Assets.xcassets */, 108 | 4AC0167727511264009D8E63 /* Preview Content */, 109 | ); 110 | path = SwiftUIChatDemo; 111 | sourceTree = ""; 112 | }; 113 | 4AC0167727511264009D8E63 /* Preview Content */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 4AC0167827511264009D8E63 /* Preview Assets.xcassets */, 117 | ); 118 | path = "Preview Content"; 119 | sourceTree = ""; 120 | }; 121 | 4AC0168127511265009D8E63 /* SwiftUIChatDemoTests */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 4AC0168227511265009D8E63 /* SwiftUIChatDemoTests.swift */, 125 | ); 126 | path = SwiftUIChatDemoTests; 127 | sourceTree = ""; 128 | }; 129 | 4AC0168B27511265009D8E63 /* SwiftUIChatDemoUITests */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 4AC0168C27511265009D8E63 /* SwiftUIChatDemoUITests.swift */, 133 | 4AC0168E27511265009D8E63 /* SwiftUIChatDemoUITestsLaunchTests.swift */, 134 | ); 135 | path = SwiftUIChatDemoUITests; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 4AC0166D27511264009D8E63 /* SwiftUIChatDemo */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 4AC0169227511265009D8E63 /* Build configuration list for PBXNativeTarget "SwiftUIChatDemo" */; 144 | buildPhases = ( 145 | 4AC0166A27511264009D8E63 /* Sources */, 146 | 4AC0166B27511264009D8E63 /* Frameworks */, 147 | 4AC0166C27511264009D8E63 /* Resources */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | ); 153 | name = SwiftUIChatDemo; 154 | packageProductDependencies = ( 155 | 4AC0169C2751130A009D8E63 /* StreamChatSwiftUI */, 156 | ); 157 | productName = SwiftUIChatDemo; 158 | productReference = 4AC0166E27511264009D8E63 /* SwiftUIChatDemo.app */; 159 | productType = "com.apple.product-type.application"; 160 | }; 161 | 4AC0167D27511264009D8E63 /* SwiftUIChatDemoTests */ = { 162 | isa = PBXNativeTarget; 163 | buildConfigurationList = 4AC0169527511265009D8E63 /* Build configuration list for PBXNativeTarget "SwiftUIChatDemoTests" */; 164 | buildPhases = ( 165 | 4AC0167A27511264009D8E63 /* Sources */, 166 | 4AC0167B27511264009D8E63 /* Frameworks */, 167 | 4AC0167C27511264009D8E63 /* Resources */, 168 | ); 169 | buildRules = ( 170 | ); 171 | dependencies = ( 172 | 4AC0168027511265009D8E63 /* PBXTargetDependency */, 173 | ); 174 | name = SwiftUIChatDemoTests; 175 | productName = SwiftUIChatDemoTests; 176 | productReference = 4AC0167E27511264009D8E63 /* SwiftUIChatDemoTests.xctest */; 177 | productType = "com.apple.product-type.bundle.unit-test"; 178 | }; 179 | 4AC0168727511265009D8E63 /* SwiftUIChatDemoUITests */ = { 180 | isa = PBXNativeTarget; 181 | buildConfigurationList = 4AC0169827511265009D8E63 /* Build configuration list for PBXNativeTarget "SwiftUIChatDemoUITests" */; 182 | buildPhases = ( 183 | 4AC0168427511265009D8E63 /* Sources */, 184 | 4AC0168527511265009D8E63 /* Frameworks */, 185 | 4AC0168627511265009D8E63 /* Resources */, 186 | ); 187 | buildRules = ( 188 | ); 189 | dependencies = ( 190 | 4AC0168A27511265009D8E63 /* PBXTargetDependency */, 191 | ); 192 | name = SwiftUIChatDemoUITests; 193 | productName = SwiftUIChatDemoUITests; 194 | productReference = 4AC0168827511265009D8E63 /* SwiftUIChatDemoUITests.xctest */; 195 | productType = "com.apple.product-type.bundle.ui-testing"; 196 | }; 197 | /* End PBXNativeTarget section */ 198 | 199 | /* Begin PBXProject section */ 200 | 4AC0166627511264009D8E63 /* Project object */ = { 201 | isa = PBXProject; 202 | attributes = { 203 | BuildIndependentTargetsInParallel = 1; 204 | LastSwiftUpdateCheck = 1310; 205 | LastUpgradeCheck = 1310; 206 | TargetAttributes = { 207 | 4AC0166D27511264009D8E63 = { 208 | CreatedOnToolsVersion = 13.1; 209 | }; 210 | 4AC0167D27511264009D8E63 = { 211 | CreatedOnToolsVersion = 13.1; 212 | TestTargetID = 4AC0166D27511264009D8E63; 213 | }; 214 | 4AC0168727511265009D8E63 = { 215 | CreatedOnToolsVersion = 13.1; 216 | TestTargetID = 4AC0166D27511264009D8E63; 217 | }; 218 | }; 219 | }; 220 | buildConfigurationList = 4AC0166927511264009D8E63 /* Build configuration list for PBXProject "SwiftUIChatDemo" */; 221 | compatibilityVersion = "Xcode 13.0"; 222 | developmentRegion = en; 223 | hasScannedForEncodings = 0; 224 | knownRegions = ( 225 | en, 226 | Base, 227 | ); 228 | mainGroup = 4AC0166527511264009D8E63; 229 | packageReferences = ( 230 | 4AC0169B2751130A009D8E63 /* XCRemoteSwiftPackageReference "stream-chat-swiftui" */, 231 | ); 232 | productRefGroup = 4AC0166F27511264009D8E63 /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | 4AC0166D27511264009D8E63 /* SwiftUIChatDemo */, 237 | 4AC0167D27511264009D8E63 /* SwiftUIChatDemoTests */, 238 | 4AC0168727511265009D8E63 /* SwiftUIChatDemoUITests */, 239 | ); 240 | }; 241 | /* End PBXProject section */ 242 | 243 | /* Begin PBXResourcesBuildPhase section */ 244 | 4AC0166C27511264009D8E63 /* Resources */ = { 245 | isa = PBXResourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 4AC0167927511264009D8E63 /* Preview Assets.xcassets in Resources */, 249 | 4AC0167627511264009D8E63 /* Assets.xcassets in Resources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | 4AC0167C27511264009D8E63 /* Resources */ = { 254 | isa = PBXResourcesBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | 4AC0168627511265009D8E63 /* Resources */ = { 261 | isa = PBXResourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | /* End PBXResourcesBuildPhase section */ 268 | 269 | /* Begin PBXSourcesBuildPhase section */ 270 | 4AC0166A27511264009D8E63 /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 4AC0169F27511371009D8E63 /* AppDelegate.swift in Sources */, 275 | 4A9408AB2754F2F50026FAD4 /* CustomAttachment.swift in Sources */, 276 | 4AC0167427511264009D8E63 /* ContentView.swift in Sources */, 277 | 4AC0167227511264009D8E63 /* SwiftUIChatDemoApp.swift in Sources */, 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | 4AC0167A27511264009D8E63 /* Sources */ = { 282 | isa = PBXSourcesBuildPhase; 283 | buildActionMask = 2147483647; 284 | files = ( 285 | 4AC0168327511265009D8E63 /* SwiftUIChatDemoTests.swift in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | 4AC0168427511265009D8E63 /* Sources */ = { 290 | isa = PBXSourcesBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | 4AC0168D27511265009D8E63 /* SwiftUIChatDemoUITests.swift in Sources */, 294 | 4AC0168F27511265009D8E63 /* SwiftUIChatDemoUITestsLaunchTests.swift in Sources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | /* End PBXSourcesBuildPhase section */ 299 | 300 | /* Begin PBXTargetDependency section */ 301 | 4AC0168027511265009D8E63 /* PBXTargetDependency */ = { 302 | isa = PBXTargetDependency; 303 | target = 4AC0166D27511264009D8E63 /* SwiftUIChatDemo */; 304 | targetProxy = 4AC0167F27511265009D8E63 /* PBXContainerItemProxy */; 305 | }; 306 | 4AC0168A27511265009D8E63 /* PBXTargetDependency */ = { 307 | isa = PBXTargetDependency; 308 | target = 4AC0166D27511264009D8E63 /* SwiftUIChatDemo */; 309 | targetProxy = 4AC0168927511265009D8E63 /* PBXContainerItemProxy */; 310 | }; 311 | /* End PBXTargetDependency section */ 312 | 313 | /* Begin XCBuildConfiguration section */ 314 | 4AC0169027511265009D8E63 /* Debug */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = NO; 318 | CLANG_ANALYZER_NONNULL = YES; 319 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_ENABLE_OBJC_WEAK = YES; 325 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 326 | CLANG_WARN_BOOL_CONVERSION = YES; 327 | CLANG_WARN_COMMA = YES; 328 | CLANG_WARN_CONSTANT_CONVERSION = YES; 329 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 331 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 332 | CLANG_WARN_EMPTY_BODY = YES; 333 | CLANG_WARN_ENUM_CONVERSION = YES; 334 | CLANG_WARN_INFINITE_RECURSION = YES; 335 | CLANG_WARN_INT_CONVERSION = YES; 336 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 337 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 340 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 342 | CLANG_WARN_STRICT_PROTOTYPES = YES; 343 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 344 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | COPY_PHASE_STRIP = NO; 348 | DEBUG_INFORMATION_FORMAT = dwarf; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | ENABLE_TESTABILITY = YES; 351 | GCC_C_LANGUAGE_STANDARD = gnu11; 352 | GCC_DYNAMIC_NO_PIC = NO; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_OPTIMIZATION_LEVEL = 0; 355 | GCC_PREPROCESSOR_DEFINITIONS = ( 356 | "DEBUG=1", 357 | "$(inherited)", 358 | ); 359 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 360 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 361 | GCC_WARN_UNDECLARED_SELECTOR = YES; 362 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 363 | GCC_WARN_UNUSED_FUNCTION = YES; 364 | GCC_WARN_UNUSED_VARIABLE = YES; 365 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 366 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 367 | MTL_FAST_MATH = YES; 368 | ONLY_ACTIVE_ARCH = YES; 369 | SDKROOT = iphoneos; 370 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 371 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 372 | }; 373 | name = Debug; 374 | }; 375 | 4AC0169127511265009D8E63 /* Release */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | ALWAYS_SEARCH_USER_PATHS = NO; 379 | CLANG_ANALYZER_NONNULL = YES; 380 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 381 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 382 | CLANG_CXX_LIBRARY = "libc++"; 383 | CLANG_ENABLE_MODULES = YES; 384 | CLANG_ENABLE_OBJC_ARC = YES; 385 | CLANG_ENABLE_OBJC_WEAK = YES; 386 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 387 | CLANG_WARN_BOOL_CONVERSION = YES; 388 | CLANG_WARN_COMMA = YES; 389 | CLANG_WARN_CONSTANT_CONVERSION = YES; 390 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 391 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 392 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 393 | CLANG_WARN_EMPTY_BODY = YES; 394 | CLANG_WARN_ENUM_CONVERSION = YES; 395 | CLANG_WARN_INFINITE_RECURSION = YES; 396 | CLANG_WARN_INT_CONVERSION = YES; 397 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 398 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 399 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 400 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 401 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 402 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 403 | CLANG_WARN_STRICT_PROTOTYPES = YES; 404 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 405 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 406 | CLANG_WARN_UNREACHABLE_CODE = YES; 407 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 408 | COPY_PHASE_STRIP = NO; 409 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 410 | ENABLE_NS_ASSERTIONS = NO; 411 | ENABLE_STRICT_OBJC_MSGSEND = YES; 412 | GCC_C_LANGUAGE_STANDARD = gnu11; 413 | GCC_NO_COMMON_BLOCKS = YES; 414 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 415 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 416 | GCC_WARN_UNDECLARED_SELECTOR = YES; 417 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 418 | GCC_WARN_UNUSED_FUNCTION = YES; 419 | GCC_WARN_UNUSED_VARIABLE = YES; 420 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 421 | MTL_ENABLE_DEBUG_INFO = NO; 422 | MTL_FAST_MATH = YES; 423 | SDKROOT = iphoneos; 424 | SWIFT_COMPILATION_MODE = wholemodule; 425 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 426 | VALIDATE_PRODUCT = YES; 427 | }; 428 | name = Release; 429 | }; 430 | 4AC0169327511265009D8E63 /* Debug */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 434 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 435 | CODE_SIGN_STYLE = Automatic; 436 | CURRENT_PROJECT_VERSION = 1; 437 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUIChatDemo/Preview Content\""; 438 | DEVELOPMENT_TEAM = EHV7XZLAHA; 439 | ENABLE_PREVIEWS = YES; 440 | GENERATE_INFOPLIST_FILE = YES; 441 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 442 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 443 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 444 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 445 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 446 | LD_RUNPATH_SEARCH_PATHS = ( 447 | "$(inherited)", 448 | "@executable_path/Frameworks", 449 | ); 450 | MARKETING_VERSION = 1.0; 451 | PRODUCT_BUNDLE_IDENTIFIER = getstream.io.SwiftUIChatDemo; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | SWIFT_EMIT_LOC_STRINGS = YES; 454 | SWIFT_VERSION = 5.0; 455 | TARGETED_DEVICE_FAMILY = "1,2"; 456 | }; 457 | name = Debug; 458 | }; 459 | 4AC0169427511265009D8E63 /* Release */ = { 460 | isa = XCBuildConfiguration; 461 | buildSettings = { 462 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 463 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 464 | CODE_SIGN_STYLE = Automatic; 465 | CURRENT_PROJECT_VERSION = 1; 466 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUIChatDemo/Preview Content\""; 467 | DEVELOPMENT_TEAM = EHV7XZLAHA; 468 | ENABLE_PREVIEWS = YES; 469 | GENERATE_INFOPLIST_FILE = YES; 470 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 471 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 472 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 473 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 474 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 475 | LD_RUNPATH_SEARCH_PATHS = ( 476 | "$(inherited)", 477 | "@executable_path/Frameworks", 478 | ); 479 | MARKETING_VERSION = 1.0; 480 | PRODUCT_BUNDLE_IDENTIFIER = getstream.io.SwiftUIChatDemo; 481 | PRODUCT_NAME = "$(TARGET_NAME)"; 482 | SWIFT_EMIT_LOC_STRINGS = YES; 483 | SWIFT_VERSION = 5.0; 484 | TARGETED_DEVICE_FAMILY = "1,2"; 485 | }; 486 | name = Release; 487 | }; 488 | 4AC0169627511265009D8E63 /* Debug */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 492 | BUNDLE_LOADER = "$(TEST_HOST)"; 493 | CODE_SIGN_STYLE = Automatic; 494 | CURRENT_PROJECT_VERSION = 1; 495 | DEVELOPMENT_TEAM = EHV7XZLAHA; 496 | GENERATE_INFOPLIST_FILE = YES; 497 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 498 | LD_RUNPATH_SEARCH_PATHS = ( 499 | "$(inherited)", 500 | "@executable_path/Frameworks", 501 | "@loader_path/Frameworks", 502 | ); 503 | MARKETING_VERSION = 1.0; 504 | PRODUCT_BUNDLE_IDENTIFIER = getstream.io.SwiftUIChatDemoTests; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | SWIFT_EMIT_LOC_STRINGS = NO; 507 | SWIFT_VERSION = 5.0; 508 | TARGETED_DEVICE_FAMILY = "1,2"; 509 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftUIChatDemo.app/SwiftUIChatDemo"; 510 | }; 511 | name = Debug; 512 | }; 513 | 4AC0169727511265009D8E63 /* Release */ = { 514 | isa = XCBuildConfiguration; 515 | buildSettings = { 516 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 517 | BUNDLE_LOADER = "$(TEST_HOST)"; 518 | CODE_SIGN_STYLE = Automatic; 519 | CURRENT_PROJECT_VERSION = 1; 520 | DEVELOPMENT_TEAM = EHV7XZLAHA; 521 | GENERATE_INFOPLIST_FILE = YES; 522 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 523 | LD_RUNPATH_SEARCH_PATHS = ( 524 | "$(inherited)", 525 | "@executable_path/Frameworks", 526 | "@loader_path/Frameworks", 527 | ); 528 | MARKETING_VERSION = 1.0; 529 | PRODUCT_BUNDLE_IDENTIFIER = getstream.io.SwiftUIChatDemoTests; 530 | PRODUCT_NAME = "$(TARGET_NAME)"; 531 | SWIFT_EMIT_LOC_STRINGS = NO; 532 | SWIFT_VERSION = 5.0; 533 | TARGETED_DEVICE_FAMILY = "1,2"; 534 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftUIChatDemo.app/SwiftUIChatDemo"; 535 | }; 536 | name = Release; 537 | }; 538 | 4AC0169927511265009D8E63 /* Debug */ = { 539 | isa = XCBuildConfiguration; 540 | buildSettings = { 541 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 542 | CODE_SIGN_STYLE = Automatic; 543 | CURRENT_PROJECT_VERSION = 1; 544 | DEVELOPMENT_TEAM = EHV7XZLAHA; 545 | GENERATE_INFOPLIST_FILE = YES; 546 | LD_RUNPATH_SEARCH_PATHS = ( 547 | "$(inherited)", 548 | "@executable_path/Frameworks", 549 | "@loader_path/Frameworks", 550 | ); 551 | MARKETING_VERSION = 1.0; 552 | PRODUCT_BUNDLE_IDENTIFIER = getstream.io.SwiftUIChatDemoUITests; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | SWIFT_EMIT_LOC_STRINGS = NO; 555 | SWIFT_VERSION = 5.0; 556 | TARGETED_DEVICE_FAMILY = "1,2"; 557 | TEST_TARGET_NAME = SwiftUIChatDemo; 558 | }; 559 | name = Debug; 560 | }; 561 | 4AC0169A27511265009D8E63 /* Release */ = { 562 | isa = XCBuildConfiguration; 563 | buildSettings = { 564 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 565 | CODE_SIGN_STYLE = Automatic; 566 | CURRENT_PROJECT_VERSION = 1; 567 | DEVELOPMENT_TEAM = EHV7XZLAHA; 568 | GENERATE_INFOPLIST_FILE = YES; 569 | LD_RUNPATH_SEARCH_PATHS = ( 570 | "$(inherited)", 571 | "@executable_path/Frameworks", 572 | "@loader_path/Frameworks", 573 | ); 574 | MARKETING_VERSION = 1.0; 575 | PRODUCT_BUNDLE_IDENTIFIER = getstream.io.SwiftUIChatDemoUITests; 576 | PRODUCT_NAME = "$(TARGET_NAME)"; 577 | SWIFT_EMIT_LOC_STRINGS = NO; 578 | SWIFT_VERSION = 5.0; 579 | TARGETED_DEVICE_FAMILY = "1,2"; 580 | TEST_TARGET_NAME = SwiftUIChatDemo; 581 | }; 582 | name = Release; 583 | }; 584 | /* End XCBuildConfiguration section */ 585 | 586 | /* Begin XCConfigurationList section */ 587 | 4AC0166927511264009D8E63 /* Build configuration list for PBXProject "SwiftUIChatDemo" */ = { 588 | isa = XCConfigurationList; 589 | buildConfigurations = ( 590 | 4AC0169027511265009D8E63 /* Debug */, 591 | 4AC0169127511265009D8E63 /* Release */, 592 | ); 593 | defaultConfigurationIsVisible = 0; 594 | defaultConfigurationName = Release; 595 | }; 596 | 4AC0169227511265009D8E63 /* Build configuration list for PBXNativeTarget "SwiftUIChatDemo" */ = { 597 | isa = XCConfigurationList; 598 | buildConfigurations = ( 599 | 4AC0169327511265009D8E63 /* Debug */, 600 | 4AC0169427511265009D8E63 /* Release */, 601 | ); 602 | defaultConfigurationIsVisible = 0; 603 | defaultConfigurationName = Release; 604 | }; 605 | 4AC0169527511265009D8E63 /* Build configuration list for PBXNativeTarget "SwiftUIChatDemoTests" */ = { 606 | isa = XCConfigurationList; 607 | buildConfigurations = ( 608 | 4AC0169627511265009D8E63 /* Debug */, 609 | 4AC0169727511265009D8E63 /* Release */, 610 | ); 611 | defaultConfigurationIsVisible = 0; 612 | defaultConfigurationName = Release; 613 | }; 614 | 4AC0169827511265009D8E63 /* Build configuration list for PBXNativeTarget "SwiftUIChatDemoUITests" */ = { 615 | isa = XCConfigurationList; 616 | buildConfigurations = ( 617 | 4AC0169927511265009D8E63 /* Debug */, 618 | 4AC0169A27511265009D8E63 /* Release */, 619 | ); 620 | defaultConfigurationIsVisible = 0; 621 | defaultConfigurationName = Release; 622 | }; 623 | /* End XCConfigurationList section */ 624 | 625 | /* Begin XCRemoteSwiftPackageReference section */ 626 | 4AC0169B2751130A009D8E63 /* XCRemoteSwiftPackageReference "stream-chat-swiftui" */ = { 627 | isa = XCRemoteSwiftPackageReference; 628 | repositoryURL = "https://github.com/getstream/stream-chat-swiftui"; 629 | requirement = { 630 | branch = main; 631 | kind = branch; 632 | }; 633 | }; 634 | /* End XCRemoteSwiftPackageReference section */ 635 | 636 | /* Begin XCSwiftPackageProductDependency section */ 637 | 4AC0169C2751130A009D8E63 /* StreamChatSwiftUI */ = { 638 | isa = XCSwiftPackageProductDependency; 639 | package = 4AC0169B2751130A009D8E63 /* XCRemoteSwiftPackageReference "stream-chat-swiftui" */; 640 | productName = StreamChatSwiftUI; 641 | }; 642 | /* End XCSwiftPackageProductDependency section */ 643 | }; 644 | rootObject = 4AC0166627511264009D8E63 /* Project object */; 645 | } 646 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "difference", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/krzysztofzablocki/Difference.git", 7 | "state" : { 8 | "revision" : "02fe1111edc8318c4f8a0da96336fcbcc201f38b", 9 | "version" : "1.0.1" 10 | } 11 | }, 12 | { 13 | "identity" : "nuke", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/kean/Nuke.git", 16 | "state" : { 17 | "revision" : "93c8dc78fbc0aa3538a0db460eb389d4180af242", 18 | "version" : "11.3.1" 19 | } 20 | }, 21 | { 22 | "identity" : "stream-chat-swift", 23 | "kind" : "remoteSourceControl", 24 | "location" : "https://github.com/GetStream/stream-chat-swift.git", 25 | "state" : { 26 | "revision" : "7efe79171c24c8eb312401f87c453c15f77fbba9", 27 | "version" : "4.31.0" 28 | } 29 | }, 30 | { 31 | "identity" : "stream-chat-swift-test-helpers", 32 | "kind" : "remoteSourceControl", 33 | "location" : "https://github.com/GetStream/stream-chat-swift-test-helpers.git", 34 | "state" : { 35 | "revision" : "237da98e4946ed8e2dac51d9e82e713b0d47a298", 36 | "version" : "0.2.5" 37 | } 38 | }, 39 | { 40 | "identity" : "stream-chat-swiftui", 41 | "kind" : "remoteSourceControl", 42 | "location" : "https://github.com/getstream/stream-chat-swiftui", 43 | "state" : { 44 | "branch" : "main", 45 | "revision" : "4d7fa3d08de8ce5f955df89f75973f5241d77e3f" 46 | } 47 | }, 48 | { 49 | "identity" : "swifter", 50 | "kind" : "remoteSourceControl", 51 | "location" : "https://github.com/httpswift/swifter", 52 | "state" : { 53 | "revision" : "9483a5d459b45c3ffd059f7b55f9638e268632fd", 54 | "version" : "1.5.0" 55 | } 56 | } 57 | ], 58 | "version" : 2 59 | } 60 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo.xcodeproj/xcuserdata/jeroenleenarts.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Stream (Playground) 1.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 2 13 | 14 | Stream (Playground) 2.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 3 20 | 21 | Stream (Playground).xcscheme 22 | 23 | isShown 24 | 25 | orderHint 26 | 1 27 | 28 | SwiftUIChatDemo.xcscheme_^#shared#^_ 29 | 30 | orderHint 31 | 0 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import StreamChat 2 | import StreamChatSwiftUI 3 | import UIKit 4 | import SwiftUI 5 | 6 | class AppDelegate: NSObject, UIApplicationDelegate { 7 | 8 | var streamChat: StreamChat? 9 | 10 | var chatClient: ChatClient = { 11 | var config = ChatClientConfig(apiKey: .init("8br4watad788")) 12 | config.applicationGroupIdentifier = "group.io.getstream.iOS.ChatDemoAppSwiftUI" 13 | 14 | let client = ChatClient(config: config) 15 | return client 16 | }() 17 | 18 | func application(_ application: UIApplication, 19 | didFinishLaunchingWithOptions launchOptions: 20 | [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { 21 | var colors = ColorPalette() 22 | let streamBlue = UIColor(red: 0, green: 108.0 / 255.0, blue: 255.0 / 255.0, alpha: 1) 23 | colors.tintColor = Color(streamBlue) 24 | 25 | var fonts = Fonts() 26 | fonts.footnoteBold = Font.footnote 27 | fonts.body = Font.title 28 | 29 | let images = Images() 30 | images.reactionLoveBig = UIImage(systemName: "heart.fill")! 31 | 32 | let appearance = Appearance(colors: colors, images: images, fonts: fonts) 33 | 34 | let channelNamer: ChatChannelNamer = { channel, _ in 35 | "This is our custom name: \(channel.name ?? "no name")" 36 | } 37 | 38 | let customResolver = CustomMessageResolver() 39 | let utils = Utils(messageTypeResolver: customResolver, channelNamer: channelNamer) 40 | 41 | streamChat = StreamChat(chatClient: chatClient, appearance: appearance, utils: utils) 42 | 43 | connectUser() 44 | 45 | return true 46 | } 47 | 48 | private func connectUser() { 49 | let token = try! Token(rawValue: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoibHVrZV9za3l3YWxrZXIifQ.kFSLHRB5X62t0Zlc7nwczWUfsQMwfkpylC6jCUZ6Mc0") 50 | 51 | chatClient.connectUser( 52 | userInfo: .init(id: "luke_skywalker", 53 | name: "Luke Skywalker", 54 | imageURL: URL(string: "https://vignette.wikia.nocookie.net/starwars/images/2/20/LukeTLJ.jpg")!), 55 | token: token 56 | ) { error in 57 | if let error = error { 58 | log.error("connecting the user failed \(error)") 59 | return 60 | } 61 | } 62 | } 63 | } 64 | 65 | class CustomViewFactory: ViewFactory { 66 | @Injected(\.chatClient) public var chatClient 67 | 68 | func makeMessageTextView(for message: ChatMessage, 69 | isFirst: Bool, 70 | availableWidth: CGFloat) -> some View { 71 | return CustomMessageTextView(message: message, isFirst: isFirst) 72 | } 73 | 74 | func makeCustomAttachmentViewType( 75 | for message: ChatMessage, 76 | isFirst: Bool, 77 | availableWidth: CGFloat, 78 | scrolledId: Binding 79 | ) -> some View { 80 | CustomAttachmentView( 81 | message: message, 82 | width: availableWidth, 83 | isFirst: isFirst 84 | ) 85 | } 86 | } 87 | 88 | struct CustomMessageTextView: View { 89 | @Injected(\.colors) var colors 90 | @Injected(\.fonts) var fonts 91 | 92 | var message: ChatMessage 93 | var isFirst: Bool 94 | 95 | public var body: some View { 96 | Text(message.text) 97 | .padding() 98 | .messageBubble(for: message, isFirst: isFirst) 99 | .foregroundColor(Color(colors.text)) 100 | .font(fonts.bodyBold) 101 | .overlay( 102 | BottomRightView { 103 | Image(systemName: "checkmark.circle.fill") 104 | .foregroundColor(.green) 105 | .offset(x: 1, y: -1) 106 | } 107 | ) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo/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 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // SwiftUIChatDemo 4 | // 5 | // Created by Jeroen Leenarts on 26/11/2021. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | var body: some View { 12 | Text("Hello, world!") 13 | .padding() 14 | } 15 | } 16 | 17 | struct ContentView_Previews: PreviewProvider { 18 | static var previews: some View { 19 | ContentView() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo/CustomAttachment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2021 Stream.io Inc. All rights reserved. 3 | // 4 | 5 | import SwiftUI 6 | import StreamChat 7 | import StreamChatSwiftUI 8 | 9 | class CustomMessageResolver: MessageTypeResolving { 10 | 11 | func hasCustomAttachment(message: ChatMessage) -> Bool { 12 | let messageComponents = message.text.components(separatedBy: CharacterSet.whitespacesAndNewlines) 13 | return messageComponents.filter { component in 14 | isValidEmail(component) 15 | } 16 | .count > 0 17 | } 18 | 19 | private func isValidEmail(_ email: String) -> Bool { 20 | let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" 21 | 22 | let emailPred = NSPredicate(format: "SELF MATCHES %@", emailRegEx) 23 | return emailPred.evaluate(with: email) 24 | } 25 | 26 | } 27 | 28 | struct CustomAttachmentView: View { 29 | 30 | @State var envelopeTapped = false 31 | 32 | let message: ChatMessage 33 | let width: CGFloat 34 | let isFirst: Bool 35 | 36 | var body: some View { 37 | HStack { 38 | Image(systemName: "envelope").onTapGesture { 39 | self.envelopeTapped = true 40 | } 41 | Text(message.text) 42 | } 43 | .padding() 44 | .alert(isPresented: $envelopeTapped) { 45 | Alert( 46 | title: Text("Tapped an envelope"), 47 | message: Text("Maybe show an email composer now?"), 48 | dismissButton: .cancel(Text("Ok")) 49 | ) 50 | } 51 | .messageBubble(for: message, isFirst: isFirst) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemo/SwiftUIChatDemoApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIChatDemoApp.swift 3 | // SwiftUIChatDemo 4 | // 5 | // Created by Jeroen Leenarts on 26/11/2021. 6 | // 7 | 8 | import SwiftUI 9 | import StreamChat 10 | import StreamChatSwiftUI 11 | 12 | @main 13 | struct SwiftUIChatDemoApp: App { 14 | @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate 15 | 16 | var body: some Scene { 17 | WindowGroup { 18 | ChatChannelListView(viewFactory: CustomViewFactory()) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemoTests/SwiftUIChatDemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIChatDemoTests.swift 3 | // SwiftUIChatDemoTests 4 | // 5 | // Created by Jeroen Leenarts on 26/11/2021. 6 | // 7 | 8 | import XCTest 9 | @testable import SwiftUIChatDemo 10 | 11 | class SwiftUIChatDemoTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemoUITests/SwiftUIChatDemoUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIChatDemoUITests.swift 3 | // SwiftUIChatDemoUITests 4 | // 5 | // Created by Jeroen Leenarts on 26/11/2021. 6 | // 7 | 8 | import XCTest 9 | 10 | class SwiftUIChatDemoUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | func testLaunchPerformance() throws { 35 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /SwiftUIChatDemo/SwiftUIChatDemoUITests/SwiftUIChatDemoUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIChatDemoUITestsLaunchTests.swift 3 | // SwiftUIChatDemoUITests 4 | // 5 | // Created by Jeroen Leenarts on 26/11/2021. 6 | // 7 | 8 | import XCTest 9 | 10 | class SwiftUIChatDemoUITestsLaunchTests: XCTestCase { 11 | 12 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 13 | true 14 | } 15 | 16 | override func setUpWithError() throws { 17 | continueAfterFailure = false 18 | } 19 | 20 | func testLaunch() throws { 21 | let app = XCUIApplication() 22 | app.launch() 23 | 24 | // Insert steps here to perform after app launch but before taking a screenshot, 25 | // such as logging into a test account or navigating somewhere in the app 26 | 27 | let attachment = XCTAttachment(screenshot: app.screenshot()) 28 | attachment.name = "Launch Screen" 29 | attachment.lifetime = .keepAlways 30 | add(attachment) 31 | } 32 | } 33 | --------------------------------------------------------------------------------