├── SwiftUI_NotificationBanner_Example
├── SwiftUI_NotificationBanner_Example
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── SwiftUI_NotificationBanner_ExampleApp.swift
│ ├── RootView.swift
│ ├── AppDelegate.swift
│ ├── SceneDelegate.swift
│ └── NotificationRootView.swift
├── SwiftUI_NotificationBanner_Example_macOS
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── SwiftUI_NotificationBanner_Example_macOS.entitlements
│ ├── ContentView.swift
│ └── SwiftUI_NotificationBanner_Example_macOSApp.swift
├── SwiftUI_NotificationBanner_Example.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── SwiftUI_NotificationBanner_Example.xcscheme
│ └── project.pbxproj
├── SwiftUI_NotificationBanner_Example_macOSUITests
│ ├── SwiftUI_NotificationBanner_Example_macOSUITestsLaunchTests.swift
│ └── SwiftUI_NotificationBanner_Example_macOSUITests.swift
└── SwiftUI_NotificationBanner_Example_macOSTests
│ └── SwiftUI_NotificationBanner_Example_macOSTests.swift
├── .gitignore
├── Sources
└── SwiftUI_NotificationBanner
│ ├── Model
│ ├── Shadow.swift
│ ├── DYNotificationTypeSettings.swift
│ ├── DYNotification.swift
│ └── DYNotificationHandler.swift
│ ├── View
│ ├── PassThroughWindow.swift
│ ├── NotificationScene.swift
│ ├── Example.swift
│ └── DYNotificationBanner.swift
│ └── Shape
│ └── RoundedCornerRectangle.swift
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ └── xcschemes
│ └── SwiftUI_NotificationBanner.xcscheme
├── Tests
└── SwiftUI_NotificationBannerTests
│ └── SwiftUI_NotificationBannerTests.swift
├── LICENSE
├── Package.swift
├── SwiftUI_NotificationBanner.podspec
└── README.md
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOS/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example/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 |
--------------------------------------------------------------------------------
/Sources/SwiftUI_NotificationBanner/Model/Shadow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Dominik Butz on 24/10/2022.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 |
12 | internal typealias Shadow = (color: Color, radius: CGFloat, x: CGFloat, y: CGFloat)
13 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOS/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 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOS/SwiftUI_NotificationBanner_Example_macOS.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Tests/SwiftUI_NotificationBannerTests/SwiftUI_NotificationBannerTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import SwiftUI_NotificationBanner
3 |
4 | final class SwiftUI_NotificationBannerTests: XCTestCase {
5 | func testExample() throws {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | // XCTAssertEqual(SwiftUI_NotificationBanner().text, "Hello, World!")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOS/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // SwiftUI_NotificationBanner_Example_macOS
4 | //
5 | // Created by Dominik Butz on 4/7/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ContentView: View {
11 | var body: some View {
12 | VStack {
13 | Image(systemName: "globe")
14 | .imageScale(.large)
15 | .foregroundColor(.accentColor)
16 | Text("Hello, world!")
17 | }
18 | .padding()
19 | }
20 | }
21 |
22 | struct ContentView_Previews: PreviewProvider {
23 | static var previews: some View {
24 | ContentView()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_ExampleApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUI_NotificationBanner_ExampleApp.swift
3 | // SwiftUI_NotificationBanner_Example
4 | //
5 | // Created by Dominik Butz on 11/11/2022.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftUI_NotificationBanner
10 |
11 | @main
12 | struct SwiftUI_NotificationBanner_ExampleApp: App {
13 |
14 | @StateObject var notificationHandler = DYNotificationHandler()
15 |
16 | @UIApplicationDelegateAdaptor var delegate: AppDelegate
17 |
18 | var body: some Scene {
19 | WindowGroup {
20 | RootView().environmentObject(notificationHandler)
21 | }
22 |
23 |
24 | }
25 | }
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example/RootView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // SwiftUI_NotificationBanner_Example
4 | //
5 | // Created by Dominik Butz on 11/11/2022.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftUI_NotificationBanner
10 |
11 | struct RootView: View {
12 | #if os(iOS)
13 | @EnvironmentObject var sceneDelegate: SceneDelegate
14 | #endif
15 | @EnvironmentObject var notificationHandler: DYNotificationHandler
16 |
17 | var body: some View {
18 | Example().environmentObject(notificationHandler)
19 | #if os(iOS)
20 | .onAppear {
21 | sceneDelegate.notificationHandler = notificationHandler
22 | }
23 | #endif
24 | }
25 | }
26 |
27 | //struct RootView_Previews: PreviewProvider {
28 | // static var previews: some View {
29 | // RootView()
30 | // }
31 | //}
32 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SwiftUI_NotificationBanner_Example
4 | //
5 | // Created by Dominik Butz on 14/11/2022.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | #if os(iOS)
12 | class AppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
13 | // not required in this example
14 | // func application(
15 | // _ application: UIApplication,
16 | // didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
17 | // ) -> Bool {
18 | // // ...
19 | // return true
20 | // }
21 |
22 | func application(
23 | _ application: UIApplication,
24 | configurationForConnecting connectingSceneSession: UISceneSession,
25 | options: UIScene.ConnectionOptions
26 | ) -> UISceneConfiguration {
27 | let sceneConfig = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
28 | sceneConfig.delegateClass = SceneDelegate.self
29 | return sceneConfig
30 | }
31 | }
32 | #endif
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Dominik Butz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOSUITests/SwiftUI_NotificationBanner_Example_macOSUITestsLaunchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUI_NotificationBanner_Example_macOSUITestsLaunchTests.swift
3 | // SwiftUI_NotificationBanner_Example_macOSUITests
4 | //
5 | // Created by Dominik Butz on 4/7/2023.
6 | //
7 |
8 | import XCTest
9 |
10 | final class SwiftUI_NotificationBanner_Example_macOSUITestsLaunchTests: 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 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.7
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "SwiftUI_NotificationBanner",
8 | platforms: [
9 | .iOS(.v14), .macOS(.v11)
10 | ],
11 | products: [
12 | // Products define the executables and libraries a package produces, and make them visible to other packages.
13 | .library(
14 | name: "SwiftUI_NotificationBanner",
15 | targets: ["SwiftUI_NotificationBanner"]),
16 | ],
17 | dependencies: [
18 | // Dependencies declare other packages that this package depends on.
19 | // .package(url: /* package url */, from: "1.0.0"),
20 | ],
21 | targets: [
22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
23 | // Targets can depend on other targets in this package, and on products in packages this package depends on.
24 | .target(
25 | name: "SwiftUI_NotificationBanner",
26 | dependencies: []),
27 | .testTarget(
28 | name: "SwiftUI_NotificationBannerTests",
29 | dependencies: ["SwiftUI_NotificationBanner"]),
30 | ]
31 | )
32 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner.podspec:
--------------------------------------------------------------------------------
1 |
2 |
3 | #
4 | # Any lines starting with a # are optional, but their use is encouraged
5 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
6 | #
7 |
8 | Pod::Spec.new do |s|
9 | s.name = 'SwiftUI_NotificationBanner'
10 | s.version = '0.3'
11 | s.summary = 'With SwiftUI Notification Banner it is super-easy to display in-app notifications.'
12 | s.swift_version = '5.1'
13 |
14 |
15 | s.description = <<-DESC
16 | Finally a native SwiftUI notification banner package! With SwiftUI Notification Banner it is super-easy to display in-app notifications.
17 | DESC
18 |
19 | s.homepage = 'https://github.com/DominikButz/SwiftUI_NotificationBanner'
20 | s.license = { :type => 'MIT', :file => 'LICENSE' }
21 | s.author = { 'dominikbutz' => 'dominikbutz@gmail.com' }
22 | s.source = { :git => 'https://github.com/DominikButz/SwiftUI_NotificationBanner.git', :tag => s.version.to_s }
23 |
24 | s.platform = :ios
25 | s.ios.deployment_target = '14.0'
26 |
27 | s.platform = :macos
28 | s.macos.deployment_target = '11.0'
29 |
30 | s.source_files = 'Sources/**/*'
31 |
32 |
33 | # s.public_header_files = 'SwiftUI_NotificationBanner/**/*.h'
34 |
35 | end
36 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOSTests/SwiftUI_NotificationBanner_Example_macOSTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUI_NotificationBanner_Example_macOSTests.swift
3 | // SwiftUI_NotificationBanner_Example_macOSTests
4 | //
5 | // Created by Dominik Butz on 4/7/2023.
6 | //
7 |
8 | import XCTest
9 | @testable import SwiftUI_NotificationBanner_Example_macOS
10 |
11 | final class SwiftUI_NotificationBanner_Example_macOSTests: 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 | // Any test you write for XCTest can be annotated as throws and async.
25 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
26 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
27 | }
28 |
29 | func testPerformanceExample() throws {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/SwiftUI_NotificationBanner/View/PassThroughWindow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PassThroughWindow.swift
3 | //
4 | //
5 | // Created by Federico Zanetello (www.fivestars.blog) in 2021
6 | //
7 |
8 | #if canImport(UIKit)
9 | import Foundation
10 | import UIKit
11 | /// PassThroughWindow. can used as window for a custom notification root view. This window is placed above the existing app window
12 | public class PassThroughWindow: UIWindow {
13 | public var notificationHandler: DYNotificationHandler?
14 | /// hitTest - override function
15 | /// - Parameters:
16 | /// - point: a CGPoint locating the hit event
17 | /// - event: UIEvent
18 | /// - Returns: view of the root view controller or nil
19 | public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
20 | guard let hitView = super.hitTest(point, with: event) else {
21 |
22 | return nil
23 | }
24 |
25 | // If there's no notification showing, pass through touches to the main window
26 | guard let _ = notificationHandler?.currentNotification else {
27 |
28 | return rootViewController?.view == hitView ? nil : hitView
29 | }
30 |
31 | if let rootView = rootViewController?.view,
32 | let notificationView = rootView.hitTest(point, with: event),
33 | notificationView.isDescendant(of: rootView)
34 | {
35 |
36 | //print("passthrough window: touched the notifcation root view")
37 | return hitView
38 | }
39 |
40 | // Otherwise, pass through the touch to the window below
41 | return nil
42 | }
43 |
44 |
45 | }
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOSUITests/SwiftUI_NotificationBanner_Example_macOSUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUI_NotificationBanner_Example_macOSUITests.swift
3 | // SwiftUI_NotificationBanner_Example_macOSUITests
4 | //
5 | // Created by Dominik Butz on 4/7/2023.
6 | //
7 |
8 | import XCTest
9 |
10 | final class SwiftUI_NotificationBanner_Example_macOSUITests: 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 XCTAssert and related functions to verify your tests produce the correct results.
31 | }
32 |
33 | func testLaunchPerformance() throws {
34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
35 | // This measures how long it takes to launch your application.
36 | measure(metrics: [XCTApplicationLaunchMetric()]) {
37 | XCUIApplication().launch()
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // SwiftUI_NotificationBanner_Example
4 | //
5 | // Created by Dominik Butz on 14/11/2022.
6 | //
7 |
8 | #if os(iOS)
9 | import Foundation
10 | import SwiftUI
11 | import SwiftUI_NotificationBanner
12 |
13 |
14 | class SceneDelegate: UIResponder, UIWindowSceneDelegate, ObservableObject {
15 |
16 | var notificationHandler: DYNotificationHandler? {
17 | didSet {
18 | setupNotificationWindow()
19 | }
20 | }
21 |
22 | var notificationWindow: UIWindow?
23 | weak var windowScene: UIWindowScene?
24 |
25 | func scene(
26 | _ scene: UIScene,
27 | willConnectTo session: UISceneSession,
28 | options connectionOptions: UIScene.ConnectionOptions
29 | ) {
30 | windowScene = scene as? UIWindowScene
31 |
32 | }
33 |
34 | // without a second app window, the notification banner will not appear above a sheet, fullScreenCover etc.
35 | // special thanks to Federico Zanetello (www.fivestars.blog) for this function
36 | func setupNotificationWindow() {
37 | guard let windowScene = windowScene, let notificationHandler = notificationHandler else {
38 |
39 | return
40 | }
41 |
42 | let notificationViewController = UIHostingController(rootView: NotificationRootView().environmentObject(notificationHandler))
43 | notificationViewController.view.backgroundColor = .clear
44 |
45 | let notificationWindow = PassThroughWindow(windowScene: windowScene)
46 | notificationWindow.notificationHandler = notificationHandler
47 | notificationWindow.rootViewController = notificationViewController
48 | notificationWindow.isHidden = false
49 | self.notificationWindow = notificationWindow
50 | }
51 | }
52 | #endif
53 |
--------------------------------------------------------------------------------
/Sources/SwiftUI_NotificationBanner/Shape/RoundedCornerRectangle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoundedCornerRectangle.swift
3 | // Progressive
4 | //
5 | // Created by Dominik Butz on 28/5/2020.
6 | // Copyright © 2020 Duoyun. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | internal struct RoundedCornerRectangle: Shape {
13 | var tl: CGFloat = 0.0
14 | var tr: CGFloat = 0.0
15 | var bl: CGFloat = 0.0
16 | var br: CGFloat = 0.0
17 |
18 | func path(in rect: CGRect) -> Path {
19 | var path = Path()
20 |
21 | let w = rect.size.width
22 | let h = rect.size.height
23 |
24 | // Make sure we do not exceed the size of the rectangle
25 | let tr = min(min(self.tr, h/2), w/2)
26 | let tl = min(min(self.tl, h/2), w/2)
27 | let bl = min(min(self.bl, h/2), w/2)
28 | let br = min(min(self.br, h/2), w/2)
29 |
30 | path.move(to: CGPoint(x: w / 2.0, y: 0))
31 | path.addLine(to: CGPoint(x: w - tr, y: 0))
32 | path.addArc(center: CGPoint(x: w - tr, y: tr), radius: tr,
33 | startAngle: Angle(degrees: -90), endAngle: Angle(degrees: 0), clockwise: false)
34 |
35 | path.addLine(to: CGPoint(x: w, y: h - br))
36 | path.addArc(center: CGPoint(x: w - br, y: h - br), radius: br,
37 | startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 90), clockwise: false)
38 |
39 | path.addLine(to: CGPoint(x: bl, y: h))
40 | path.addArc(center: CGPoint(x: bl, y: h - bl), radius: bl,
41 | startAngle: Angle(degrees: 90), endAngle: Angle(degrees: 180), clockwise: false)
42 |
43 | path.addLine(to: CGPoint(x: 0, y: tl))
44 | path.addArc(center: CGPoint(x: tl, y: tl), radius: tl,
45 | startAngle: Angle(degrees: 180), endAngle: Angle(degrees: 270), clockwise: false)
46 |
47 | return path
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example/NotificationRootView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DYNotificationWindowView.swift
3 | //
4 | //
5 | // Created by Dominik Butz on 14/11/2022.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftUI_NotificationBanner
10 |
11 | struct NotificationRootView: View {
12 | @EnvironmentObject var notificationHandler: DYNotificationHandler
13 | @Environment(\.colorScheme) var colorScheme
14 |
15 | var body: some View {
16 | Color.clear
17 | .ignoresSafeArea(.all)
18 | .notificationView(handler: notificationHandler, notificationBanner: {notification in
19 |
20 | DYNotificationBanner(notification: notification, frameWidth: min(450, UIScreen.main.bounds.size.width))
21 | .text(color: foregroundColorForNotification(type: notification.type))
22 | .image(color: foregroundColorForNotification(type: notification.type))
23 | .dropShadow(color: colorScheme == .light ? .gray.opacity(0.4) : .clear, radius: 5, x: 0, y: notification.displayEdge == .top ? 5 : -5)
24 | .cornerRadius(self.cornerRadius(displayEdge: notification.displayEdge))
25 |
26 |
27 | }).statusBarHidden(notificationHandler.currentNotification?.displayEdge == .top)
28 |
29 | }
30 |
31 | func foregroundColorForNotification(type: DYNotificationType)->Color {
32 | switch type {
33 | case .info, .error:
34 | return .white
35 | case .warning, .success:
36 | return .black
37 | }
38 | }
39 |
40 |
41 | func cornerRadius(displayEdge: Edge)-> CGFloat {
42 | guard displayEdge != .leading && displayEdge != .trailing else {
43 | return UIDevice.current.userInterfaceIdiom == .phone ? 5 : 10
44 | }
45 | guard UIDevice.current.userInterfaceIdiom == .phone else {
46 | return 10
47 | }
48 | return UIScreen.main.bounds.width < UIScreen.main.bounds.height ? 0 : 10
49 | }
50 | }
51 |
52 |
53 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example_macOS/SwiftUI_NotificationBanner_Example_macOSApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUI_NotificationBanner_Example_macOSApp.swift
3 | // SwiftUI_NotificationBanner_Example_macOS
4 | //
5 | // Created by Dominik Butz on 4/7/2023.
6 | //
7 | import SwiftUI
8 | import SwiftUI_NotificationBanner
9 |
10 | @main
11 | struct SwiftUI_NotificationBanner_ExampleApp: App {
12 | @Environment(\.colorScheme) var colorScheme
13 | @StateObject var notificationHandler = DYNotificationHandler()
14 |
15 | var body: some Scene {
16 | WindowGroup {
17 | RootView().environmentObject(notificationHandler)
18 | .notificationView(handler: notificationHandler, notificationBanner: {notification in
19 |
20 | DYNotificationBanner(notification: notification, frameWidth: 450)
21 | .text(color: foregroundColorForNotification(type: notification.type))
22 | .image(color: foregroundColorForNotification(type: notification.type))
23 | // .backgroundGradientForNotificationType(success: LinearGradient(colors: [.green.opacity(0.4), .green], startPoint: .leading, endPoint: .trailing), error: LinearGradient(colors: [.red, .red.opacity(0.3)], startPoint: .top, endPoint: .bottom))
24 | .dropShadow(color: colorScheme == .light ? .gray.opacity(0.4) : .clear, radius: 5, x: 0, y: notification.displayEdge == .top ? 5 : -5)
25 | .cornerRadius(self.cornerRadius(displayEdge: notification.displayEdge))
26 |
27 | })
28 |
29 | }
30 |
31 |
32 |
33 |
34 | }
35 |
36 |
37 | func foregroundColorForNotification(type: DYNotificationType)->Color {
38 | switch type {
39 | case .info, .error:
40 | return .white
41 | case .warning, .success:
42 | return .black
43 | }
44 | }
45 |
46 |
47 | func cornerRadius(displayEdge: Edge)-> CGFloat {
48 | return 10
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/SwiftUI_NotificationBanner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
54 |
60 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Sources/SwiftUI_NotificationBanner/View/NotificationScene.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIView.swift
3 | //
4 | //
5 | // Created by Dominik Butz on 10/11/2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | internal struct NotificationScene: ViewModifier {
11 | @ObservedObject var notificationHandler: DYNotificationHandler
12 | let animation: Animation
13 | var notificationView: (DYNotification)->N
14 |
15 | func body(content: Content)-> some View {
16 |
17 | ZStack {
18 |
19 | content
20 |
21 | VStack {
22 |
23 | if let notification = self.notificationHandler.currentNotification, notification.displayEdge == .bottom {
24 | Spacer()
25 | }
26 |
27 | if let notification = self.notificationHandler.currentNotification {
28 | self.notificationView(notification)
29 | .onTapGesture {
30 | print("tapped notification banner view")
31 | if notification.dismissOnTap {
32 | self.notificationHandler.remove(notification: notification, userInitiated: true) // remove the current notfication, initiated by user
33 | }
34 | print("calling tap handler if applicable")
35 | notification.tapHandler?()
36 | }
37 | .id(notification.id)
38 | .transition(AnyTransition.move(edge: notification.displayEdge))
39 |
40 | }
41 |
42 | if let notification = self.notificationHandler.currentNotification, notification.displayEdge == .top {
43 | Spacer()
44 | }
45 | }
46 |
47 |
48 | }
49 | .edgesIgnoringSafeArea(.all)
50 | .animation(animation, value: notificationHandler.currentNotification)
51 |
52 |
53 |
54 | }
55 |
56 |
57 |
58 | }
59 |
60 | public extension View {
61 |
62 | /// notificationView modifier function
63 | /// - Parameters:
64 | /// - handler: a DYNotificationHandler object
65 | /// - animation: appear animation of the notfification banner
66 | /// - notificationView: notificationView closue - return a DYNotificationView or a custom view.
67 | /// - Returns: some View
68 | func notificationView(handler: DYNotificationHandler, animation: Animation = .easeInOut(duration: 0.5), notificationBanner: @escaping (DYNotification)->N)->some View {
69 | self.modifier(NotificationScene(notificationHandler: handler, animation: animation, notificationView: notificationBanner))
70 | }
71 |
72 | }
73 |
74 | //struct DYNotificationScene_Previews: PreviewProvider {
75 | // static var previews: some View {
76 | // DYNotificationScene()
77 | // }
78 | //}
79 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example.xcodeproj/xcshareddata/xcschemes/SwiftUI_NotificationBanner_Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
44 |
50 |
51 |
52 |
53 |
59 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Sources/SwiftUI_NotificationBanner/Model/DYNotificationTypeSettings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Dominik Butz on 10/11/2022.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | internal struct DYNotificationBannerSettings {
12 |
13 | //text settings
14 | var titleFont: Font = .headline
15 | var messageFont: Font = .body
16 | var textColor: Color = .primary
17 |
18 | // image settings
19 | var imageMaxHeight: CGFloat? = 40
20 | var imageMaxWidth: CGFloat? = 40
21 | var imageContentMode: ContentMode = .fit
22 | var imageRenderingMode: Image.TemplateRenderingMode = .template
23 | var imageColor: Color? = .primary
24 |
25 | // background settings
26 | var infoBannerBackgroundGradient: LinearGradient = LinearGradient(colors: [.blue], startPoint: .top, endPoint: .bottom)
27 | var successBannerBackgroundGradient: LinearGradient = LinearGradient(colors: [.green], startPoint: .top, endPoint: .bottom)
28 | var warningBannerBackgroundGradient: LinearGradient = LinearGradient(colors: [.yellow], startPoint: .top, endPoint: .bottom)
29 | var errorBannerBackgroundGradient: LinearGradient = LinearGradient(colors: [.red], startPoint: .top, endPoint: .bottom)
30 | var dropShadow: Shadow? = nil
31 | var cornerRadius: CGFloat = 0
32 |
33 | //
34 | //
35 | // init(titleFont: Font = .headline, messageFont: Font = .body, textColor:Color = Color.primary, imageMaxHeight: CGFloat? = 40, imageMaxWidth: CGFloat? = 40, imageContentMode: ContentMode = .fit, imageRenderingMode: Image.TemplateRenderingMode = .template, imageColor: Color? = Color.primary, infoBannerBackgroundGradient: LinearGradient = LinearGradient(colors: [.blue], startPoint: .top, endPoint: .bottom), successBannerBackgroundGradient: LinearGradient = LinearGradient(colors: [.green], startPoint: .top, endPoint: .bottom), warningBannerBackgroundGradient: LinearGradient = LinearGradient(colors: [.yellow], startPoint: .top, endPoint: .bottom), errorBannerBackgroundGradient: LinearGradient = LinearGradient(colors: [.red], startPoint: .top, endPoint: .bottom), dropShadow: Shadow? = nil, cornerRadius: CGFloat = 0) {
36 | // self.messageFont = messageFont
37 | // self.titleFont = titleFont
38 | // self.textColor = textColor
39 | // self.imageMaxHeight = imageMaxHeight
40 | // self.imageMaxWidth = imageMaxWidth
41 | // self.imageContentMode = imageContentMode
42 | // self.imageRenderingMode = imageRenderingMode
43 | // self.imageColor = imageColor
44 | // self.infoBannerBackgroundGradient = infoBannerBackgroundGradient
45 | // self.successBannerBackgroundGradient = successBannerBackgroundGradient
46 | // self.warningBannerBackgroundGradient = warningBannerBackgroundGradient
47 | // self.errorBannerBackgroundGradient = errorBannerBackgroundGradient
48 | // self.dropShadow = dropShadow
49 | // self.cornerRadius = cornerRadius
50 | // }
51 |
52 | func gradientFor(notificationType: DYNotificationType)->LinearGradient {
53 | switch notificationType {
54 | case .error:
55 | return self.errorBannerBackgroundGradient
56 | case .success:
57 | return self.successBannerBackgroundGradient
58 | case .warning:
59 | return self.warningBannerBackgroundGradient
60 | default:
61 | return self.infoBannerBackgroundGradient
62 | }
63 | }
64 |
65 |
66 | }
67 |
68 |
69 |
--------------------------------------------------------------------------------
/Sources/SwiftUI_NotificationBanner/Model/DYNotification.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DYNotification.swift
3 | //
4 | //
5 | // Created by Dominik Butz on 10/11/2022.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 |
12 |
13 | public struct DYNotification: Identifiable, Equatable {
14 |
15 | public let id: String
16 | public let message: String
17 | public var title: String?
18 | public var image: Image?
19 | public let type: DYNotificationType
20 | public let displayDuration: TimeInterval
21 | public let dismissOnTap: Bool
22 | public let displayEdge: Edge
23 | #if canImport(UIKit)
24 | public var hapticFeedbackType: UINotificationFeedbackGenerator.FeedbackType?
25 | #endif
26 | public var tapHandler: (()->Void)?
27 |
28 | #if canImport(UIKit)
29 | public init(id: String = UUID().uuidString, title: String? = nil, message: String, image: Image? = nil, type: DYNotificationType = .info, displayDuration: TimeInterval = 3, dismissOnTap: Bool = true, displayEdge: Edge = .top, hapticFeedbackType: UINotificationFeedbackGenerator.FeedbackType?, tapHandler: (()->Void)? = nil) {
30 | self.init(id: id, title: title, message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, tapHandler: tapHandler)
31 | self.hapticFeedbackType = hapticFeedbackType
32 | }
33 | // #else
34 | // public init(id: String = UUID().uuidString, title: String? = nil, message: String, image: Image? = nil, type: DYNotificationType = .info, displayDuration: TimeInterval = 3, dismissOnTap: Bool = true, displayEdge: Edge = .top, tapHandler: (()->Void)? = nil) {
35 | // self.init(id: id, title: title, message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, tapHandler: tapHandler)
36 | //
37 | // }
38 |
39 | #endif
40 |
41 |
42 | public init(id: String = UUID().uuidString, title: String? = nil, message: String, image: Image? = nil, type: DYNotificationType = .info, displayDuration: TimeInterval = 3, dismissOnTap: Bool = true, displayEdge: Edge = .top, tapHandler: (()->Void)? = nil) {
43 |
44 | self.id = id
45 | self.message = message
46 | self.title = title
47 | self.image = image
48 | self.type = type
49 | self.displayDuration = displayDuration
50 | self.dismissOnTap = dismissOnTap
51 | self.displayEdge = displayEdge
52 | self.tapHandler = tapHandler
53 | }
54 |
55 |
56 |
57 | public static func == (lhs: DYNotification, rhs: DYNotification) -> Bool {
58 | lhs.id == rhs.id
59 | }
60 |
61 | }
62 |
63 | public enum DYNotificationType {
64 |
65 | case info, success, warning, error
66 | }
67 |
68 | //public enum DYNotificationBannerEdge {
69 | // case top, bottom
70 | //}
71 |
72 |
73 | //public struct DYNotification: Identifiable, Equatable {
74 | //
75 | // public let id: String
76 | //
77 | // public let displayDuration: TimeInterval
78 | // public let dismissOnTap: Bool
79 | // public var notificationView: N
80 | //
81 | // public init(id: String = UUID().uuidString, displayDuration: TimeInterval = 3, dismissOnTap: Bool = true, notificationView: N) {
82 | // self.id = id
83 | // self.displayDuration = displayDuration
84 | // self.dismissOnTap = dismissOnTap
85 | // self.notificationView = notificationView
86 | // }
87 | //
88 | // public static func == (lhs: DYNotification, rhs: DYNotification) -> Bool {
89 | // lhs.id == rhs.id
90 | // }
91 | //
92 | //}
93 |
--------------------------------------------------------------------------------
/Sources/SwiftUI_NotificationBanner/Model/DYNotificationHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DYNotificationHandler.swift
3 | //
4 | //
5 | // Created by Dominik Butz on 10/11/2022.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 |
12 | /// Notification Handler
13 | public class DYNotificationHandler: ObservableObject {
14 |
15 | #if os(iOS)
16 | let feedbackGenerator: UINotificationFeedbackGenerator
17 | #endif
18 | let queue: OperationQueue
19 |
20 | var queuedOperations: [String: Operation ] = [:]
21 | var currentTimer: DispatchSourceTimer?
22 | var currentTimerNotificationId: String?
23 |
24 | @Published public var currentNotification: DYNotification?
25 |
26 | public init() {
27 |
28 | self.queue = OperationQueue()
29 | self.queue.maxConcurrentOperationCount = 1
30 | self.queue.qualityOfService = .userInteractive
31 |
32 | #if os(iOS)
33 | self.feedbackGenerator = UINotificationFeedbackGenerator()
34 | #endif
35 |
36 |
37 | }
38 |
39 |
40 | /// show function
41 | /// - Parameter notification: a DYNotificationHandler object
42 | public func show(notification: DYNotification) {
43 | #if os(iOS)
44 | self.feedbackGenerator.prepare()
45 | #endif
46 |
47 | let operation = BlockOperation()
48 |
49 | operation.addExecutionBlock {
50 | DispatchQueue.main.async {
51 | self.currentNotification = notification
52 | self.currentTimerNotificationId = notification.id
53 |
54 | #if os(iOS)
55 | if let hapticFeedbackType = self.currentNotification?.hapticFeedbackType {
56 | self.feedbackGenerator.notificationOccurred(hapticFeedbackType)
57 | }
58 | #endif
59 |
60 | // Create and start timer for auto-dismissal
61 | self.startDismissalTimer(for: notification)
62 | }
63 | }
64 |
65 | self.queuedOperations[notification.id] = operation
66 | self.queue.addOperation(operation)
67 | }
68 |
69 | private func startDismissalTimer(for notification: DYNotification) {
70 | // Cancel any existing timer
71 | currentTimer?.cancel()
72 |
73 | // Create new timer
74 | let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
75 | timer.schedule(deadline: .now() + notification.displayDuration)
76 |
77 | timer.setEventHandler { [weak self] in
78 | guard let self = self else { return }
79 |
80 | // Only auto-dismiss if this is still the current notification
81 | if self.currentTimerNotificationId == notification.id {
82 | self.remove(notification: notification, userInitiated: false)
83 | }
84 | }
85 |
86 | currentTimer = timer
87 | timer.resume()
88 | }
89 |
90 | func remove(notification: DYNotification, userInitiated: Bool = false) {
91 | if self.currentNotification == notification {
92 | self.currentNotification = nil
93 |
94 | // Cancel the current timer since notification is being removed
95 | if currentTimerNotificationId == notification.id {
96 | currentTimer?.cancel()
97 | currentTimer = nil
98 | currentTimerNotificationId = nil
99 | }
100 | }
101 |
102 | if let operation = self.queuedOperations.removeValue(forKey: notification.id) as? BlockOperation {
103 | operation.cancel()
104 |
105 | // If user initiated dismissal, show next notification immediately (after brief animation delay)
106 | // If auto-dismissal, use longer delay
107 | let delay: TimeInterval = userInitiated ? 0.5 : 1.1
108 |
109 | DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
110 | // The queue will automatically process the next operation
111 | }
112 | }
113 | }
114 |
115 | }
116 |
117 |
--------------------------------------------------------------------------------
/Sources/SwiftUI_NotificationBanner/View/Example.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIView.swift
3 | //
4 | //
5 | // Created by Dominik Butz on 10/11/2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public struct Example: View {
11 |
12 | @EnvironmentObject var notificationHandler: DYNotificationHandler
13 | @State private var sheetPresented: Bool = false
14 |
15 | public init() {}
16 |
17 | public var body: some View {
18 | GeometryReader { proxy in
19 | VStack(alignment: .center, spacing: 10) {
20 | Spacer()
21 |
22 | Button(action: {
23 |
24 | notificationHandler.show(notification: self.infoNotification)
25 | }, label: {
26 | Text("Info Banner")
27 | }).foregroundColor(.blue)
28 |
29 | Button(action: {
30 |
31 | notificationHandler.show(notification: self.warningNotification)
32 | }, label: {
33 | Text("Warning Status Banner")
34 | }).foregroundColor(.yellow)
35 |
36 | Button(action: {
37 | notificationHandler.show(notification: self.errorNotification)
38 | }, label: {
39 | Text("Error Status Banner")
40 | }).foregroundColor(.red)
41 |
42 | Spacer()
43 |
44 | #if os(iOS)
45 | Button {
46 | self.sheetPresented = true
47 | } label: {
48 | Text("Launch Sheet").padding(5).overlay(RoundedRectangle(cornerRadius: 10).stroke(.blue, lineWidth: 2))
49 | }
50 | .sheet(isPresented: $sheetPresented) {
51 | SheetView().environmentObject(notificationHandler)
52 | }.padding()
53 | #endif
54 | }
55 | .frame(width: proxy.size.width, height: proxy.size.height)
56 | }
57 |
58 | }
59 |
60 | var infoNotification: DYNotification {
61 |
62 | let title = "Warm reminder"
63 | let message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
64 | let image = Image(systemName: "checkmark.seal.fill")
65 | let type: DYNotificationType = .info
66 | let displayDuration: TimeInterval = 10
67 | let dismissOnTap = true
68 | let displayEdge: Edge = .leading
69 |
70 | #if os(iOS)
71 | return DYNotification(title: title, message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, hapticFeedbackType: .success, tapHandler: {
72 | print("info banner tapped")
73 | })
74 |
75 | #else
76 | return DYNotification(title: title, message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, tapHandler: {
77 | print("info banner tapped")
78 | })
79 | #endif
80 |
81 | }
82 |
83 | var warningNotification: DYNotification {
84 | let message = "Running out of time!"
85 | let image = Image(systemName: "checkmark.seal.fill")
86 | let type: DYNotificationType = .warning
87 | let displayDuration: TimeInterval = 5
88 | let dismissOnTap = true
89 | let displayEdge: Edge = .top
90 |
91 | #if os(iOS)
92 | return DYNotification(message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, hapticFeedbackType: .warning, tapHandler: {
93 | print("warning banner tapped")
94 | })
95 |
96 | #else
97 | return DYNotification(message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, tapHandler: {
98 | print("warning banner tapped")
99 | })
100 | #endif
101 |
102 | }
103 |
104 | var errorNotification: DYNotification {
105 | let title = "Error"
106 | let message = "Danger Zone! An unknown error occurred."
107 | let image = Image(systemName: "exclamationmark.triangle.fill")
108 | let type: DYNotificationType = .error
109 | let displayDuration: TimeInterval = 2
110 | let dismissOnTap = true
111 | let displayEdge: Edge = .bottom
112 |
113 | #if os(iOS)
114 | return DYNotification(title: title, message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, hapticFeedbackType: .error, tapHandler: {
115 | print("error banner tapped")
116 | })
117 |
118 | #else
119 | return DYNotification(title: title, message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, tapHandler: {
120 | print("error banner tapped")
121 | })
122 | #endif
123 |
124 | }
125 |
126 |
127 | }
128 |
129 | struct SheetView: View {
130 | @EnvironmentObject var notificationHandler: DYNotificationHandler
131 | var body: some View {
132 |
133 | VStack {
134 | Text("Sheet Header").font(.title).padding()
135 | Spacer()
136 | Button(action: {
137 | notificationHandler.show(notification: self.successNotification)
138 | }, label: {
139 | Text("Launch success banner above sheet!")
140 | }).foregroundColor(.green)
141 | Spacer()
142 | }
143 | }
144 |
145 | var successNotification: DYNotification {
146 | let title = "Success"
147 | let message = "Notification banner successfully presented above sheet!"
148 | let image = Image(systemName: "checkmark.circle")
149 | let type: DYNotificationType = .success
150 | let displayDuration: TimeInterval = 3
151 | let dismissOnTap = true
152 | let displayEdge: Edge = .top
153 |
154 | #if os(iOS)
155 | return DYNotification(title: title, message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, hapticFeedbackType: .success, tapHandler: {
156 | print("success banner tapped")
157 | })
158 |
159 | #else
160 | return DYNotification(title: title, message: message, image: image, type: type, displayDuration: displayDuration, dismissOnTap: dismissOnTap, displayEdge: displayEdge, tapHandler: {
161 | print("success banner tapped")
162 | })
163 | #endif
164 |
165 | }
166 | }
167 |
168 |
169 |
170 | //#if DEBUG
171 | //struct Example_Previews: PreviewProvider {
172 | //
173 | // static var previews: some View {
174 | //
175 | // Example()
176 | // }
177 | //}
178 | //#endif
179 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftUI Notification Banner (v0.3)
2 |
3 | ## Features
4 |
5 | Finally a native SwiftUI notification banner package! With SwiftUI Notification Banner it is super-easy to display in-app notifications.
6 |
7 | * Attach a notificationView-modifier to the top view in your view hierarchy to make sure it will always appear on top
8 | * The notificationBanner closure is agnostic to what kind of View you pass in - you can use the default DYNotificationBanner or simply create your own banner view!
9 | * From version 0.3, the package also works natively under macOS.
10 | * Check out the code example below and the example project included in this package for more details.
11 |
12 | ## Installation
13 |
14 | ### Swift Package Manager
15 | Simply copy the github link of this project into the Swift Package Manager and install it. Don't forget to add the library to your target.
16 | To use the notificationView-modifier, DYNotificationBanner and DYNotificationHandler, import SwiftUI_NotificationBanner.
17 |
18 | ### Cocoapods
19 | platform :ios, '14.0'
20 |
21 | target '[project name]' do
22 | pod 'SwiftUI_NotificationBanner'
23 | end
24 |
25 | ## Code Example
26 |
27 | ### Basics
28 |
29 | 
30 |
31 |
32 | As shown below in the code example DYNotificationBanner can be modified easily by applying multiple modifiers.
33 | Here is a complete list including the given default values:
34 |
35 | * text(titleFont: Font = .headline, messageFont: Font = .body, color: Color = .primary)
36 | * image(maxHeight: CGFloat? = 40, maxWidth: CGFloat? = 40, contentMode: ContentMode = .fit, renderingMode: Image.TemplateRenderingMode = .template, color: Color? = .primary)
37 | * backgroundGradientForNotificationType(info: LinearGradient = LinearGradient(colors: [.blue], startPoint: .top, endPoint: .bottom), success: LinearGradient = LinearGradient(colors: [.green], startPoint: .top, endPoint: .bottom), warning: LinearGradient = LinearGradient(colors: [.yellow], startPoint: .top, endPoint: .bottom), error: LinearGradient = LinearGradient(colors: [.red], startPoint: .top, endPoint: .bottom))
38 | * cornerRadius(_ radius: CGFloat = 0)
39 | * dropShadow(color: Color = .clear, radius: CGFloat = 5, x: CGFloat = 0, y: CGFloat = 5)
40 |
41 |
42 |
43 |
44 | ```Swift
45 |
46 | import SwiftUI
47 | import SwiftUI_NotificationBanner
48 |
49 | struct RootView: View {
50 | @EnvironmentObject var notificationHandler: DYNotificationHandler
51 | @Environment(\.colorScheme) var colorScheme
52 |
53 | var body: some View {
54 | // your root view content
55 | .notificationView(handler: notificationHandler, notificationBanner: {notification in
56 |
57 | DYNotificationBanner(notification: notification, frameWidth: min(450, UIScreen.main.bounds.size.width))
58 | .text(color: notification.type == .info || notification.type == .error ? .white : .primary)
59 | .image(color: notification.type == .info || notification.type == .error ? .white : .primary)
60 | //change the default colors to create an actual gradient background:
61 | // .backgroundGradientForNotificationType(success: LinearGradient(colors: [.green.opacity(0.4), .green], startPoint: .leading, endPoint: .trailing), error: LinearGradient(colors: [.red, .red.opacity(0.3)], startPoint: .top, endPoint: .bottom))
62 | .dropShadow(color: self.colorScheme == .light ? .gray.opacity(0.4) : .clear, radius: 5, x: 0, y: notification.displayEdge == .top ? 5 : -5)
63 | .cornerRadius(self.cornerRadius)
64 |
65 | })
66 |
67 | }
68 |
69 |
70 |
71 | var cornerRadius: CGFloat {
72 | guard UIDevice.current.userInterfaceIdiom == .phone else {
73 | return 10
74 | }
75 | return UIScreen.main.bounds.width < UIScreen.main.bounds.height ? 0 : 10
76 | }
77 | }
78 |
79 |
80 |
81 | ```
82 |
83 | How to launch a notification banner?
84 | Pass the notification handler (DYNotificationHandler) as EnvironmentObject to your sub-views.
85 | Wherever a notification should be displayed, call the notification handler's show function:
86 |
87 | ```Swift
88 |
89 | // The macOS DYNotification initialiser does not contain "haptic feedback type" since it is part of UIKit.
90 |
91 | notificationHandler.show(notification: DYNotification(title: "Warm reminder",
92 | message: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
93 | image: Image(systemName: "checkmark.seal.fill"),
94 | type: .info, displayDuration: 3,
95 | dismissOnTap: true,
96 | hapticFeedbackType: .info,
97 | tapHandler: {
98 | print("info banner tapped")
99 | }))
100 |
101 | ```
102 |
103 | The title and image parameters are optionals - which means that you can create a simple status bar notification banner without a title or image.
104 |
105 | Check out the included example project for details.
106 |
107 |
108 |
109 | ### Advanced Implementation: the Sheet-Problem
110 |
111 | But wait a minute, you might object: The gif above shows that the notification banner is displayed above a sheet, however, that is impossible because SwiftUI sheets as well fullScreenCovers are always presented as topmost views! So is this some kind of black magic?
112 |
113 | The sad truth is that with just one app window, a sheet and full screen cover will indeed always be shown on top, so unfortunately, any notification banner will be covered without mercy...
114 |
115 | However, there is a clever solution - we just create another app window above the main app window and attach the notificationBanner to the root view of that second window. You can find the details of that solution [here](https://www.fivestars.blog/articles/swiftui-windows/). Thanks to Federico Zanetello for this great article!
116 |
117 | In the example project that is included in this package, I have adapted this solution to displaying notification banners - simply open the example project and copy the necessary code lines into your project.
118 |
119 | This solution only works for iOS / iPadOS, not under macOS.
120 |
121 |
122 | ## Change log
123 |
124 | #### [Version 0.3.1](https://github.com/DominikButz/SwiftUI_NotificationBanner/releases/tag/0.3.1)
125 | Bug fixes: 1) Setup with passthrough window does not block tap gesture detection any more on the banner view. For this to work, you need to set the notification handler variable of the passthrough window in your Scene delegate (see example code). 2) If there is more than one banner in the queue, tap-dimissing the current banner will now display the following banner without waiting for the display duration of the current banner to elapse.
126 |
127 | #### [Version 0.3](https://github.com/DominikButz/SwiftUI_NotificationBanner/releases/tag/0.3)
128 | The package can be used under macOS now.
129 |
130 | #### [Version 0.2](https://github.com/DominikButz/SwiftUI_NotificationBanner/releases/tag/0.2)
131 | Renamed the modifier to notificationView and the default banner view to DYNotificationBanner. Updated cornerRadius logic for leading and trailing display edge.
132 |
133 | #### [Version 0.1.1](https://github.com/DominikButz/SwiftUI_NotificationBanner/releases/tag/0.1.1)
134 | Updated dropShadow modifier function and added documentation.
135 |
136 | #### [Version 0.1](https://github.com/DominikButz/SwiftUI_NotificationBanner/releases/tag/0.1)
137 | Initial public release.
138 |
139 | ## Author
140 |
141 | dominikbutz@gmail.com
142 |
143 | ## License
144 |
145 | SwiftUI NotificationBanner is available under the MIT license. See the LICENSE file for more info.
146 |
147 |
--------------------------------------------------------------------------------
/Sources/SwiftUI_NotificationBanner/View/DYNotificationBanner.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by Dominik Butz on 10/11/2022.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | public struct DYNotificationBanner: View {
12 |
13 | // @Environment(\.colorScheme) var colorScheme
14 | var notification: DYNotification
15 | var settings: DYNotificationBannerSettings = DYNotificationBannerSettings()
16 | let frameWidth: CGFloat
17 |
18 | #if os(iOS)
19 | let topSafeArea: CGFloat = UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0
20 | let bottomSafeArea: CGFloat = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0
21 | #else
22 | let topSafeArea: CGFloat = NSApplication.shared.windows.first?.contentView?.safeAreaInsets.top ?? 0
23 | let bottomSafeArea: CGFloat = NSApplication.shared.windows.first?.contentView?.safeAreaInsets.bottom ?? 0
24 | #endif
25 |
26 | /// intialiser of DYNotificationBanner
27 | /// - Parameters:
28 | /// - notification: the current notification to be displayed as banner
29 | /// - frameWidth: width of the banner frame
30 | public init(notification: DYNotification, frameWidth: CGFloat) {
31 |
32 | self.notification = notification
33 | self.frameWidth = frameWidth
34 | }
35 |
36 | public var body: some View {
37 |
38 | HStack(spacing: 5) {
39 |
40 | Spacer()
41 |
42 | if let image = notification.image {
43 | image.renderingMode(settings.imageRenderingMode).resizable().aspectRatio(contentMode: settings.imageContentMode).frame(maxWidth: settings.imageMaxWidth, maxHeight: settings.imageMaxHeight).foregroundColor(settings.imageColor).padding(.horizontal, 10)
44 |
45 | }
46 | VStack(alignment: .leading, spacing:5) {
47 | if let title = notification.title {
48 | Text(title).font(settings.titleFont)
49 | }
50 | Text(notification.message).font(settings.messageFont).fixedSize(horizontal: false, vertical: true)
51 | }.foregroundColor(settings.textColor)
52 |
53 | Spacer()
54 |
55 | }.padding(.top, notification.displayEdge == .top ? topSafeArea : 5)
56 | .padding(.bottom, notification.displayEdge == .bottom ? bottomSafeArea : 5)
57 | .padding(8)
58 | .frame(width: frameWidth)
59 | .background(settings.gradientFor(notificationType: notification.type))
60 | .clipShape(backgroundShape)
61 | .shadow(color: settings.dropShadow?.color ?? .clear, radius: settings.dropShadow?.radius ?? 0, x: settings.dropShadow?.x ?? 0, y: settings.dropShadow?.y ?? 0)
62 |
63 | }
64 |
65 | var backgroundShape: some Shape {
66 |
67 | let topCorner: CGFloat = notification.displayEdge != .top ? settings.cornerRadius : 0
68 | let bottomCorner: CGFloat = notification.displayEdge != .bottom ? settings.cornerRadius : 0
69 |
70 | return RoundedCornerRectangle(tl: topCorner, tr: topCorner, bl: bottomCorner, br: bottomCorner)
71 | }
72 |
73 |
74 |
75 | }
76 |
77 | public extension View where Self == DYNotificationBanner {
78 |
79 | /// banner text modifier function
80 | /// - Parameters:
81 | /// - titleFont: font of the title label
82 | /// - messageFont: font of the message body label
83 | /// - color: text color of title and message labels
84 | /// - Returns: modified DYNotificationBanner
85 | func text(titleFont: Font = .headline, messageFont: Font = .body, color: Color = .primary)->DYNotificationBanner {
86 |
87 | var modView = self
88 | modView.settings.titleFont = titleFont
89 | modView.settings.messageFont = messageFont
90 | modView.settings.textColor = color
91 | return modView
92 |
93 | }
94 |
95 | /// banner image modifier function
96 | /// - Parameters:
97 | /// - maxHeight: max frame height of the image
98 | /// - maxWidth: max frame width of the image
99 | /// - contentMode: image content mode
100 | /// - renderingMode: image rendering mode - original or template
101 | /// - color: foreground color of the image (if rendering mode is template)
102 | /// - Returns: modified DYNotificationBanner
103 | func image(maxHeight: CGFloat? = 40, maxWidth: CGFloat? = 40, contentMode: ContentMode = .fit, renderingMode: Image.TemplateRenderingMode = .template, color: Color? = .primary)->DYNotificationBanner {
104 | var modView = self
105 | modView.settings.imageMaxWidth = maxWidth
106 | modView.settings.imageMaxHeight = maxHeight
107 | modView.settings.imageContentMode = contentMode
108 | modView.settings.imageRenderingMode = renderingMode
109 | modView.settings.imageColor = color
110 | return modView
111 | }
112 |
113 | /// banner background color gradient modifier function
114 | /// - Parameters:
115 | /// - info: gradient of an info type banner
116 | /// - success: gradient of a success type banner
117 | /// - warning: gradient of ar warning type banner
118 | /// - error: gradient of an error type banner
119 | /// - Returns: modified DYNotificationBanner
120 | func backgroundGradientForNotificationType(info: LinearGradient = LinearGradient(colors: [.blue], startPoint: .top, endPoint: .bottom), success: LinearGradient = LinearGradient(colors: [.green], startPoint: .top, endPoint: .bottom), warning: LinearGradient = LinearGradient(colors: [.yellow], startPoint: .top, endPoint: .bottom), error: LinearGradient = LinearGradient(colors: [.red], startPoint: .top, endPoint: .bottom))-> DYNotificationBanner {
121 |
122 | var modView = self
123 | modView.settings.infoBannerBackgroundGradient = info
124 | modView.settings.successBannerBackgroundGradient = success
125 | modView.settings.warningBannerBackgroundGradient = warning
126 | modView.settings.errorBannerBackgroundGradient = error
127 | return modView
128 |
129 | }
130 |
131 | /// corner radius of the banner background
132 | /// - Parameter radius: corner radius - applies to the bottom corners if displayEdge is top, otherwise radius is applied to the two top corners.
133 | /// - Returns: modified DYNotificationBanner
134 | func cornerRadius(_ radius: CGFloat = 0)->DYNotificationBanner {
135 | var modView = self
136 | modView.settings.cornerRadius = radius
137 | return modView
138 | }
139 |
140 |
141 | /// drop shadow of the banner background
142 | /// - Parameters:
143 | /// - color: shadow color. Default is clear (= no shadow)
144 | /// - radius: radius of the shadow
145 | /// - x: x offset
146 | /// - y: y offset
147 | /// - Returns: modified DYNotificationBanner
148 | func dropShadow(color: Color = .clear, radius: CGFloat = 5, x: CGFloat = 0, y: CGFloat = 5)->DYNotificationBanner {
149 | var shadow: Shadow?
150 | if color != .clear {
151 | shadow = Shadow(color: color, radius: radius, x: x, y: y)
152 | }
153 | var modView = self
154 | modView.settings.dropShadow = shadow
155 | return modView
156 | }
157 |
158 |
159 |
160 | }
161 |
162 | struct DYNotificationBanner_Previews: PreviewProvider {
163 | static var previews: some View {
164 | GeometryReader { proxy in
165 | VStack {
166 |
167 | DYNotificationBanner(notification: DYNotification(title: "Cool Lorem Ipsum", message: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", image: Image(systemName: "checkmark.circle"), displayEdge: .top), frameWidth: min(450, proxy.size.width))
168 |
169 | }
170 | .edgesIgnoringSafeArea(.all)
171 | }
172 | }
173 | }
174 |
175 | #if canImport(AppKit)
176 | extension NSWindow {
177 | var titlebarHeight: CGFloat {
178 | frame.height - contentRect(forFrameRect: frame).height
179 | }
180 | }
181 | #endif
182 |
183 |
184 |
--------------------------------------------------------------------------------
/SwiftUI_NotificationBanner_Example/SwiftUI_NotificationBanner_Example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | B479B7D0291DFB6F00C55DCC /* SwiftUI_NotificationBanner_ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B479B7CF291DFB6F00C55DCC /* SwiftUI_NotificationBanner_ExampleApp.swift */; };
11 | B479B7D2291DFB6F00C55DCC /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B479B7D1291DFB6F00C55DCC /* RootView.swift */; };
12 | B479B7D4291DFB7200C55DCC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B479B7D3291DFB7200C55DCC /* Assets.xcassets */; };
13 | B479B7D7291DFB7200C55DCC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B479B7D6291DFB7200C55DCC /* Preview Assets.xcassets */; };
14 | B479B7E1291DFB9E00C55DCC /* SwiftUI_NotificationBanner in Frameworks */ = {isa = PBXBuildFile; productRef = B479B7E0291DFB9E00C55DCC /* SwiftUI_NotificationBanner */; };
15 | B479B7E6292224FA00C55DCC /* NotificationRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B479B7E5292224FA00C55DCC /* NotificationRootView.swift */; };
16 | B479B7E829223B7500C55DCC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B479B7E729223B7500C55DCC /* AppDelegate.swift */; };
17 | B479B7EA29223BB200C55DCC /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B479B7E929223BB200C55DCC /* SceneDelegate.swift */; };
18 | B4BDB1482A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4BDB1472A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOSApp.swift */; };
19 | B4BDB14A2A541B4200558A3E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4BDB1492A541B4200558A3E /* ContentView.swift */; };
20 | B4BDB14C2A541B4400558A3E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B4BDB14B2A541B4400558A3E /* Assets.xcassets */; };
21 | B4BDB14F2A541B4400558A3E /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B4BDB14E2A541B4400558A3E /* Preview Assets.xcassets */; };
22 | B4BDB1702A5665EC00558A3E /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B479B7D1291DFB6F00C55DCC /* RootView.swift */; };
23 | B4BDB1742A56672100558A3E /* SwiftUI_NotificationBanner in Frameworks */ = {isa = PBXBuildFile; productRef = B4BDB1732A56672100558A3E /* SwiftUI_NotificationBanner */; };
24 | /* End PBXBuildFile section */
25 |
26 | /* Begin PBXFileReference section */
27 | B479B7CC291DFB6F00C55DCC /* SwiftUI_NotificationBanner_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUI_NotificationBanner_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
28 | B479B7CF291DFB6F00C55DCC /* SwiftUI_NotificationBanner_ExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUI_NotificationBanner_ExampleApp.swift; sourceTree = ""; };
29 | B479B7D1291DFB6F00C55DCC /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; };
30 | B479B7D3291DFB7200C55DCC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
31 | B479B7D6291DFB7200C55DCC /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
32 | B479B7DE291DFB9200C55DCC /* SwiftUI_NotificationBanner */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SwiftUI_NotificationBanner; path = ..; sourceTree = ""; };
33 | B479B7E5292224FA00C55DCC /* NotificationRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRootView.swift; sourceTree = ""; };
34 | B479B7E729223B7500C55DCC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
35 | B479B7E929223BB200C55DCC /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
36 | B4BDB1452A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUI_NotificationBanner_Example_macOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
37 | B4BDB1472A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUI_NotificationBanner_Example_macOSApp.swift; sourceTree = ""; };
38 | B4BDB1492A541B4200558A3E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
39 | B4BDB14B2A541B4400558A3E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
40 | B4BDB14E2A541B4400558A3E /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
41 | B4BDB1502A541B4400558A3E /* SwiftUI_NotificationBanner_Example_macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SwiftUI_NotificationBanner_Example_macOS.entitlements; sourceTree = ""; };
42 | B4BDB1592A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUI_NotificationBanner_Example_macOSTests.swift; sourceTree = ""; };
43 | B4BDB1632A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUI_NotificationBanner_Example_macOSUITests.swift; sourceTree = ""; };
44 | B4BDB1652A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUI_NotificationBanner_Example_macOSUITestsLaunchTests.swift; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | B479B7C9291DFB6F00C55DCC /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | B479B7E1291DFB9E00C55DCC /* SwiftUI_NotificationBanner in Frameworks */,
53 | );
54 | runOnlyForDeploymentPostprocessing = 0;
55 | };
56 | B4BDB1422A541B4200558A3E /* Frameworks */ = {
57 | isa = PBXFrameworksBuildPhase;
58 | buildActionMask = 2147483647;
59 | files = (
60 | B4BDB1742A56672100558A3E /* SwiftUI_NotificationBanner in Frameworks */,
61 | );
62 | runOnlyForDeploymentPostprocessing = 0;
63 | };
64 | /* End PBXFrameworksBuildPhase section */
65 |
66 | /* Begin PBXGroup section */
67 | B479B7C3291DFB6F00C55DCC = {
68 | isa = PBXGroup;
69 | children = (
70 | B479B7DD291DFB9200C55DCC /* Packages */,
71 | B479B7CE291DFB6F00C55DCC /* SwiftUI_NotificationBanner_Example */,
72 | B4BDB1462A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOS */,
73 | B4BDB1582A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSTests */,
74 | B4BDB1622A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSUITests */,
75 | B479B7CD291DFB6F00C55DCC /* Products */,
76 | B479B7DF291DFB9E00C55DCC /* Frameworks */,
77 | );
78 | sourceTree = "";
79 | };
80 | B479B7CD291DFB6F00C55DCC /* Products */ = {
81 | isa = PBXGroup;
82 | children = (
83 | B479B7CC291DFB6F00C55DCC /* SwiftUI_NotificationBanner_Example.app */,
84 | B4BDB1452A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOS.app */,
85 | );
86 | name = Products;
87 | sourceTree = "";
88 | };
89 | B479B7CE291DFB6F00C55DCC /* SwiftUI_NotificationBanner_Example */ = {
90 | isa = PBXGroup;
91 | children = (
92 | B479B7CF291DFB6F00C55DCC /* SwiftUI_NotificationBanner_ExampleApp.swift */,
93 | B479B7E729223B7500C55DCC /* AppDelegate.swift */,
94 | B479B7E929223BB200C55DCC /* SceneDelegate.swift */,
95 | B479B7E5292224FA00C55DCC /* NotificationRootView.swift */,
96 | B479B7D1291DFB6F00C55DCC /* RootView.swift */,
97 | B479B7D3291DFB7200C55DCC /* Assets.xcassets */,
98 | B479B7D5291DFB7200C55DCC /* Preview Content */,
99 | );
100 | path = SwiftUI_NotificationBanner_Example;
101 | sourceTree = "";
102 | };
103 | B479B7D5291DFB7200C55DCC /* Preview Content */ = {
104 | isa = PBXGroup;
105 | children = (
106 | B479B7D6291DFB7200C55DCC /* Preview Assets.xcassets */,
107 | );
108 | path = "Preview Content";
109 | sourceTree = "";
110 | };
111 | B479B7DD291DFB9200C55DCC /* Packages */ = {
112 | isa = PBXGroup;
113 | children = (
114 | B479B7DE291DFB9200C55DCC /* SwiftUI_NotificationBanner */,
115 | );
116 | name = Packages;
117 | sourceTree = "";
118 | };
119 | B479B7DF291DFB9E00C55DCC /* Frameworks */ = {
120 | isa = PBXGroup;
121 | children = (
122 | );
123 | name = Frameworks;
124 | sourceTree = "";
125 | };
126 | B4BDB1462A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOS */ = {
127 | isa = PBXGroup;
128 | children = (
129 | B4BDB1472A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOSApp.swift */,
130 | B4BDB1492A541B4200558A3E /* ContentView.swift */,
131 | B4BDB14B2A541B4400558A3E /* Assets.xcassets */,
132 | B4BDB1502A541B4400558A3E /* SwiftUI_NotificationBanner_Example_macOS.entitlements */,
133 | B4BDB14D2A541B4400558A3E /* Preview Content */,
134 | );
135 | path = SwiftUI_NotificationBanner_Example_macOS;
136 | sourceTree = "";
137 | };
138 | B4BDB14D2A541B4400558A3E /* Preview Content */ = {
139 | isa = PBXGroup;
140 | children = (
141 | B4BDB14E2A541B4400558A3E /* Preview Assets.xcassets */,
142 | );
143 | path = "Preview Content";
144 | sourceTree = "";
145 | };
146 | B4BDB1582A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSTests */ = {
147 | isa = PBXGroup;
148 | children = (
149 | B4BDB1592A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSTests.swift */,
150 | );
151 | path = SwiftUI_NotificationBanner_Example_macOSTests;
152 | sourceTree = "";
153 | };
154 | B4BDB1622A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSUITests */ = {
155 | isa = PBXGroup;
156 | children = (
157 | B4BDB1632A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSUITests.swift */,
158 | B4BDB1652A541B4500558A3E /* SwiftUI_NotificationBanner_Example_macOSUITestsLaunchTests.swift */,
159 | );
160 | path = SwiftUI_NotificationBanner_Example_macOSUITests;
161 | sourceTree = "";
162 | };
163 | /* End PBXGroup section */
164 |
165 | /* Begin PBXNativeTarget section */
166 | B479B7CB291DFB6F00C55DCC /* SwiftUI_NotificationBanner_Example */ = {
167 | isa = PBXNativeTarget;
168 | buildConfigurationList = B479B7DA291DFB7200C55DCC /* Build configuration list for PBXNativeTarget "SwiftUI_NotificationBanner_Example" */;
169 | buildPhases = (
170 | B479B7C8291DFB6F00C55DCC /* Sources */,
171 | B479B7C9291DFB6F00C55DCC /* Frameworks */,
172 | B479B7CA291DFB6F00C55DCC /* Resources */,
173 | );
174 | buildRules = (
175 | );
176 | dependencies = (
177 | );
178 | name = SwiftUI_NotificationBanner_Example;
179 | packageProductDependencies = (
180 | B479B7E0291DFB9E00C55DCC /* SwiftUI_NotificationBanner */,
181 | );
182 | productName = SwiftUI_NotificationBanner_Example;
183 | productReference = B479B7CC291DFB6F00C55DCC /* SwiftUI_NotificationBanner_Example.app */;
184 | productType = "com.apple.product-type.application";
185 | };
186 | B4BDB1442A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOS */ = {
187 | isa = PBXNativeTarget;
188 | buildConfigurationList = B4BDB16D2A541B4500558A3E /* Build configuration list for PBXNativeTarget "SwiftUI_NotificationBanner_Example_macOS" */;
189 | buildPhases = (
190 | B4BDB1412A541B4200558A3E /* Sources */,
191 | B4BDB1422A541B4200558A3E /* Frameworks */,
192 | B4BDB1432A541B4200558A3E /* Resources */,
193 | );
194 | buildRules = (
195 | );
196 | dependencies = (
197 | );
198 | name = SwiftUI_NotificationBanner_Example_macOS;
199 | packageProductDependencies = (
200 | B4BDB1732A56672100558A3E /* SwiftUI_NotificationBanner */,
201 | );
202 | productName = SwiftUI_NotificationBanner_Example_macOS;
203 | productReference = B4BDB1452A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOS.app */;
204 | productType = "com.apple.product-type.application";
205 | };
206 | /* End PBXNativeTarget section */
207 |
208 | /* Begin PBXProject section */
209 | B479B7C4291DFB6F00C55DCC /* Project object */ = {
210 | isa = PBXProject;
211 | attributes = {
212 | BuildIndependentTargetsInParallel = 1;
213 | LastSwiftUpdateCheck = 1430;
214 | LastUpgradeCheck = 1400;
215 | TargetAttributes = {
216 | B479B7CB291DFB6F00C55DCC = {
217 | CreatedOnToolsVersion = 14.0.1;
218 | };
219 | B4BDB1442A541B4200558A3E = {
220 | CreatedOnToolsVersion = 14.3;
221 | };
222 | };
223 | };
224 | buildConfigurationList = B479B7C7291DFB6F00C55DCC /* Build configuration list for PBXProject "SwiftUI_NotificationBanner_Example" */;
225 | compatibilityVersion = "Xcode 14.0";
226 | developmentRegion = en;
227 | hasScannedForEncodings = 0;
228 | knownRegions = (
229 | en,
230 | Base,
231 | );
232 | mainGroup = B479B7C3291DFB6F00C55DCC;
233 | productRefGroup = B479B7CD291DFB6F00C55DCC /* Products */;
234 | projectDirPath = "";
235 | projectRoot = "";
236 | targets = (
237 | B479B7CB291DFB6F00C55DCC /* SwiftUI_NotificationBanner_Example */,
238 | B4BDB1442A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOS */,
239 | );
240 | };
241 | /* End PBXProject section */
242 |
243 | /* Begin PBXResourcesBuildPhase section */
244 | B479B7CA291DFB6F00C55DCC /* Resources */ = {
245 | isa = PBXResourcesBuildPhase;
246 | buildActionMask = 2147483647;
247 | files = (
248 | B479B7D7291DFB7200C55DCC /* Preview Assets.xcassets in Resources */,
249 | B479B7D4291DFB7200C55DCC /* Assets.xcassets in Resources */,
250 | );
251 | runOnlyForDeploymentPostprocessing = 0;
252 | };
253 | B4BDB1432A541B4200558A3E /* Resources */ = {
254 | isa = PBXResourcesBuildPhase;
255 | buildActionMask = 2147483647;
256 | files = (
257 | B4BDB14F2A541B4400558A3E /* Preview Assets.xcassets in Resources */,
258 | B4BDB14C2A541B4400558A3E /* Assets.xcassets in Resources */,
259 | );
260 | runOnlyForDeploymentPostprocessing = 0;
261 | };
262 | /* End PBXResourcesBuildPhase section */
263 |
264 | /* Begin PBXSourcesBuildPhase section */
265 | B479B7C8291DFB6F00C55DCC /* Sources */ = {
266 | isa = PBXSourcesBuildPhase;
267 | buildActionMask = 2147483647;
268 | files = (
269 | B479B7EA29223BB200C55DCC /* SceneDelegate.swift in Sources */,
270 | B479B7E829223B7500C55DCC /* AppDelegate.swift in Sources */,
271 | B479B7E6292224FA00C55DCC /* NotificationRootView.swift in Sources */,
272 | B479B7D2291DFB6F00C55DCC /* RootView.swift in Sources */,
273 | B479B7D0291DFB6F00C55DCC /* SwiftUI_NotificationBanner_ExampleApp.swift in Sources */,
274 | );
275 | runOnlyForDeploymentPostprocessing = 0;
276 | };
277 | B4BDB1412A541B4200558A3E /* Sources */ = {
278 | isa = PBXSourcesBuildPhase;
279 | buildActionMask = 2147483647;
280 | files = (
281 | B4BDB14A2A541B4200558A3E /* ContentView.swift in Sources */,
282 | B4BDB1482A541B4200558A3E /* SwiftUI_NotificationBanner_Example_macOSApp.swift in Sources */,
283 | B4BDB1702A5665EC00558A3E /* RootView.swift in Sources */,
284 | );
285 | runOnlyForDeploymentPostprocessing = 0;
286 | };
287 | /* End PBXSourcesBuildPhase section */
288 |
289 | /* Begin XCBuildConfiguration section */
290 | B479B7D8291DFB7200C55DCC /* Debug */ = {
291 | isa = XCBuildConfiguration;
292 | buildSettings = {
293 | ALWAYS_SEARCH_USER_PATHS = NO;
294 | CLANG_ANALYZER_NONNULL = YES;
295 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
297 | CLANG_ENABLE_MODULES = YES;
298 | CLANG_ENABLE_OBJC_ARC = YES;
299 | CLANG_ENABLE_OBJC_WEAK = YES;
300 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
301 | CLANG_WARN_BOOL_CONVERSION = YES;
302 | CLANG_WARN_COMMA = YES;
303 | CLANG_WARN_CONSTANT_CONVERSION = YES;
304 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
305 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
306 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
307 | CLANG_WARN_EMPTY_BODY = YES;
308 | CLANG_WARN_ENUM_CONVERSION = YES;
309 | CLANG_WARN_INFINITE_RECURSION = YES;
310 | CLANG_WARN_INT_CONVERSION = YES;
311 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
312 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
313 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
314 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
315 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
316 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
317 | CLANG_WARN_STRICT_PROTOTYPES = YES;
318 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
319 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
320 | CLANG_WARN_UNREACHABLE_CODE = YES;
321 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
322 | COPY_PHASE_STRIP = NO;
323 | DEBUG_INFORMATION_FORMAT = dwarf;
324 | ENABLE_STRICT_OBJC_MSGSEND = YES;
325 | ENABLE_TESTABILITY = YES;
326 | GCC_C_LANGUAGE_STANDARD = gnu11;
327 | GCC_DYNAMIC_NO_PIC = NO;
328 | GCC_NO_COMMON_BLOCKS = YES;
329 | GCC_OPTIMIZATION_LEVEL = 0;
330 | GCC_PREPROCESSOR_DEFINITIONS = (
331 | "DEBUG=1",
332 | "$(inherited)",
333 | );
334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
336 | GCC_WARN_UNDECLARED_SELECTOR = YES;
337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
338 | GCC_WARN_UNUSED_FUNCTION = YES;
339 | GCC_WARN_UNUSED_VARIABLE = YES;
340 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
341 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
342 | MTL_FAST_MATH = YES;
343 | ONLY_ACTIVE_ARCH = YES;
344 | SDKROOT = iphoneos;
345 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
346 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
347 | };
348 | name = Debug;
349 | };
350 | B479B7D9291DFB7200C55DCC /* Release */ = {
351 | isa = XCBuildConfiguration;
352 | buildSettings = {
353 | ALWAYS_SEARCH_USER_PATHS = NO;
354 | CLANG_ANALYZER_NONNULL = YES;
355 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
356 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
357 | CLANG_ENABLE_MODULES = YES;
358 | CLANG_ENABLE_OBJC_ARC = YES;
359 | CLANG_ENABLE_OBJC_WEAK = YES;
360 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
361 | CLANG_WARN_BOOL_CONVERSION = YES;
362 | CLANG_WARN_COMMA = YES;
363 | CLANG_WARN_CONSTANT_CONVERSION = YES;
364 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
365 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
366 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
367 | CLANG_WARN_EMPTY_BODY = YES;
368 | CLANG_WARN_ENUM_CONVERSION = YES;
369 | CLANG_WARN_INFINITE_RECURSION = YES;
370 | CLANG_WARN_INT_CONVERSION = YES;
371 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
372 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
373 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
375 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
376 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
377 | CLANG_WARN_STRICT_PROTOTYPES = YES;
378 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
379 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
380 | CLANG_WARN_UNREACHABLE_CODE = YES;
381 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
382 | COPY_PHASE_STRIP = NO;
383 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
384 | ENABLE_NS_ASSERTIONS = NO;
385 | ENABLE_STRICT_OBJC_MSGSEND = YES;
386 | GCC_C_LANGUAGE_STANDARD = gnu11;
387 | GCC_NO_COMMON_BLOCKS = YES;
388 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
389 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
390 | GCC_WARN_UNDECLARED_SELECTOR = YES;
391 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
392 | GCC_WARN_UNUSED_FUNCTION = YES;
393 | GCC_WARN_UNUSED_VARIABLE = YES;
394 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
395 | MTL_ENABLE_DEBUG_INFO = NO;
396 | MTL_FAST_MATH = YES;
397 | SDKROOT = iphoneos;
398 | SWIFT_COMPILATION_MODE = wholemodule;
399 | SWIFT_OPTIMIZATION_LEVEL = "-O";
400 | VALIDATE_PRODUCT = YES;
401 | };
402 | name = Release;
403 | };
404 | B479B7DB291DFB7200C55DCC /* Debug */ = {
405 | isa = XCBuildConfiguration;
406 | buildSettings = {
407 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
408 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
409 | CODE_SIGN_STYLE = Automatic;
410 | CURRENT_PROJECT_VERSION = 1;
411 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUI_NotificationBanner_Example/Preview Content\"";
412 | DEVELOPMENT_TEAM = 76FXG652LY;
413 | ENABLE_PREVIEWS = YES;
414 | GENERATE_INFOPLIST_FILE = YES;
415 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
416 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
417 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
418 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
419 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
420 | LD_RUNPATH_SEARCH_PATHS = (
421 | "$(inherited)",
422 | "@executable_path/Frameworks",
423 | );
424 | MARKETING_VERSION = 1.0;
425 | PRODUCT_BUNDLE_IDENTIFIER = "me.duoyun.SwiftUI-NotificationBanner-Example";
426 | PRODUCT_NAME = "$(TARGET_NAME)";
427 | SWIFT_EMIT_LOC_STRINGS = YES;
428 | SWIFT_VERSION = 5.0;
429 | TARGETED_DEVICE_FAMILY = "1,2";
430 | };
431 | name = Debug;
432 | };
433 | B479B7DC291DFB7200C55DCC /* Release */ = {
434 | isa = XCBuildConfiguration;
435 | buildSettings = {
436 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
437 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
438 | CODE_SIGN_STYLE = Automatic;
439 | CURRENT_PROJECT_VERSION = 1;
440 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUI_NotificationBanner_Example/Preview Content\"";
441 | DEVELOPMENT_TEAM = 76FXG652LY;
442 | ENABLE_PREVIEWS = YES;
443 | GENERATE_INFOPLIST_FILE = YES;
444 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
445 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
446 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
447 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
448 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
449 | LD_RUNPATH_SEARCH_PATHS = (
450 | "$(inherited)",
451 | "@executable_path/Frameworks",
452 | );
453 | MARKETING_VERSION = 1.0;
454 | PRODUCT_BUNDLE_IDENTIFIER = "me.duoyun.SwiftUI-NotificationBanner-Example";
455 | PRODUCT_NAME = "$(TARGET_NAME)";
456 | SWIFT_EMIT_LOC_STRINGS = YES;
457 | SWIFT_VERSION = 5.0;
458 | TARGETED_DEVICE_FAMILY = "1,2";
459 | };
460 | name = Release;
461 | };
462 | B4BDB1672A541B4500558A3E /* Debug */ = {
463 | isa = XCBuildConfiguration;
464 | buildSettings = {
465 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
466 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
467 | CODE_SIGN_ENTITLEMENTS = SwiftUI_NotificationBanner_Example_macOS/SwiftUI_NotificationBanner_Example_macOS.entitlements;
468 | CODE_SIGN_STYLE = Automatic;
469 | COMBINE_HIDPI_IMAGES = YES;
470 | CURRENT_PROJECT_VERSION = 1;
471 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUI_NotificationBanner_Example_macOS/Preview Content\"";
472 | DEVELOPMENT_TEAM = 76FXG652LY;
473 | ENABLE_HARDENED_RUNTIME = YES;
474 | ENABLE_PREVIEWS = YES;
475 | GENERATE_INFOPLIST_FILE = YES;
476 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
477 | LD_RUNPATH_SEARCH_PATHS = (
478 | "$(inherited)",
479 | "@executable_path/../Frameworks",
480 | );
481 | MACOSX_DEPLOYMENT_TARGET = 13.2;
482 | MARKETING_VERSION = 1.0;
483 | PRODUCT_BUNDLE_IDENTIFIER = "me.duoyun.SwiftUI-NotificationBanner-Example-macOS";
484 | PRODUCT_NAME = "$(TARGET_NAME)";
485 | SDKROOT = macosx;
486 | SWIFT_EMIT_LOC_STRINGS = YES;
487 | SWIFT_VERSION = 5.0;
488 | };
489 | name = Debug;
490 | };
491 | B4BDB1682A541B4500558A3E /* Release */ = {
492 | isa = XCBuildConfiguration;
493 | buildSettings = {
494 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
495 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
496 | CODE_SIGN_ENTITLEMENTS = SwiftUI_NotificationBanner_Example_macOS/SwiftUI_NotificationBanner_Example_macOS.entitlements;
497 | CODE_SIGN_STYLE = Automatic;
498 | COMBINE_HIDPI_IMAGES = YES;
499 | CURRENT_PROJECT_VERSION = 1;
500 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUI_NotificationBanner_Example_macOS/Preview Content\"";
501 | DEVELOPMENT_TEAM = 76FXG652LY;
502 | ENABLE_HARDENED_RUNTIME = YES;
503 | ENABLE_PREVIEWS = YES;
504 | GENERATE_INFOPLIST_FILE = YES;
505 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
506 | LD_RUNPATH_SEARCH_PATHS = (
507 | "$(inherited)",
508 | "@executable_path/../Frameworks",
509 | );
510 | MACOSX_DEPLOYMENT_TARGET = 13.2;
511 | MARKETING_VERSION = 1.0;
512 | PRODUCT_BUNDLE_IDENTIFIER = "me.duoyun.SwiftUI-NotificationBanner-Example-macOS";
513 | PRODUCT_NAME = "$(TARGET_NAME)";
514 | SDKROOT = macosx;
515 | SWIFT_EMIT_LOC_STRINGS = YES;
516 | SWIFT_VERSION = 5.0;
517 | };
518 | name = Release;
519 | };
520 | /* End XCBuildConfiguration section */
521 |
522 | /* Begin XCConfigurationList section */
523 | B479B7C7291DFB6F00C55DCC /* Build configuration list for PBXProject "SwiftUI_NotificationBanner_Example" */ = {
524 | isa = XCConfigurationList;
525 | buildConfigurations = (
526 | B479B7D8291DFB7200C55DCC /* Debug */,
527 | B479B7D9291DFB7200C55DCC /* Release */,
528 | );
529 | defaultConfigurationIsVisible = 0;
530 | defaultConfigurationName = Release;
531 | };
532 | B479B7DA291DFB7200C55DCC /* Build configuration list for PBXNativeTarget "SwiftUI_NotificationBanner_Example" */ = {
533 | isa = XCConfigurationList;
534 | buildConfigurations = (
535 | B479B7DB291DFB7200C55DCC /* Debug */,
536 | B479B7DC291DFB7200C55DCC /* Release */,
537 | );
538 | defaultConfigurationIsVisible = 0;
539 | defaultConfigurationName = Release;
540 | };
541 | B4BDB16D2A541B4500558A3E /* Build configuration list for PBXNativeTarget "SwiftUI_NotificationBanner_Example_macOS" */ = {
542 | isa = XCConfigurationList;
543 | buildConfigurations = (
544 | B4BDB1672A541B4500558A3E /* Debug */,
545 | B4BDB1682A541B4500558A3E /* Release */,
546 | );
547 | defaultConfigurationIsVisible = 0;
548 | defaultConfigurationName = Release;
549 | };
550 | /* End XCConfigurationList section */
551 |
552 | /* Begin XCSwiftPackageProductDependency section */
553 | B479B7E0291DFB9E00C55DCC /* SwiftUI_NotificationBanner */ = {
554 | isa = XCSwiftPackageProductDependency;
555 | productName = SwiftUI_NotificationBanner;
556 | };
557 | B4BDB1732A56672100558A3E /* SwiftUI_NotificationBanner */ = {
558 | isa = XCSwiftPackageProductDependency;
559 | productName = SwiftUI_NotificationBanner;
560 | };
561 | /* End XCSwiftPackageProductDependency section */
562 | };
563 | rootObject = B479B7C4291DFB6F00C55DCC /* Project object */;
564 | }
565 |
--------------------------------------------------------------------------------