From 6c871e87e094da4e3f5419dfd97a03494c9f5253 Mon Sep 17 00:00:00 2001 From: Enes Karaosman Date: Fri, 12 Apr 2024 14:48:50 +0300 Subject: [PATCH] feat: add macOS support (#60) * Update ChatView.swift * Update ChatUser.swift * Update ImageCell.swift * Update ContactCell.swift * Update BasicInputView.swift * Update PlayerView.swift * Update AvatarModifier.swift * Update ContactItem.swift * Update ChatMessageKind.swift * Update MockMessages.swift * Update ImageCell.swift * Update CustomPlayerView.swift * Update ChatView.swift * Update PIPVideoCell.swift * Update CustomVideoPlayer.swift * refactor: add swiftUI example app, remove old UIKit generated app * refactor: remove some redundant code * fix: correct macOS video player --- .github/workflows/swift.yml | 3 - .../Example.xcodeproj}/project.pbxproj | 244 +++++++++--------- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/swiftpm/Package.resolved | 0 .../Example}/AdvancedExampleView.swift | 9 +- .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 13 + .../Example}/Assets.xcassets/Contents.json | 0 .../Example}/BasicExampleView.swift | 11 +- .../Example}/ChatListView.swift | 1 - Example/Example/ExampleApp.swift | 17 ++ .../Example}/Extensions.swift | 0 Example/Example/Info.plist | 11 + .../Preview Assets.xcassets/Contents.json | 0 Package.resolved | 78 +++--- Sources/SwiftyChat/ChatView.swift | 51 ++-- .../Common/Modifiers/AvatarModifier.swift | 2 +- .../Modifiers/DismissKeyboardModifier.swift | 52 +--- .../Modifiers/KeyboardAwareModifier.swift | 7 +- .../Common/Video/ChatVideoPlayer.swift | 83 ++++++ .../Common/Video/PIPVideoCell.swift | 33 ++- .../Video/SwiftUIViews/CustomPlayerView.swift | 37 --- .../SwiftUIViews/CustomVideoPlayer.swift | 117 --------- .../Common/Video/UIKitViews/PlayerView.swift | 23 -- ...olsView.swift => VideoPlayerOverlay.swift} | 2 +- .../Video/VideoPlayerRepresentable.swift | 78 ++++++ Sources/SwiftyChat/Extension/String++.swift | 4 - Sources/SwiftyChat/Extension/UIDevice++.swift | 14 - .../SwiftyChat/InputView/BasicInputView.swift | 2 +- .../SwiftyChat/MessageCells/ContactCell.swift | 2 +- .../SwiftyChat/MessageCells/ImageCell.swift | 29 ++- Sources/SwiftyChat/Mock/MockMessages.swift | 14 +- .../SwiftyChat/Model/ChatMessageKind.swift | 2 +- Sources/SwiftyChat/Protocols/ChatUser.swift | 8 +- .../SwiftyChat/Protocols/ContactItem.swift | 8 +- .../SwiftyChatExample/AppDelegate.swift | 36 --- .../AppIcon.appiconset/Contents.json | 98 ------- .../Base.lproj/LaunchScreen.storyboard | 25 -- .../SwiftyChatExample/Info.plist | 71 ----- .../SwiftyChatExample/SceneDelegate.swift | 63 ----- 41 files changed, 465 insertions(+), 783 deletions(-) rename {SwiftyChatExample/SwiftyChatExample.xcodeproj => Example/Example.xcodeproj}/project.pbxproj (50%) rename {SwiftyChatExample/SwiftyChatExample.xcodeproj => Example/Example.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (100%) rename {SwiftyChatExample/SwiftyChatExample.xcodeproj => Example/Example.xcodeproj}/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {SwiftyChatExample/SwiftyChatExample.xcodeproj => Example/Example.xcodeproj}/project.xcworkspace/xcshareddata/swiftpm/Package.resolved (100%) rename {SwiftyChatExample/SwiftyChatExample => Example/Example}/AdvancedExampleView.swift (97%) rename {SwiftyChatExample/SwiftyChatExample => Example/Example}/Assets.xcassets/AccentColor.colorset/Contents.json (100%) create mode 100644 Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json rename {SwiftyChatExample/SwiftyChatExample => Example/Example}/Assets.xcassets/Contents.json (100%) rename {SwiftyChatExample/SwiftyChatExample => Example/Example}/BasicExampleView.swift (89%) rename {SwiftyChatExample/SwiftyChatExample => Example/Example}/ChatListView.swift (91%) create mode 100644 Example/Example/ExampleApp.swift rename {SwiftyChatExample/SwiftyChatExample => Example/Example}/Extensions.swift (100%) create mode 100644 Example/Example/Info.plist rename {SwiftyChatExample/SwiftyChatExample => Example/Example}/Preview Content/Preview Assets.xcassets/Contents.json (100%) create mode 100644 Sources/SwiftyChat/Common/Video/ChatVideoPlayer.swift delete mode 100644 Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomPlayerView.swift delete mode 100644 Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomVideoPlayer.swift delete mode 100644 Sources/SwiftyChat/Common/Video/UIKitViews/PlayerView.swift rename Sources/SwiftyChat/Common/Video/{SwiftUIViews/CustomControlsView.swift => VideoPlayerOverlay.swift} (97%) create mode 100644 Sources/SwiftyChat/Common/Video/VideoPlayerRepresentable.swift delete mode 100644 SwiftyChatExample/SwiftyChatExample/AppDelegate.swift delete mode 100644 SwiftyChatExample/SwiftyChatExample/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 SwiftyChatExample/SwiftyChatExample/Base.lproj/LaunchScreen.storyboard delete mode 100644 SwiftyChatExample/SwiftyChatExample/Info.plist delete mode 100644 SwiftyChatExample/SwiftyChatExample/SceneDelegate.swift diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index ab4fb633..878da475 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -22,6 +22,3 @@ jobs: - name: Build run: swift build -v - - - name: Run tests - run: swift test -v diff --git a/SwiftyChatExample/SwiftyChatExample.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj similarity index 50% rename from SwiftyChatExample/SwiftyChatExample.xcodeproj/project.pbxproj rename to Example/Example.xcodeproj/project.pbxproj index 7b58444b..a1286b45 100644 --- a/SwiftyChatExample/SwiftyChatExample.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -3,95 +3,79 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ - F464E9DC27E2452A00B396F0 /* SwiftyChat in Frameworks */ = {isa = PBXBuildFile; productRef = F464E9DB27E2452A00B396F0 /* SwiftyChat */; }; - F4D3ECCB253FFAE100226E0C /* BasicExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D3ECC8253FFAE100226E0C /* BasicExampleView.swift */; }; - F4D3ECCC253FFAE100226E0C /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D3ECC9253FFAE100226E0C /* ChatListView.swift */; }; - F4D3ECCD253FFAE100226E0C /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D3ECCA253FFAE100226E0C /* Extensions.swift */; }; - F4D55CC6253F262F00DA8FF0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D55CC5253F262F00DA8FF0 /* AppDelegate.swift */; }; - F4D55CC8253F262F00DA8FF0 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D55CC7253F262F00DA8FF0 /* SceneDelegate.swift */; }; - F4D55CCA253F262F00DA8FF0 /* AdvancedExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D55CC9253F262F00DA8FF0 /* AdvancedExampleView.swift */; }; - F4D55CCC253F263000DA8FF0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4D55CCB253F263000DA8FF0 /* Assets.xcassets */; }; - F4D55CCF253F263000DA8FF0 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4D55CCE253F263000DA8FF0 /* Preview Assets.xcassets */; }; - F4D55CD2253F263000DA8FF0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4D55CD0253F263000DA8FF0 /* LaunchScreen.storyboard */; }; + 94410B752BC6B4D400C37BDB /* ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94410B742BC6B4D400C37BDB /* ExampleApp.swift */; }; + 94410B792BC6B4D700C37BDB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 94410B782BC6B4D700C37BDB /* Assets.xcassets */; }; + 94410B7C2BC6B4D700C37BDB /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 94410B7B2BC6B4D700C37BDB /* Preview Assets.xcassets */; }; + 94410B842BC6B50700C37BDB /* SwiftyChat in Frameworks */ = {isa = PBXBuildFile; productRef = 94410B832BC6B50700C37BDB /* SwiftyChat */; }; + 94410B862BC6B53900C37BDB /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94410B852BC6B53900C37BDB /* ChatListView.swift */; }; + 94410B882BC6B53E00C37BDB /* AdvancedExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94410B872BC6B53E00C37BDB /* AdvancedExampleView.swift */; }; + 94410B8A2BC6B54100C37BDB /* BasicExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94410B892BC6B54100C37BDB /* BasicExampleView.swift */; }; + 94410B8C2BC6B54900C37BDB /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94410B8B2BC6B54900C37BDB /* Extensions.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - F464E9D927E2440000B396F0 /* SwiftyChat */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SwiftyChat; path = ..; sourceTree = ""; }; - F4D3ECC8253FFAE100226E0C /* BasicExampleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicExampleView.swift; sourceTree = ""; }; - F4D3ECC9253FFAE100226E0C /* ChatListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = ""; }; - F4D3ECCA253FFAE100226E0C /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; - F4D55CC2253F262F00DA8FF0 /* SwiftyChatExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftyChatExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; - F4D55CC5253F262F00DA8FF0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - F4D55CC7253F262F00DA8FF0 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - F4D55CC9253F262F00DA8FF0 /* AdvancedExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedExampleView.swift; sourceTree = ""; }; - F4D55CCB253F263000DA8FF0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - F4D55CCE253F263000DA8FF0 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - F4D55CD1253F263000DA8FF0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - F4D55CD3253F263000DA8FF0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 94410B712BC6B4D400C37BDB /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 94410B742BC6B4D400C37BDB /* ExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleApp.swift; sourceTree = ""; }; + 94410B782BC6B4D700C37BDB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 94410B7B2BC6B4D700C37BDB /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 94410B852BC6B53900C37BDB /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = ""; }; + 94410B872BC6B53E00C37BDB /* AdvancedExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedExampleView.swift; sourceTree = ""; }; + 94410B892BC6B54100C37BDB /* BasicExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicExampleView.swift; sourceTree = ""; }; + 94410B8B2BC6B54900C37BDB /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; + 94410B8D2BC6B5C800C37BDB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - F4D55CBF253F262F00DA8FF0 /* Frameworks */ = { + 94410B6E2BC6B4D400C37BDB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F464E9DC27E2452A00B396F0 /* SwiftyChat in Frameworks */, + 94410B842BC6B50700C37BDB /* SwiftyChat in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - F464E9DA27E2452A00B396F0 /* Frameworks */ = { + 94410B682BC6B4D400C37BDB = { isa = PBXGroup; children = ( + 94410B732BC6B4D400C37BDB /* Example */, + 94410B722BC6B4D400C37BDB /* Products */, ); - name = Frameworks; sourceTree = ""; }; - F4D55CB9253F262F00DA8FF0 = { + 94410B722BC6B4D400C37BDB /* Products */ = { isa = PBXGroup; children = ( - F464E9D927E2440000B396F0 /* SwiftyChat */, - F4D55CC4253F262F00DA8FF0 /* SwiftyChatExample */, - F4D55CC3253F262F00DA8FF0 /* Products */, - F464E9DA27E2452A00B396F0 /* Frameworks */, - ); - sourceTree = ""; - }; - F4D55CC3253F262F00DA8FF0 /* Products */ = { - isa = PBXGroup; - children = ( - F4D55CC2253F262F00DA8FF0 /* SwiftyChatExample.app */, + 94410B712BC6B4D400C37BDB /* Example.app */, ); name = Products; sourceTree = ""; }; - F4D55CC4253F262F00DA8FF0 /* SwiftyChatExample */ = { + 94410B732BC6B4D400C37BDB /* Example */ = { isa = PBXGroup; children = ( - F4D55CC9253F262F00DA8FF0 /* AdvancedExampleView.swift */, - F4D3ECC8253FFAE100226E0C /* BasicExampleView.swift */, - F4D3ECC9253FFAE100226E0C /* ChatListView.swift */, - F4D3ECCA253FFAE100226E0C /* Extensions.swift */, - F4D55CC5253F262F00DA8FF0 /* AppDelegate.swift */, - F4D55CC7253F262F00DA8FF0 /* SceneDelegate.swift */, - F4D55CCB253F263000DA8FF0 /* Assets.xcassets */, - F4D55CD0253F263000DA8FF0 /* LaunchScreen.storyboard */, - F4D55CD3253F263000DA8FF0 /* Info.plist */, - F4D55CCD253F263000DA8FF0 /* Preview Content */, + 94410B8D2BC6B5C800C37BDB /* Info.plist */, + 94410B742BC6B4D400C37BDB /* ExampleApp.swift */, + 94410B852BC6B53900C37BDB /* ChatListView.swift */, + 94410B872BC6B53E00C37BDB /* AdvancedExampleView.swift */, + 94410B892BC6B54100C37BDB /* BasicExampleView.swift */, + 94410B8B2BC6B54900C37BDB /* Extensions.swift */, + 94410B782BC6B4D700C37BDB /* Assets.xcassets */, + 94410B7A2BC6B4D700C37BDB /* Preview Content */, ); - path = SwiftyChatExample; + path = Example; sourceTree = ""; }; - F4D55CCD253F263000DA8FF0 /* Preview Content */ = { + 94410B7A2BC6B4D700C37BDB /* Preview Content */ = { isa = PBXGroup; children = ( - F4D55CCE253F263000DA8FF0 /* Preview Assets.xcassets */, + 94410B7B2BC6B4D700C37BDB /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; @@ -99,110 +83,98 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - F4D55CC1253F262F00DA8FF0 /* SwiftyChatExample */ = { + 94410B702BC6B4D400C37BDB /* Example */ = { isa = PBXNativeTarget; - buildConfigurationList = F4D55CD6253F263000DA8FF0 /* Build configuration list for PBXNativeTarget "SwiftyChatExample" */; + buildConfigurationList = 94410B7F2BC6B4D700C37BDB /* Build configuration list for PBXNativeTarget "Example" */; buildPhases = ( - F4D55CBE253F262F00DA8FF0 /* Sources */, - F4D55CBF253F262F00DA8FF0 /* Frameworks */, - F4D55CC0253F262F00DA8FF0 /* Resources */, + 94410B6D2BC6B4D400C37BDB /* Sources */, + 94410B6E2BC6B4D400C37BDB /* Frameworks */, + 94410B6F2BC6B4D400C37BDB /* Resources */, ); buildRules = ( ); dependencies = ( ); - name = SwiftyChatExample; + name = Example; packageProductDependencies = ( - F464E9DB27E2452A00B396F0 /* SwiftyChat */, + 94410B832BC6B50700C37BDB /* SwiftyChat */, ); - productName = SwiftyChatExample; - productReference = F4D55CC2253F262F00DA8FF0 /* SwiftyChatExample.app */; + productName = Example; + productReference = 94410B712BC6B4D400C37BDB /* Example.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - F4D55CBA253F262F00DA8FF0 /* Project object */ = { + 94410B692BC6B4D400C37BDB /* Project object */ = { isa = PBXProject; attributes = { - BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 1200; + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1520; LastUpgradeCheck = 1520; TargetAttributes = { - F4D55CC1253F262F00DA8FF0 = { - CreatedOnToolsVersion = 12.0.1; + 94410B702BC6B4D400C37BDB = { + CreatedOnToolsVersion = 15.2; }; }; }; - buildConfigurationList = F4D55CBD253F262F00DA8FF0 /* Build configuration list for PBXProject "SwiftyChatExample" */; - compatibilityVersion = "Xcode 9.3"; + buildConfigurationList = 94410B6C2BC6B4D400C37BDB /* Build configuration list for PBXProject "Example" */; + compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); - mainGroup = F4D55CB9253F262F00DA8FF0; + mainGroup = 94410B682BC6B4D400C37BDB; packageReferences = ( + 94410B822BC6B50700C37BDB /* XCLocalSwiftPackageReference ".." */, ); - productRefGroup = F4D55CC3253F262F00DA8FF0 /* Products */; + productRefGroup = 94410B722BC6B4D400C37BDB /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - F4D55CC1253F262F00DA8FF0 /* SwiftyChatExample */, + 94410B702BC6B4D400C37BDB /* Example */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - F4D55CC0253F262F00DA8FF0 /* Resources */ = { + 94410B6F2BC6B4D400C37BDB /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - F4D55CD2253F263000DA8FF0 /* LaunchScreen.storyboard in Resources */, - F4D55CCF253F263000DA8FF0 /* Preview Assets.xcassets in Resources */, - F4D55CCC253F263000DA8FF0 /* Assets.xcassets in Resources */, + 94410B7C2BC6B4D700C37BDB /* Preview Assets.xcassets in Resources */, + 94410B792BC6B4D700C37BDB /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - F4D55CBE253F262F00DA8FF0 /* Sources */ = { + 94410B6D2BC6B4D400C37BDB /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F4D55CC6253F262F00DA8FF0 /* AppDelegate.swift in Sources */, - F4D3ECCD253FFAE100226E0C /* Extensions.swift in Sources */, - F4D3ECCC253FFAE100226E0C /* ChatListView.swift in Sources */, - F4D55CC8253F262F00DA8FF0 /* SceneDelegate.swift in Sources */, - F4D3ECCB253FFAE100226E0C /* BasicExampleView.swift in Sources */, - F4D55CCA253F262F00DA8FF0 /* AdvancedExampleView.swift in Sources */, + 94410B8C2BC6B54900C37BDB /* Extensions.swift in Sources */, + 94410B8A2BC6B54100C37BDB /* BasicExampleView.swift in Sources */, + 94410B882BC6B53E00C37BDB /* AdvancedExampleView.swift in Sources */, + 94410B862BC6B53900C37BDB /* ChatListView.swift in Sources */, + 94410B752BC6B4D400C37BDB /* ExampleApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXVariantGroup section */ - F4D55CD0253F263000DA8FF0 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - F4D55CD1253F263000DA8FF0 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ - F4D55CD4253F263000DA8FF0 /* Debug */ = { + 94410B7D2BC6B4D700C37BDB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; @@ -233,7 +205,7 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_C_LANGUAGE_STANDARD = gnu17; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -247,24 +219,25 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; - F4D55CD5253F263000DA8FF0 /* Release */ = { + 94410B7E2BC6B4D700C37BDB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; @@ -295,7 +268,7 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_C_LANGUAGE_STANDARD = gnu17; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -303,59 +276,75 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; - F4D55CD7253F263000DA8FF0 /* Debug */ = { + 94410B802BC6B4D700C37BDB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"SwiftyChatExample/Preview Content\""; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Example/Preview Content\""; DEVELOPMENT_TEAM = L9DND36YTU; ENABLE_PREVIEWS = YES; - INFOPLIST_FILE = SwiftyChatExample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Example/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.eneskaraosman.SwiftyChatExample; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.eneskaraosman.Example; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; - F4D55CD8253F263000DA8FF0 /* Release */ = { + 94410B812BC6B4D700C37BDB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"SwiftyChatExample/Preview Content\""; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Example/Preview Content\""; DEVELOPMENT_TEAM = L9DND36YTU; ENABLE_PREVIEWS = YES; - INFOPLIST_FILE = SwiftyChatExample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Example/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.eneskaraosman.SwiftyChatExample; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.eneskaraosman.Example; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -364,32 +353,39 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - F4D55CBD253F262F00DA8FF0 /* Build configuration list for PBXProject "SwiftyChatExample" */ = { + 94410B6C2BC6B4D400C37BDB /* Build configuration list for PBXProject "Example" */ = { isa = XCConfigurationList; buildConfigurations = ( - F4D55CD4253F263000DA8FF0 /* Debug */, - F4D55CD5253F263000DA8FF0 /* Release */, + 94410B7D2BC6B4D700C37BDB /* Debug */, + 94410B7E2BC6B4D700C37BDB /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F4D55CD6253F263000DA8FF0 /* Build configuration list for PBXNativeTarget "SwiftyChatExample" */ = { + 94410B7F2BC6B4D700C37BDB /* Build configuration list for PBXNativeTarget "Example" */ = { isa = XCConfigurationList; buildConfigurations = ( - F4D55CD7253F263000DA8FF0 /* Debug */, - F4D55CD8253F263000DA8FF0 /* Release */, + 94410B802BC6B4D700C37BDB /* Debug */, + 94410B812BC6B4D700C37BDB /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 94410B822BC6B50700C37BDB /* XCLocalSwiftPackageReference ".." */ = { + isa = XCLocalSwiftPackageReference; + relativePath = ..; + }; +/* End XCLocalSwiftPackageReference section */ + /* Begin XCSwiftPackageProductDependency section */ - F464E9DB27E2452A00B396F0 /* SwiftyChat */ = { + 94410B832BC6B50700C37BDB /* SwiftyChat */ = { isa = XCSwiftPackageProductDependency; productName = SwiftyChat; }; /* End XCSwiftPackageProductDependency section */ }; - rootObject = F4D55CBA253F262F00DA8FF0 /* Project object */; + rootObject = 94410B692BC6B4D400C37BDB /* Project object */; } diff --git a/SwiftyChatExample/SwiftyChatExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from SwiftyChatExample/SwiftyChatExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/SwiftyChatExample/SwiftyChatExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from SwiftyChatExample/SwiftyChatExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/SwiftyChatExample/SwiftyChatExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved similarity index 100% rename from SwiftyChatExample/SwiftyChatExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved rename to Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/SwiftyChatExample/SwiftyChatExample/AdvancedExampleView.swift b/Example/Example/AdvancedExampleView.swift similarity index 97% rename from SwiftyChatExample/SwiftyChatExample/AdvancedExampleView.swift rename to Example/Example/AdvancedExampleView.swift index 9e28999d..679035a9 100644 --- a/SwiftyChatExample/SwiftyChatExample/AdvancedExampleView.swift +++ b/Example/Example/AdvancedExampleView.swift @@ -16,9 +16,7 @@ struct AdvancedExampleView: View { @State private var message = "" var body: some View { - NavigationView { - chatView - } + chatView } private var chatView: some View { @@ -84,8 +82,9 @@ struct AdvancedExampleView: View { }) // ▼ Required .environmentObject(ChatMessageCellStyle()) - .navigationBarHidden(true) - .navigationBarTitle("") + #if os(iOS) + .navigationBarTitle("Advanced") + #endif .listStyle(PlainListStyle()) .onAppear { diff --git a/SwiftyChatExample/SwiftyChatExample/Assets.xcassets/AccentColor.colorset/Contents.json b/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from SwiftyChatExample/SwiftyChatExample/Assets.xcassets/AccentColor.colorset/Contents.json rename to Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..13613e3e --- /dev/null +++ b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SwiftyChatExample/SwiftyChatExample/Assets.xcassets/Contents.json b/Example/Example/Assets.xcassets/Contents.json similarity index 100% rename from SwiftyChatExample/SwiftyChatExample/Assets.xcassets/Contents.json rename to Example/Example/Assets.xcassets/Contents.json diff --git a/SwiftyChatExample/SwiftyChatExample/BasicExampleView.swift b/Example/Example/BasicExampleView.swift similarity index 89% rename from SwiftyChatExample/SwiftyChatExample/BasicExampleView.swift rename to Example/Example/BasicExampleView.swift index 6aaf2fb8..28444768 100644 --- a/SwiftyChatExample/SwiftyChatExample/BasicExampleView.swift +++ b/Example/Example/BasicExampleView.swift @@ -12,7 +12,6 @@ struct BasicExampleView: View { @State var messages: [MockMessages.ChatMessageItem] = MockMessages.generateMessage(kind: .Text, count: 20) - // MARK: - InputBarView variables @State private var message = "" var body: some View { @@ -31,9 +30,7 @@ struct BasicExampleView: View { ) } ) - .accentColor(.chatBlue) .background(Color.primary.colorInvert()) - .animation(.linear) .embedInAnyView() } @@ -43,7 +40,13 @@ struct BasicExampleView: View { case .text(let text): return Button(action: { print("Copy Context Menu tapped!!") + #if os(iOS) UIPasteboard.general.string = text + #endif + + #if os(macOS) + NSPasteboard.general.setString(text, forType: .string) + #endif }) { Text("Copy") Image(systemName: "doc.on.doc") @@ -56,7 +59,9 @@ struct BasicExampleView: View { } // ▼ Required .environmentObject(ChatMessageCellStyle.basicStyle) + #if os(iOS) .navigationBarTitle("Basic") + #endif .listStyle(PlainListStyle()) } } diff --git a/SwiftyChatExample/SwiftyChatExample/ChatListView.swift b/Example/Example/ChatListView.swift similarity index 91% rename from SwiftyChatExample/SwiftyChatExample/ChatListView.swift rename to Example/Example/ChatListView.swift index fa58860a..ba015b4c 100644 --- a/SwiftyChatExample/SwiftyChatExample/ChatListView.swift +++ b/Example/Example/ChatListView.swift @@ -14,7 +14,6 @@ struct ChatListView: View { NavigationLink("Basic Example", destination: BasicExampleView()) NavigationLink("Advanced Example", destination: AdvancedExampleView()) } - .navigationBarTitle("SwiftyChats") } } } diff --git a/Example/Example/ExampleApp.swift b/Example/Example/ExampleApp.swift new file mode 100644 index 00000000..f9e069c3 --- /dev/null +++ b/Example/Example/ExampleApp.swift @@ -0,0 +1,17 @@ +// +// ExampleApp.swift +// Example +// +// Created by Enes Karaosman on 10.04.2024. +// + +import SwiftUI + +@main +struct ExampleApp: App { + var body: some Scene { + WindowGroup { + ChatListView() + } + } +} diff --git a/SwiftyChatExample/SwiftyChatExample/Extensions.swift b/Example/Example/Extensions.swift similarity index 100% rename from SwiftyChatExample/SwiftyChatExample/Extensions.swift rename to Example/Example/Extensions.swift diff --git a/Example/Example/Info.plist b/Example/Example/Info.plist new file mode 100644 index 00000000..6a6654d9 --- /dev/null +++ b/Example/Example/Info.plist @@ -0,0 +1,11 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + + diff --git a/SwiftyChatExample/SwiftyChatExample/Preview Content/Preview Assets.xcassets/Contents.json b/Example/Example/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from SwiftyChatExample/SwiftyChatExample/Preview Content/Preview Assets.xcassets/Contents.json rename to Example/Example/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/Package.resolved b/Package.resolved index 5384cdec..e0035e60 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,52 +1,32 @@ { - "object": { - "pins": [ - { - "package": "GSPlayer", - "repositoryURL": "https://github.com/wxxsw/GSPlayer.git", - "state": { - "branch": null, - "revision": "706fc28fcc8e33607993903eca82d9f6c7c3f6d2", - "version": "0.2.27" - } - }, - { - "package": "Kingfisher", - "repositoryURL": "https://github.com/onevcat/Kingfisher.git", - "state": { - "branch": null, - "revision": "add0a87ec4e31e2ca2a0b2085f0559201e4f5918", - "version": "7.10.1" - } - }, - { - "package": "SwiftUIEKtensions", - "repositoryURL": "https://github.com/EnesKaraosman/SwiftUIEKtensions.git", - "state": { - "branch": null, - "revision": "21421df3266c99b83992face9e1523c8f1acb924", - "version": "0.2.0" - } - }, - { - "package": "VideoPlayer", - "repositoryURL": "https://github.com/wxxsw/VideoPlayer.git", - "state": { - "branch": null, - "revision": "0a271928849292713daf471398a2a9185f28709c", - "version": "1.2.4" - } - }, - { - "package": "WrappingHStack", - "repositoryURL": "https://github.com/dkk/WrappingHStack.git", - "state": { - "branch": null, - "revision": "425d9488ba55f58f0b34498c64c054c77fc2a44b", - "version": "2.2.11" - } + "pins" : [ + { + "identity" : "kingfisher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/onevcat/Kingfisher.git", + "state" : { + "revision" : "5b92f029fab2cce44386d28588098b5be0824ef5", + "version" : "7.11.0" } - ] - }, - "version": 1 + }, + { + "identity" : "swiftuiektensions", + "kind" : "remoteSourceControl", + "location" : "https://github.com/EnesKaraosman/SwiftUIEKtensions.git", + "state" : { + "revision" : "bf96c0d841d2bc81dac0d7e28aa9ab71cd7d6c2a", + "version" : "0.4.0" + } + }, + { + "identity" : "wrappinghstack", + "kind" : "remoteSourceControl", + "location" : "https://github.com/dkk/WrappingHStack.git", + "state" : { + "revision" : "425d9488ba55f58f0b34498c64c054c77fc2a44b", + "version" : "2.2.11" + } + } + ], + "version" : 2 } diff --git a/Sources/SwiftyChat/ChatView.swift b/Sources/SwiftyChat/ChatView.swift index 6c2ff09a..02b3b4f3 100644 --- a/Sources/SwiftyChat/ChatView.swift +++ b/Sources/SwiftyChat/ChatView.swift @@ -42,12 +42,14 @@ public struct ChatView: View { PIPVideoCell() } - .iOSOnlyModifier { $0.keyboardAwarePadding() } + .keyboardAwarePadding() // iOS only } + #if os(iOS) .environmentObject(DeviceOrientationInfo()) + #endif .environmentObject(VideoManager()) .edgesIgnoringSafeArea(.bottom) - .iOSOnlyModifier { $0.dismissKeyboardOnTappingOutside() } + .dismissKeyboardOnTappingOutside() // iOS only } @ViewBuilder private func chatView(in geometry: GeometryProxy) -> some View { @@ -107,23 +109,27 @@ public struct ChatView: View { scrollTo = nil } } - .iOSOnlyModifier { - // Auto Scroll with Keyboard Notification - $0.onReceive( - NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification) - .debounce(for: .milliseconds(400), scheduler: RunLoop.main), - perform: { _ in - if !isKeyboardActive { - isKeyboardActive = true - scrollToBottom = true - } + #if os(iOS) + // Auto Scroll with Keyboard Notification + .onReceive( + NotificationCenter + .default + .publisher(for: UIResponder.keyboardWillShowNotification) + .debounce(for: .milliseconds(400), scheduler: RunLoop.main), + perform: { _ in + if !isKeyboardActive { + isKeyboardActive = true + scrollToBottom = true } - ) - .onReceive( - NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification), - perform: { _ in isKeyboardActive = false } - ) - } + } + ) + .onReceive( + NotificationCenter + .default + .publisher(for: UIResponder.keyboardWillHideNotification), + perform: { _ in isKeyboardActive = false } + ) + #endif } } .background(Color.clear) @@ -186,10 +192,15 @@ public extension ChatView { } if let messageIndex = messages.firstIndex(where: { $0.id == thisMessage.id }) { - if messageIndex == 0 { return true } + if messageIndex == 0 { + return true + } + let prevMessageUserID = messages[messageIndex].user.id let currMessageUserID = messages[messageIndex - 1].user.id - return !(prevMessageUserID == currMessageUserID) + let isDifferentUser = prevMessageUserID != currMessageUserID + + return isDifferentUser } return false diff --git a/Sources/SwiftyChat/Common/Modifiers/AvatarModifier.swift b/Sources/SwiftyChat/Common/Modifiers/AvatarModifier.swift index 8e551230..33daf1f2 100644 --- a/Sources/SwiftyChat/Common/Modifiers/AvatarModifier.swift +++ b/Sources/SwiftyChat/Common/Modifiers/AvatarModifier.swift @@ -89,7 +89,7 @@ internal struct AvatarModifier: ViewModifi } else if let imageURL = user.avatarURL, currentStyle.imageStyle.imageSize.width > 0 { KFImage(imageURL).resizable() } else if let avatar = user.avatar, currentStyle.imageStyle.imageSize.width > 0 { - Image(uiImage: avatar).resizable() + Image(image: avatar).resizable() } } diff --git a/Sources/SwiftyChat/Common/Modifiers/DismissKeyboardModifier.swift b/Sources/SwiftyChat/Common/Modifiers/DismissKeyboardModifier.swift index 63e21401..c233b517 100644 --- a/Sources/SwiftyChat/Common/Modifiers/DismissKeyboardModifier.swift +++ b/Sources/SwiftyChat/Common/Modifiers/DismissKeyboardModifier.swift @@ -7,58 +7,8 @@ import SwiftUI -// Use below approach, when handle pushing content up when keyboard appears. -// Investigate keyboardAwarePadding() modifier, it does actually its job -// But some how its broken with current as-is - -#if os(iOS) -internal extension UIApplication { - func addTapGestureRecognizer() { - let scenes = UIApplication.shared.connectedScenes - let windowScene = scenes.first as? UIWindowScene - guard let window = windowScene?.windows.first else { return } - let tapGesture = AnyGestureRecognizer(target: window, action:#selector(UIView.endEditing)) - tapGesture.requiresExclusiveTouchType = false - tapGesture.cancelsTouchesInView = false - tapGesture.delegate = self // I don't use window as delegate to minimize possible side effects - window.addGestureRecognizer(tapGesture) - } -} - -extension UIApplication: UIGestureRecognizerDelegate { - public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { - return true // set to `false` if you don't want to detect tap during other gestures - } -} - -internal final class AnyGestureRecognizer: UIGestureRecognizer { - override func touchesBegan(_ touches: Set, with event: UIEvent) { - if let touchedView = touches.first?.view, touchedView is UIControl { - state = .cancelled - - } else if let touchedView = touches.first?.view as? UITextView, touchedView.isEditable { - state = .cancelled - } else if let touchedView = touches.first?.view as? UIButton, touchedView.isSelected { - state = .cancelled - } else { - state = .began - } - } - - override func touchesEnded(_ touches: Set, with event: UIEvent?) { - state = .ended - } - - override func touchesCancelled(_ touches: Set, with event: UIEvent) { - state = .cancelled - } -} -#endif - -// ----- - internal extension View { - dynamic func dismissKeyboardOnTappingOutside() -> some View { + func dismissKeyboardOnTappingOutside() -> some View { return ModifiedContent(content: self, modifier: DismissKeyboardOnTappingOutside()) } } diff --git a/Sources/SwiftyChat/Common/Modifiers/KeyboardAwareModifier.swift b/Sources/SwiftyChat/Common/Modifiers/KeyboardAwareModifier.swift index 72e31242..14c861bf 100644 --- a/Sources/SwiftyChat/Common/Modifiers/KeyboardAwareModifier.swift +++ b/Sources/SwiftyChat/Common/Modifiers/KeyboardAwareModifier.swift @@ -6,14 +6,18 @@ // Copyright © 2020 All rights reserved. // -#if os(iOS) import Combine import SwiftUI import SwiftUIEKtensions internal extension View { + /// iOS only modifier to add necessary padding according to keyboard height func keyboardAwarePadding() -> some View { + #if os(iOS) ModifiedContent(content: self, modifier: KeyboardAwareModifier()) + #else + self + #endif } } @@ -28,4 +32,3 @@ struct KeyboardAwareModifier: ViewModifier { } } } -#endif diff --git a/Sources/SwiftyChat/Common/Video/ChatVideoPlayer.swift b/Sources/SwiftyChat/Common/Video/ChatVideoPlayer.swift new file mode 100644 index 00000000..bf481892 --- /dev/null +++ b/Sources/SwiftyChat/Common/Video/ChatVideoPlayer.swift @@ -0,0 +1,83 @@ +import AVFoundation +import AVKit +import SwiftUI + +#if os(iOS) +struct iOSChatVideoPlayer: View { + @StateObject private var playerVM = PlayerViewModel() + let media: VideoItem + let message: Message + + @EnvironmentObject var style: ChatMessageCellStyle + @EnvironmentObject var videoManager: VideoManager + + private var cellStyle: VideoPlaceholderCellStyle { + style.videoPlaceholderCellStyle + } + + init(media: VideoItem, message: Message) { + self.media = media + self.message = message + + try? AVAudioSession.sharedInstance().setCategory(.playback) + } + + var body: some View { + VStack(spacing: .zero) { + VideoPlayerRepresentable(playerVM: playerVM) + VideoPlayerOverlay(for: playerVM) + } + .clipShape(RoundedRectangle(cornerRadius: cellStyle.cellCornerRadius)) + .onAppear { + playerVM.setCurrentItem(AVPlayerItem(url: media.url)) + playerVM.player.play() + } + .onDisappear { + playerVM.player.pause() + } + } +} +#endif + +// TODO: - Works for iOS as well but overlay is problematic currently +struct MacOSChatVideoPlayer: View { + @StateObject private var playerVM = PlayerViewModel() + let media: VideoItem + let message: Message + + @EnvironmentObject var style: ChatMessageCellStyle + @EnvironmentObject var videoManager: VideoManager + + private var cellStyle: VideoPlaceholderCellStyle { + style.videoPlaceholderCellStyle + } + + var body: some View { + VideoPlayer(player: playerVM.player) + .overlay(alignment: .topTrailing, content: { + closeButton + }) + .onAppear { + playerVM.setCurrentItem(AVPlayerItem(url: media.url)) + playerVM.player.play() + } + .onDisappear { + playerVM.player.pause() + } + } + + private var closeButton: some View { + Color.secondary.colorInvert() + .cornerRadius(10) + .frame(width: 50, height: 40) + .overlay( + Image(systemName: "xmark") + .font(Font.body.weight(.semibold)) + .foregroundColor(Color.white) + .padding() + ) + .onTapGesture { + self.videoManager.flushState() + } + } +} diff --git a/Sources/SwiftyChat/Common/Video/PIPVideoCell.swift b/Sources/SwiftyChat/Common/Video/PIPVideoCell.swift index 36fe9b3b..7b13b374 100644 --- a/Sources/SwiftyChat/Common/Video/PIPVideoCell.swift +++ b/Sources/SwiftyChat/Common/Video/PIPVideoCell.swift @@ -5,9 +5,9 @@ // Created by Enes Karaosman on 9.11.2020. // +import Combine import SwiftUI import SwiftUIEKtensions -import Combine internal extension CGSize { var midX: CGFloat { width / 2 } @@ -17,7 +17,10 @@ internal extension CGSize { internal struct PIPVideoCell: View { @EnvironmentObject var videoManager: VideoManager + + #if os(iOS) @EnvironmentObject var model: DeviceOrientationInfo + #endif @State private var cancellables: Set = .init() @State private var location: CGPoint = .zero @@ -27,20 +30,34 @@ internal struct PIPVideoCell: View { private let aspectRatio: CGFloat = 1.4 private func videoFrameHeight(in size: CGSize) -> CGFloat { + let portraitVideoFrameHeight = videoFrameWidth(in: size) / aspectRatio + + #if os(iOS) if videoManager.isFullScreen && model.orientation == .landscape { return size.height } else { - return videoFrameWidth(in: size) / aspectRatio + return portraitVideoFrameHeight } + + #else + return portraitVideoFrameHeight + #endif } private func videoFrameWidth(in size: CGSize) -> CGFloat { + let portraitVideoFrameWidth = abs(size.width - horizontalPadding) // Padding + + #if os(iOS) if videoManager.isFullScreen { return size.width } else { return model.orientation == .landscape ? - (size.width / aspectRatio) : abs(size.width - horizontalPadding) // Padding + (size.width / aspectRatio) : portraitVideoFrameWidth } + + #else + return portraitVideoFrameWidth + #endif } private enum Corner { @@ -111,6 +128,7 @@ internal struct PIPVideoCell: View { } .store(in: &cancellables) + #if os(iOS) model.$orientation .removeDuplicates() .sink(receiveValue: { _ in @@ -119,6 +137,7 @@ internal struct PIPVideoCell: View { } }) .store(in: &cancellables) + #endif } .onDisappear { cancellables.forEach { $0.cancel() } @@ -130,7 +149,13 @@ internal struct PIPVideoCell: View { @ViewBuilder private var video: some View { if let message = videoManager.message, let videoItem = videoManager.videoItem { - CustomPlayerView(media: videoItem, message: message) + #if os(iOS) + iOSChatVideoPlayer(media: videoItem, message: message) + #endif + + #if os(macOS) + MacOSChatVideoPlayer(media: videoItem, message: message) + #endif } } diff --git a/Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomPlayerView.swift b/Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomPlayerView.swift deleted file mode 100644 index 4cb356ef..00000000 --- a/Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomPlayerView.swift +++ /dev/null @@ -1,37 +0,0 @@ -import AVFoundation -import SwiftUI - -struct CustomPlayerView: View { - @StateObject private var playerVM = PlayerViewModel() - public let media: VideoItem - public let message: Message - - @EnvironmentObject var style: ChatMessageCellStyle - @EnvironmentObject var videoManager: VideoManager - - private var cellStyle: VideoPlaceholderCellStyle { - style.videoPlaceholderCellStyle - } - - init(media: VideoItem, message: Message) { - self.media = media - self.message = message - - try? AVAudioSession.sharedInstance().setCategory(.playback) - } - - var body: some View { - VStack(spacing: .zero) { - CustomVideoPlayer(playerVM: playerVM) - CustomControlsView(for: playerVM) - } - .clipShape(RoundedRectangle(cornerRadius: cellStyle.cellCornerRadius)) - .onAppear { - playerVM.setCurrentItem(AVPlayerItem(url: media.url)) - playerVM.player.play() - } - .onDisappear { - playerVM.player.pause() - } - } -} diff --git a/Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomVideoPlayer.swift b/Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomVideoPlayer.swift deleted file mode 100644 index 11b85d51..00000000 --- a/Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomVideoPlayer.swift +++ /dev/null @@ -1,117 +0,0 @@ -import AVKit -import Combine -import SwiftUI - -#if os(iOS) -struct CustomVideoPlayer: UIViewRepresentable { - @ObservedObject var playerVM: PlayerViewModel - - func makeUIView(context: Context) -> PlayerView { - let view = PlayerView() - view.player = playerVM.player - context.coordinator.setController(view.playerLayer) - return view - } - - func updateUIView(_ uiView: PlayerView, context: Context) { } - - func makeCoordinator() -> Coordinator { - return Coordinator(self) - } - - class Coordinator: NSObject, AVPictureInPictureControllerDelegate { - private let parent: CustomVideoPlayer - private var controller: AVPictureInPictureController? - private var cancellable: AnyCancellable? - - init(_ parent: CustomVideoPlayer) { - self.parent = parent - super.init() - - cancellable = parent.playerVM.$isInPipMode - .sink { [weak self] in - guard let self = self, - let controller = self.controller else { return } - if $0 { - if controller.isPictureInPictureActive == false { - controller.startPictureInPicture() - } - } else if controller.isPictureInPictureActive { - controller.stopPictureInPicture() - } - } - } - - func setController(_ playerLayer: AVPlayerLayer) { - controller = AVPictureInPictureController(playerLayer: playerLayer) - controller?.canStartPictureInPictureAutomaticallyFromInline = true - controller?.delegate = self - } - - func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - parent.playerVM.isInPipMode = true - } - - func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - parent.playerVM.isInPipMode = false - } - } -} -#endif - -#if os(macOS) -struct CustomVideoPlayer: NSViewRepresentable { - @ObservedObject var playerVM: PlayerViewModel - - func makeNSView(context: Context) -> PlayerView { - let view = PlayerView() - view.player = playerVM.player - context.coordinator.setController(view.playerLayer) - return view - } - - func updateNSView(_ nsView: PlayerView, context: Context) {} - - func makeCoordinator() -> Coordinator { - return Coordinator(self) - } - - class Coordinator: NSObject, AVPictureInPictureControllerDelegate { - private let parent: CustomVideoPlayer - private var controller: AVPictureInPictureController? - private var cancellable: AnyCancellable? - - init(_ parent: CustomVideoPlayer) { - self.parent = parent - super.init() - - cancellable = parent.playerVM.$isInPipMode - .sink { [weak self] in - guard let self = self, - let controller = self.controller else { return } - if $0 { - if controller.isPictureInPictureActive == false { - controller.startPictureInPicture() - } - } else if controller.isPictureInPictureActive { - controller.stopPictureInPicture() - } - } - } - - func setController(_ playerLayer: AVPlayerLayer) { - controller = AVPictureInPictureController(playerLayer: playerLayer) - controller?.delegate = self - } - - func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - parent.playerVM.isInPipMode = true - } - - func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { - parent.playerVM.isInPipMode = false - } - } - -} -#endif diff --git a/Sources/SwiftyChat/Common/Video/UIKitViews/PlayerView.swift b/Sources/SwiftyChat/Common/Video/UIKitViews/PlayerView.swift deleted file mode 100644 index 415cb805..00000000 --- a/Sources/SwiftyChat/Common/Video/UIKitViews/PlayerView.swift +++ /dev/null @@ -1,23 +0,0 @@ -import AVFoundation - -#if canImport(UIKit) -import UIKit -#endif - -final class PlayerView: UIView { - override static var layerClass: AnyClass { - AVPlayerLayer.self - } - - var playerLayer: AVPlayerLayer { layer as! AVPlayerLayer } - - var player: AVPlayer? { - get { - playerLayer.player - } - set { - playerLayer.videoGravity = .resizeAspectFill - playerLayer.player = newValue - } - } -} diff --git a/Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomControlsView.swift b/Sources/SwiftyChat/Common/Video/VideoPlayerOverlay.swift similarity index 97% rename from Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomControlsView.swift rename to Sources/SwiftyChat/Common/Video/VideoPlayerOverlay.swift index 8124ee02..e96081d4 100644 --- a/Sources/SwiftyChat/Common/Video/SwiftUIViews/CustomControlsView.swift +++ b/Sources/SwiftyChat/Common/Video/VideoPlayerOverlay.swift @@ -1,6 +1,6 @@ import SwiftUI -struct CustomControlsView: View { +struct VideoPlayerOverlay: View { @ObservedObject var playerVM: PlayerViewModel @EnvironmentObject var videoManager: VideoManager diff --git a/Sources/SwiftyChat/Common/Video/VideoPlayerRepresentable.swift b/Sources/SwiftyChat/Common/Video/VideoPlayerRepresentable.swift new file mode 100644 index 00000000..b2ece54f --- /dev/null +++ b/Sources/SwiftyChat/Common/Video/VideoPlayerRepresentable.swift @@ -0,0 +1,78 @@ +#if os(iOS) +import AVKit +import Combine +import SwiftUI + +struct VideoPlayerRepresentable: UIViewRepresentable { + @ObservedObject var playerVM: PlayerViewModel + + func makeUIView(context: Context) -> PlayerView { + let view = PlayerView() + view.player = playerVM.player + context.coordinator.setController(view.playerLayer) + return view + } + + func updateUIView(_ uiView: PlayerView, context: Context) { } + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator: NSObject, AVPictureInPictureControllerDelegate { + private let parent: VideoPlayerRepresentable + private var controller: AVPictureInPictureController? + private var cancellable: AnyCancellable? + + init(_ parent: VideoPlayerRepresentable) { + self.parent = parent + super.init() + + cancellable = parent.playerVM.$isInPipMode + .sink { [weak self] in + guard let self = self, + let controller = self.controller else { return } + if $0 { + if controller.isPictureInPictureActive == false { + controller.startPictureInPicture() + } + } else if controller.isPictureInPictureActive { + controller.stopPictureInPicture() + } + } + } + + func setController(_ playerLayer: AVPlayerLayer) { + controller = AVPictureInPictureController(playerLayer: playerLayer) + controller?.canStartPictureInPictureAutomaticallyFromInline = true + controller?.delegate = self + } + + func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + parent.playerVM.isInPipMode = true + } + + func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + parent.playerVM.isInPipMode = false + } + } + + final class PlayerView: UIView { + override static var layerClass: AnyClass { + AVPlayerLayer.self + } + + var playerLayer: AVPlayerLayer { layer as! AVPlayerLayer } + + var player: AVPlayer? { + get { + playerLayer.player + } + set { + playerLayer.videoGravity = .resizeAspectFill + playerLayer.player = newValue + } + } + } +} +#endif diff --git a/Sources/SwiftyChat/Extension/String++.swift b/Sources/SwiftyChat/Extension/String++.swift index 6fe0f1ce..482fb1ee 100644 --- a/Sources/SwiftyChat/Extension/String++.swift +++ b/Sources/SwiftyChat/Extension/String++.swift @@ -6,10 +6,6 @@ // Copyright © 2020 All rights reserved. // -#if canImport(UIKit) -import UIKit -#endif - /// Emoji helper internal extension Character { /// A simple emoji is one scalar and presented to the user as an Emoji diff --git a/Sources/SwiftyChat/Extension/UIDevice++.swift b/Sources/SwiftyChat/Extension/UIDevice++.swift index c730461d..05846212 100644 --- a/Sources/SwiftyChat/Extension/UIDevice++.swift +++ b/Sources/SwiftyChat/Extension/UIDevice++.swift @@ -19,20 +19,6 @@ public struct Device { #endif - #if os(macOS) return false - #endif - } - - public static var isPortrait: Bool { - #if os(iOS) - return UIDevice.current.orientation == .portrait || - UIDevice.current.orientation == .portraitUpsideDown - - #endif - - #if os(macOS) - return false - #endif } } diff --git a/Sources/SwiftyChat/InputView/BasicInputView.swift b/Sources/SwiftyChat/InputView/BasicInputView.swift index c7d07295..30130287 100644 --- a/Sources/SwiftyChat/InputView/BasicInputView.swift +++ b/Sources/SwiftyChat/InputView/BasicInputView.swift @@ -26,7 +26,7 @@ public struct BasicInputView: View { @ViewBuilder private var messageEditorView: some View { - if #available(iOS 16.0, *) { + if #available(iOS 16.0, macOS 13.0, *) { TextField(placeholder, text: $message, axis: .vertical) .lineLimit(5) } else { diff --git a/Sources/SwiftyChat/MessageCells/ContactCell.swift b/Sources/SwiftyChat/MessageCells/ContactCell.swift index a3867d45..5ca793e7 100644 --- a/Sources/SwiftyChat/MessageCells/ContactCell.swift +++ b/Sources/SwiftyChat/MessageCells/ContactCell.swift @@ -42,7 +42,7 @@ internal struct ContactCell: View { // MARK: - Image @ViewBuilder private var contactImage: some View { if let contactImage = contact.image { - Image(uiImage: contactImage) + Image(image: contactImage) .resizable() .frame( width: imageStyle.imageSize.width, diff --git a/Sources/SwiftyChat/MessageCells/ImageCell.swift b/Sources/SwiftyChat/MessageCells/ImageCell.swift index f87d1d99..51128c56 100644 --- a/Sources/SwiftyChat/MessageCells/ImageCell.swift +++ b/Sources/SwiftyChat/MessageCells/ImageCell.swift @@ -19,9 +19,9 @@ internal struct ImageLoadingKindCell: View { self.imageLoadingType = kind self.width = width - if case .remote(let url) = kind, let width = width, height == nil { + if case .remote(let url) = kind, let width, height == nil { let path = ImageCache.default.cachePath(forKey: url.cacheKey) - if let image = UIImage.init(contentsOfFile: path) { + if let image = PlatformImage(contentsOfFile: path) { self.height = image.size.height * (width / image.size.width) return } @@ -36,19 +36,19 @@ internal struct ImageLoadingKindCell: View { @ViewBuilder private var imageView: some View { switch imageLoadingType { - case .local(let image): localImage(uiImage: image) + case .local(let image): localImage(image) case .remote(let remoteUrl): remoteImage(url: remoteUrl) } } - @ViewBuilder private func localImage(uiImage: UIImage) -> some View { - let _width = uiImage.size.width - let _height = uiImage.size.height - let isLandscape = _width > _height + @ViewBuilder private func localImage(_ image: PlatformImage) -> some View { + let width = image.size.width + let height = image.size.height + let isLandscape = width > height - Image(uiImage: uiImage) + Image(image: image) .resizable() - .aspectRatio(_width / _height, contentMode: isLandscape ? .fit : .fill) + .aspectRatio(width / height, contentMode: isLandscape ? .fit : .fill) .frame(width: width, height: height) } @@ -123,3 +123,14 @@ internal struct ImageCell: View { } } +extension Image { + init(image: PlatformImage) { + #if os(iOS) + self.init(uiImage: image) + #endif + + #if os(macOS) + self.init(nsImage: image) + #endif + } +} diff --git a/Sources/SwiftyChat/Mock/MockMessages.swift b/Sources/SwiftyChat/Mock/MockMessages.swift index 53d47d74..02c1c3b3 100644 --- a/Sources/SwiftyChat/Mock/MockMessages.swift +++ b/Sources/SwiftyChat/Mock/MockMessages.swift @@ -8,10 +8,6 @@ import Foundation -#if canImport(UIKit) -import UIKit -#endif - public struct MockMessages { public enum Kind { @@ -47,7 +43,7 @@ public struct MockMessages { // MARK: - Concrete model for Contact private struct ContactRow: ContactItem { var displayName: String - var image: UIImage? + var image: PlatformImage? var initials: String = "" var phoneNumbers: [String] = [] var emails: [String] = [] @@ -109,12 +105,12 @@ public struct MockMessages { public var userName: String /// User's chat profile image, considered if `avatarURL` is nil - public var avatar: UIImage? - + public var avatar: PlatformImage? + /// User's chat profile image URL public var avatarURL: URL? - public init(userName: String, avatarURL: URL? = nil, avatar: UIImage? = nil) { + public init(userName: String, avatarURL: URL? = nil, avatar: PlatformImage? = nil) { self.userName = userName self.avatar = avatar self.avatarURL = avatarURL @@ -136,7 +132,7 @@ public struct MockMessages { [sender, chatbot].randomElement()! } - public static var mockImages: [UIImage] = [] + public static var mockImages: [PlatformImage] = [] public static func generateMessage(kind: MockMessages.Kind, count: UInt) -> [ChatMessageItem] { (1...count).map { _ in generateMessage(kind: kind) } diff --git a/Sources/SwiftyChat/Model/ChatMessageKind.swift b/Sources/SwiftyChat/Model/ChatMessageKind.swift index f55eead0..c4dc01f5 100644 --- a/Sources/SwiftyChat/Model/ChatMessageKind.swift +++ b/Sources/SwiftyChat/Model/ChatMessageKind.swift @@ -9,7 +9,7 @@ import SwiftUI public enum ImageLoadingKind { - case local(UIImage) + case local(PlatformImage) case remote(URL) } diff --git a/Sources/SwiftyChat/Protocols/ChatUser.swift b/Sources/SwiftyChat/Protocols/ChatUser.swift index 911fd45f..c7fd2238 100644 --- a/Sources/SwiftyChat/Protocols/ChatUser.swift +++ b/Sources/SwiftyChat/Protocols/ChatUser.swift @@ -8,14 +8,14 @@ #if os(iOS) import UIKit + +public typealias PlatformImage = UIImage #endif #if os(macOS) import AppKit -import Cocoa -public typealias UIImage = NSImage -public typealias URL = NSURL +public typealias PlatformImage = NSImage #endif public protocol ChatUser: Identifiable, Equatable { @@ -24,7 +24,7 @@ public protocol ChatUser: Identifiable, Equatable { var userName: String { get } /// User's chat profile image, considered if `avatarURL` is nil - var avatar: UIImage? { get } + var avatar: PlatformImage? { get } /// User's chat profile image URL var avatarURL: URL? { get } diff --git a/Sources/SwiftyChat/Protocols/ContactItem.swift b/Sources/SwiftyChat/Protocols/ContactItem.swift index 63a8c315..84ef14e0 100644 --- a/Sources/SwiftyChat/Protocols/ContactItem.swift +++ b/Sources/SwiftyChat/Protocols/ContactItem.swift @@ -7,10 +7,6 @@ import Foundation -#if canImport(UIKit) -import UIKit -#endif - /// Represents the data for a contact. public protocol ContactItem { @@ -18,8 +14,8 @@ public protocol ContactItem { var displayName: String { get } /// contact profile image - var image: UIImage? { get } - + var image: PlatformImage? { get } + /// initials from contact first and last name var initials: String { get } diff --git a/SwiftyChatExample/SwiftyChatExample/AppDelegate.swift b/SwiftyChatExample/SwiftyChatExample/AppDelegate.swift deleted file mode 100644 index 29e67a25..00000000 --- a/SwiftyChatExample/SwiftyChatExample/AppDelegate.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// AppDelegate.swift -// SwiftyChatExample -// -// Created by Enes Karaosman on 20.10.2020. -// - -import UIKit - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - - - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - return true - } - - // MARK: UISceneSession Lifecycle - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. - } - - -} - diff --git a/SwiftyChatExample/SwiftyChatExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/SwiftyChatExample/SwiftyChatExample/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 9221b9bb..00000000 --- a/SwiftyChatExample/SwiftyChatExample/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/SwiftyChatExample/SwiftyChatExample/Base.lproj/LaunchScreen.storyboard b/SwiftyChatExample/SwiftyChatExample/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e9329..00000000 --- a/SwiftyChatExample/SwiftyChatExample/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SwiftyChatExample/SwiftyChatExample/Info.plist b/SwiftyChatExample/SwiftyChatExample/Info.plist deleted file mode 100644 index 7a638465..00000000 --- a/SwiftyChatExample/SwiftyChatExample/Info.plist +++ /dev/null @@ -1,71 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - - - - - UIApplicationSupportsIndirectInputEvents - - UIBackgroundModes - - audio - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/SwiftyChatExample/SwiftyChatExample/SceneDelegate.swift b/SwiftyChatExample/SwiftyChatExample/SceneDelegate.swift deleted file mode 100644 index 93408a53..00000000 --- a/SwiftyChatExample/SwiftyChatExample/SceneDelegate.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// SceneDelegate.swift -// SwiftyChatExample -// -// Created by Enes Karaosman on 20.10.2020. -// - -import UIKit -import SwiftUI - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - - var window: UIWindow? - - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - - // Create the SwiftUI view that provides the window contents. - let contentView = ChatListView() - - // Use a UIHostingController as window root view controller. - if let windowScene = scene as? UIWindowScene { - let window = UIWindow(windowScene: windowScene) - window.rootViewController = UIHostingController(rootView: contentView) - self.window = window - window.makeKeyAndVisible() - } - } - - func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). - } - - func sceneDidBecomeActive(_ scene: UIScene) { - // Called when the scene has moved from an inactive state to an active state. - // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. - } - - func sceneWillResignActive(_ scene: UIScene) { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). - } - - func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. - } - - func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. - } - - -} -