diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08f50bbb..1fa85286 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,88 @@
# Change Log
All notable changes to this project will be documented in this file.
+## 10.0.1
+
+## Features
+
+* #523 Add a `priority` configuration option.
+* #548 Adds 'TopBottomPresentable' protocol to allow animators implementation to reuse 'top/bottom' integration in presentation
+* #543 Make the `SwiftMessages` initializer `nonisolated` to improve interoperability with dependency injection frameworks like Factory.
+* #560 Add a new `swiftMessage` modifier variation that provides a ` MessageGeometryProxy` type to the message view builder—this works around an inssue with `GeometryReader` not working in `UIHostingController`.
+
+Fixes
+* Fix broken touch handling in iOS 18.
+
+## 10.0.0
+
+### Features
+
+* Add a variation on the `.swiftMessage()` modifier that takes a view builder instead of requiring that the bound value conform to `MessageViewConvertible`. This syntax is more similar to the familiar `sheet()` modifier syntax and provides more flexibility for constructing message views.
+* #207 Add optional haptic feedback
+
+### Changes
+
+* Use `@MainActor` to ensure that SwiftMessages is not called from a background queue.
+* Bump minimum deployment target to iOS 13.
+
+### Fixes
+
+* #535 window being accessed from background thread when dequeueNext is called
+* #534 Xcode warnings in two swift files
+* #533 How do I show a message that appears above the keyboard, when the keyboard is already visible?
+
+## 9.0.9
+
+### Fixes
+
+* Fix hit testing on SwiftUI views to allow touches around the view's margins to pass through to the underlying view.
+* Update `KeyboardTrackingView` to continue tracking the keyboard even when not installed in the view hierarchy.
+
+## 9.0.8
+
+### Changes
+
+* #529 Update readme and SwiftUI demo to demostrate how to mask edges.
+
+## 9.0.7
+
+### Features
+
+* Added support for SwiftUI
+
+### Fixes
+
+* #527 Crash while clicking two times to hide the presenting controller
+* #517 Prevent orphaned views from blocking the queue
+* Prevent orphaned `SwiftMessagesSeque`s from retaining the presenting view controller
+
+## 9.0.6
+
+### Features
+
+* Add `UIView` associated type to `Event`, e.g. `willShow(UIView)` so that event listeners can inspect the view.
+* Add `Event.id: String?` property so that event listeners can reason about the view's ID.
+
+## 9.0.5
+
+### Fixes
+
+* #482 Fix timing of `KeyboardTrackingView` callbacks.
+* #483 KeyboardTrackingView causes a small space under bottom-style view
+
+## 9.0.4
+
+* #471 Xcode 13 issue - Enum cases with associated values cannot be marked potentially unavailable with '@available'
+* Improve colors for dark mode.
+
+## 9.0.3
+
+### Fixes
+
+* #467 Lower or equal level window's views disappear upon hide
+* #466 Alert not shown after Biometry check
+* #465 Fix broken Carthage build. The Carthage build was broken due to the `iMessageDemo` project's use of CocoaPods and the automatically generated `SwiftMessages` framework scheme created by CocoaPods. The podfile was modified to delete this scheme, but Carthage users may need to run `pod install` on the `iMessagesDemo` project, if they have CocoaPods installed, or manually delete the `iMessageDemo/Pods/Pods.xcodeproj/xcuserdata` folder.
+
## 9.0.2
### Fixes
diff --git a/Demo/Demo.xcodeproj/project.pbxproj b/Demo/Demo.xcodeproj/project.pbxproj
index e7d3b1e6..57a9b7a5 100644
--- a/Demo/Demo.xcodeproj/project.pbxproj
+++ b/Demo/Demo.xcodeproj/project.pbxproj
@@ -200,7 +200,6 @@
TargetAttributes = {
86AEDCE11D5D1DB70030232E = {
CreatedOnToolsVersion = 7.3.1;
- DevelopmentTeam = 38R82CD868;
LastSwiftMigration = 1020;
};
};
@@ -427,11 +426,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
- DEVELOPMENT_TEAM = 38R82CD868;
+ DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Demo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.Demo;
+ PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.SwiftMessages.Demo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -444,11 +443,11 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
- DEVELOPMENT_TEAM = 38R82CD868;
+ DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = Demo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.Demo;
+ PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.SwiftMessages.Demo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
diff --git a/Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme b/Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme
index 847d6292..db82f1fa 100644
--- a/Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme
+++ b/Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme
@@ -50,13 +50,6 @@
ReferencedContainer = "container:Demo.xcodeproj">
-
-
-
-
-
-
-
-
+
+
-
-
+
@@ -22,14 +19,14 @@
-
+
-
+
diff --git a/Demo/Demo/Base.lproj/Main.storyboard b/Demo/Demo/Base.lproj/Main.storyboard
index 08ab75d2..c38d6707 100644
--- a/Demo/Demo/Base.lproj/Main.storyboard
+++ b/Demo/Demo/Base.lproj/Main.storyboard
@@ -1,8 +1,10 @@
-
+
-
+
+
+
@@ -30,10 +32,10 @@
-
+
-
+
@@ -47,7 +49,7 @@
-
+
@@ -67,21 +69,21 @@
-
+
-
+
-
+
-
+
-
+
@@ -102,21 +104,21 @@
-
+
-
+
-
+
-
+
-
+
@@ -137,21 +139,21 @@
-
+
-
+
-
+
-
+
-
+
@@ -171,21 +173,21 @@
-
+
-
+
-
+
-
+
-
+
@@ -223,25 +225,25 @@
-
+
-
+
-
+
-
+
-
+
@@ -260,21 +262,21 @@
-
-
+
+
-
+
-
+
-
+
@@ -293,11 +295,11 @@
-
-
+
+
-
+
@@ -306,7 +308,7 @@
-
+
@@ -326,11 +328,11 @@
-
-
+
+
-
+
@@ -339,7 +341,7 @@
-
+
@@ -360,19 +362,19 @@
-
+
-
+
-
+
-
+
@@ -391,20 +393,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
@@ -427,20 +461,20 @@
-
-
+
+
-
+
-
+
-
+
@@ -460,11 +494,11 @@
-
-
+
+
-
+
@@ -473,7 +507,7 @@
-
+
@@ -494,20 +528,20 @@
-
-
+
+
-
+
-
+
-
+
@@ -527,19 +561,19 @@
-
+
-
+
-
+
-
+
@@ -556,20 +590,20 @@
-
-
+
+
-
+
-
+
-
+
@@ -585,20 +619,20 @@
-
-
+
+
-
+
-
+
-
+
@@ -614,48 +648,48 @@
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -663,32 +697,32 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -780,6 +814,7 @@
+
@@ -810,10 +845,10 @@
-
+
-
+
@@ -827,7 +862,7 @@
-
+
@@ -841,7 +876,7 @@
-
+
@@ -855,7 +890,7 @@
-
+
@@ -869,7 +904,7 @@
-
+
@@ -883,7 +918,7 @@
-
+
@@ -897,7 +932,7 @@
-
+
@@ -913,7 +948,7 @@
-
+
@@ -933,7 +968,7 @@
-
+
@@ -951,19 +986,19 @@
-
+
-
+
-
+
-
+
@@ -976,14 +1011,14 @@
-
+
-
+
-
+
@@ -996,14 +1031,14 @@
-
+
-
+
-
+
@@ -1049,13 +1084,13 @@
-
+
-
+
@@ -1072,7 +1107,7 @@
-
+
@@ -1084,11 +1119,17 @@
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Demo/Demo/ExploreViewController.swift b/Demo/Demo/ExploreViewController.swift
index d39009a2..5d34762a 100644
--- a/Demo/Demo/ExploreViewController.swift
+++ b/Demo/Demo/ExploreViewController.swift
@@ -41,16 +41,16 @@ class ExploreViewController: UITableViewController, UITextFieldDelegate {
switch theme.selectedSegmentIndex {
case 0:
- view.configureTheme(.info, iconStyle: iconStyle)
+ view.configureTheme(.info, iconStyle: iconStyle, includeHaptic: hapticFeedback.isOn)
view.accessibilityPrefix = "info"
case 1:
- view.configureTheme(.success, iconStyle: iconStyle)
+ view.configureTheme(.success, iconStyle: iconStyle, includeHaptic: hapticFeedback.isOn)
view.accessibilityPrefix = "success"
case 2:
- view.configureTheme(.warning, iconStyle: iconStyle)
+ view.configureTheme(.warning, iconStyle: iconStyle, includeHaptic: hapticFeedback.isOn)
view.accessibilityPrefix = "warning"
case 3:
- view.configureTheme(.error, iconStyle: iconStyle)
+ view.configureTheme(.error, iconStyle: iconStyle, includeHaptic: hapticFeedback.isOn)
view.accessibilityPrefix = "error"
default:
let iconText = ["🐸", "🐷", "🐬", "🐠", "🐍", "🐹", "🐼"].randomElement()
@@ -140,7 +140,11 @@ class ExploreViewController: UITableViewController, UITextFieldDelegate {
break
}
}
-
+
+ if view.defaultHaptic == nil && hapticFeedback.isOn {
+ config.haptic = .success
+ }
+
// Show
SwiftMessages.show(config: config, view: view)
}
@@ -154,6 +158,7 @@ class ExploreViewController: UITableViewController, UITextFieldDelegate {
@IBOutlet weak var duration: UISegmentedControl!
@IBOutlet weak var dimMode: UISegmentedControl!
@IBOutlet weak var interactiveHide: UISwitch!
+ @IBOutlet weak var hapticFeedback: UISwitch!
@IBOutlet weak var layout: UISegmentedControl!
@IBOutlet weak var theme: UISegmentedControl!
@IBOutlet weak var iconStyle: UISegmentedControl!
diff --git a/Package.swift b/Package.swift
index 5324d1b7..a906ba1a 100644
--- a/Package.swift
+++ b/Package.swift
@@ -4,10 +4,11 @@ import PackageDescription
let package = Package(
name: "SwiftMessages",
platforms: [
- .iOS("9.0")
+ .iOS("13.0")
],
products: [
- .library(name: "SwiftMessages", targets: ["SwiftMessages"])
+ .library(name: "SwiftMessages", targets: ["SwiftMessages"]),
+ .library(name: "SwiftMessages-Dynamic", type: .dynamic, targets: ["SwiftMessages"])
],
targets: [
.target(
diff --git a/README.md b/README.md
index c46c84cf..abec2f0e 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,9 @@
## Overview
-SwiftMessages is a very flexible view and view controller presentation library for iOS.
+🔥🔥🔥 **NEW** SwiftUI support added!
+
+SwiftMessages is a very flexible view and view controller presentation library for UIKit and SwiftUI.
Message views and view controllers can be displayed at the top, bottom, or center of the screen, or behind navigation bars and tab bars. There are interactive dismiss gestures including a fun, physics-based one. Multiple background dimming modes. And a lot more!
@@ -20,32 +22,12 @@ In addition to the numerous configuration options, SwiftMessages provides severa
* Copy one of the included nib files into your project and change it.
* Subclass `MessageView` and add elements, etc.
-* Or just supply an arbitrary instance of `UIView`.
-
-Try exploring [the demo app via appetize.io](http://goo.gl/KXw4nD) to get a feel for the extensive configurability of SwiftMessages.
+* Or just supply an arbitrary instance of `View` or `UIView`.
-
-
-
-
-## View Controllers
-
-SwiftMessages can present view controllers using the `SwiftMessagesSegue` custom modal segue!
-
-
-
-
-
-[`SwiftMessagesSegue`](./SwiftMessages/SwiftMessagesSegue.swift) is a subclass of `UIStoryboardSegue` that integrates directly into Interface Builder as a custom modal segue, enabling view controllers to take advantage of SwiftMessages layouts, animations and more. `SwiftMessagesSegue` works with any UIKIt project — storyboards are not required. Refer to the View Controllers readme below for more information.
-
-#### [View Controllers Readme](./ViewControllers.md)
-
-And check out our blog post [Elegant Custom UIViewController Transitioning](http://www.swiftkickmobile.com/elegant-custom-uiviewcontroller-transitioning-uiviewcontrollertransitioningdelegate-uiviewcontrolleranimatedtransitioning/) to learn a great technique you can use to build your own custom segues that utilize `UIViewControllerTransitioningDelegate` and `UIViewControllerAnimatedTransitioning`.
-
## Installation
### Swift Package Manager
@@ -151,12 +133,17 @@ config.dimMode = .gray(interactive: true)
// Disable the interactive pan-to-hide gesture.
config.interactiveHide = false
+// Specify haptic feedback (see also MessageView/configureTheme)
+config.haptic = .success
+
// Specify a status bar style to if the message is displayed directly under the status bar.
config.preferredStatusBarStyle = .lightContent
// Specify one or more event listeners to respond to show and hide events.
config.eventListeners.append() { event in
- if case .didHide = event { print("yep") }
+ if case .didHide = event {
+ print("yep id=\(String(describing: event.id)")
+ }
}
SwiftMessages.show(config: config, view: view)
@@ -176,6 +163,121 @@ config.duration = .forever
SwiftMessages.show(config: config, view: view)
````
+### View Controllers
+
+SwiftMessages can present view controllers using the `SwiftMessagesSegue` custom modal segue!
+
+
+
+
+
+[`SwiftMessagesSegue`](./SwiftMessages/SwiftMessagesSegue.swift) is a subclass of `UIStoryboardSegue` that integrates directly into Interface Builder as a custom modal segue, enabling view controllers to take advantage of SwiftMessages layouts, animations and more. `SwiftMessagesSegue` works with any UIKIt project — storyboards are not required. Refer to the View Controllers readme below for more information.
+
+#### [View Controllers Readme](./ViewControllers.md)
+
+And check out our blog post [Elegant Custom UIViewController Transitioning](http://www.swiftkickmobile.com/elegant-custom-uiviewcontroller-transitioning-uiviewcontrollertransitioningdelegate-uiviewcontrolleranimatedtransitioning/) to learn a great technique you can use to build your own custom segues that utilize `UIViewControllerTransitioningDelegate` and `UIViewControllerAnimatedTransitioning`.
+
+### SwiftUI
+
+Any of the built-in SwiftMessages views can be displayed by calling the SwiftMessages APIs from within observable object, a button action closure, etc. However, SwiftMessages can also display your custom SwiftUI views.
+
+Take the following message view and companion data model:
+
+````swift
+struct DemoMessage: Identifiable {
+ let title: String
+ let body: String
+
+ var id: String { title + body }
+}
+
+struct DemoMessageView: View {
+
+ let message: DemoMessage
+
+ var body: some View {
+ VStack(alignment: .leading) {
+ Text(message.title).font(.system(size: 20, weight: .bold))
+ Text(message.body)
+ }
+ .multilineTextAlignment(.leading)
+ .padding(30)
+ // This makes the message width greedy
+ .frame(maxWidth: .infinity)
+ .background(.gray)
+ // This makes a tab-style view where the bottom corners are rounded and
+ // the view's background extends to the top edge.
+ .mask(
+ UnevenRoundedRectangle(bottomLeadingRadius: 15, bottomTrailingRadius: 15)
+ // This causes the background to extend into the safe area to the screen edge.
+ .edgesIgnoringSafeArea(.top)
+ )
+ }
+}
+````
+
+You can show it from a button action, view model or other similar context like:
+
+````swift
+struct DemoView: View {
+ var body: some View {
+ Button("Show message") {
+ let message = DemoMessage(title: "Demo", body: "SwiftUI forever!")
+ let messageView = MessageHostingView(id: message.id, content: DemoMessageView(message: message)
+ SwiftMessages.show(view: messageView)
+ }
+ }
+}
+````
+
+But you may also use a state-based approach using the `swiftMessage()` view modifier:
+
+````swift
+struct DemoView: View {
+
+ @State var message: DemoMessage?
+
+ var body: some View {
+ Button("Show message") {
+ message = DemoMessage(title: "Demo", body: "SwiftUI forever!")
+ }
+ .swiftMessage(message: $message) { message in
+ DemoMessageView(message: message)
+ }
+ }
+}
+````
+
+This is very similar to the `.sheet()` modifier. However, it doesn't expose all of the features of SwiftMessages, such as explicitly hiding messages by ID. It is totally reasonable to use a combination of both approaches.
+
+If your message views are purely data-driven and don't require delegates, callbacks, etc., there is a slightly simplified variation on `swiftMessage()` that doesn't require a view builder. Instead, your data model should conform to `MessageViewConvertible`.
+
+````swift
+extension DemoMessage: MessageViewConvertible {
+ func asMessageView() -> DemoMessageView {
+ DemoMessageView(message: self)
+ }
+}
+````
+
+Then you can drop the view builder when calling `swiftMessage()`:
+
+````swift
+struct DemoView: View {
+
+ @State var message: DemoMessage?
+
+ var body: some View {
+ Button("Show message") {
+ message = DemoMessage(title: "Demo", body: "SwiftUI forever!")
+ }
+ .swiftMessage(message: $message)
+ }
+}
+````
+
+Try it out in the SwiftUI demo app!
+
### Accessibility
SwiftMessages provides excellent VoiceOver support out-of-the-box.
@@ -308,7 +410,7 @@ A common mistake is attempting to remove an element by setting the corresponding
`MessageView` provides numerous methods that follow the `configure*` naming convention:
````swift
-view.configureTheme(.warning)
+view.configureTheme(.warning, includeHaptic: true)
view.configureContent(title: "Warning", body: "Consider yourself warned.", iconText: "🤔")
````
diff --git a/SwiftMessages.podspec b/SwiftMessages.podspec
index a6c018fa..0500d4ac 100644
--- a/SwiftMessages.podspec
+++ b/SwiftMessages.podspec
@@ -1,14 +1,14 @@
Pod::Spec.new do |spec|
spec.name = 'SwiftMessages'
- spec.version = '9.0.3'
+ spec.version = '10.0.1'
spec.license = { :type => 'MIT' }
spec.homepage = 'https://github.com/SwiftKickMobile/SwiftMessages'
- spec.authors = { 'Timothy Moose' => 'tim@swiftkick.it' }
+ spec.authors = { 'Timothy Moose' => 'tim@swiftkickmobile.com' }
spec.summary = 'A very flexible message bar for iOS written in Swift.'
spec.source = {:git => 'https://github.com/SwiftKickMobile/SwiftMessages.git', :tag => spec.version}
- spec.platform = :ios, '9.0'
+ spec.platform = :ios, '13.0'
spec.swift_version = '5.0'
- spec.ios.deployment_target = '9.0'
+ spec.ios.deployment_target = '13.0'
spec.framework = 'UIKit'
spec.requires_arc = true
spec.default_subspec = 'App'
diff --git a/SwiftMessages.xcodeproj/project.pbxproj b/SwiftMessages.xcodeproj/project.pbxproj
index 5103fcdd..10384827 100644
--- a/SwiftMessages.xcodeproj/project.pbxproj
+++ b/SwiftMessages.xcodeproj/project.pbxproj
@@ -3,12 +3,16 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
+ 0797E40E26EE12B400691606 /* WindowScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0797E40D26EE12B400691606 /* WindowScene.swift */; };
220655121FAF82B600F4E00F /* MarginAdjustable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 220655111FAF82B600F4E00F /* MarginAdjustable+Extensions.swift */; };
220D386E2597AA5B00BB2B88 /* SwiftMessages.Config+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 220D386D2597AA5B00BB2B88 /* SwiftMessages.Config+Extensions.swift */; };
+ 223DE69D2C29E50C000161E5 /* MessageGeometryProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 223DE69C2C29E50B000161E5 /* MessageGeometryProxy.swift */; };
+ 224C3C902C28A2F900B50B18 /* TopBottomPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 224C3C8F2C28A2F900B50B18 /* TopBottomPresentable.swift */; };
+ 224C3C932C28BC4900B50B18 /* TopBottomAnimationStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 224C3C922C28BC4400B50B18 /* TopBottomAnimationStyle.swift */; };
224FB69921153B440081D4DE /* CALayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 224FB69821153B440081D4DE /* CALayer+Extensions.swift */; };
225304622290C76E00A03ACF /* NSLayoutConstraint+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 225304612290C76E00A03ACF /* NSLayoutConstraint+Extensions.swift */; };
225304662293000C00A03ACF /* KeyboardTrackingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 225304652293000C00A03ACF /* KeyboardTrackingView.swift */; };
@@ -51,10 +55,15 @@
228DF5681FAD0806004F8A39 /* infoIconSubtle.png in Resources */ = {isa = PBXBuildFile; fileRef = 228DF5471FAD0805004F8A39 /* infoIconSubtle.png */; };
228DF5691FAD0806004F8A39 /* successIconLight.png in Resources */ = {isa = PBXBuildFile; fileRef = 228DF5481FAD0805004F8A39 /* successIconLight.png */; };
228DF56A1FAD0806004F8A39 /* infoIconSubtle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 228DF5491FAD0805004F8A39 /* infoIconSubtle@3x.png */; };
+ 228F7DDE2ACF703A006C9644 /* MessageHostingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228F7DDB2ACF7039006C9644 /* MessageHostingView.swift */; };
+ 228F7DDF2ACF703A006C9644 /* SwiftMessageModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228F7DDC2ACF703A006C9644 /* SwiftMessageModifier.swift */; };
+ 228F7DE02ACF703A006C9644 /* MessageViewConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228F7DDD2ACF703A006C9644 /* MessageViewConvertible.swift */; };
+ 22982C172B6030B000852311 /* HapticMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22982C162B6030B000852311 /* HapticMessage.swift */; };
2298C2051EE47DC900E2DDC1 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2298C2041EE47DC900E2DDC1 /* Weak.swift */; };
2298C2071EE480D000E2DDC1 /* Animator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2298C2061EE480D000E2DDC1 /* Animator.swift */; };
2298C2091EE486E300E2DDC1 /* TopBottomAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2298C2081EE486E300E2DDC1 /* TopBottomAnimation.swift */; };
- 229F778125FAB1E9008C2ACB /* UIWindow+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229F778025FAB1E9008C2ACB /* UIWindow+Extensions.swift */; };
+ 229F778125FAB1E9008C2ACB /* UIWindow+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229F778025FAB1E9008C2ACB /* UIWindow+Extensions.swift */; };
+ 22D3B4562B1CEF76002D8665 /* Task+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22D3B4552B1CEF76002D8665 /* Task+Extensions.swift */; };
22DFC9161EFF30F6001B1CA1 /* CenteredView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 22DFC9151EFF30F6001B1CA1 /* CenteredView.xib */; };
22DFC9181F00674E001B1CA1 /* PhysicsPanHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22DFC9171F00674E001B1CA1 /* PhysicsPanHandler.swift */; };
22E01F641E74EC8B00ACE19A /* MaskingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E01F631E74EC8B00ACE19A /* MaskingView.swift */; };
@@ -94,8 +103,12 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 0797E40D26EE12B400691606 /* WindowScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WindowScene.swift; sourceTree = ""; };
220655111FAF82B600F4E00F /* MarginAdjustable+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MarginAdjustable+Extensions.swift"; sourceTree = ""; };
220D386D2597AA5B00BB2B88 /* SwiftMessages.Config+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SwiftMessages.Config+Extensions.swift"; sourceTree = ""; };
+ 223DE69C2C29E50B000161E5 /* MessageGeometryProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageGeometryProxy.swift; sourceTree = ""; };
+ 224C3C8F2C28A2F900B50B18 /* TopBottomPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBottomPresentable.swift; sourceTree = ""; };
+ 224C3C922C28BC4400B50B18 /* TopBottomAnimationStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBottomAnimationStyle.swift; sourceTree = ""; };
224FB69821153B440081D4DE /* CALayer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+Extensions.swift"; sourceTree = ""; };
225304612290C76E00A03ACF /* NSLayoutConstraint+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLayoutConstraint+Extensions.swift"; sourceTree = ""; };
225304652293000C00A03ACF /* KeyboardTrackingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardTrackingView.swift; sourceTree = ""; };
@@ -138,11 +151,16 @@
228DF5471FAD0805004F8A39 /* infoIconSubtle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = infoIconSubtle.png; path = Resources/infoIconSubtle.png; sourceTree = ""; };
228DF5481FAD0805004F8A39 /* successIconLight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = successIconLight.png; path = Resources/successIconLight.png; sourceTree = ""; };
228DF5491FAD0805004F8A39 /* infoIconSubtle@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "infoIconSubtle@3x.png"; path = "Resources/infoIconSubtle@3x.png"; sourceTree = ""; };
+ 228F7DDB2ACF7039006C9644 /* MessageHostingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHostingView.swift; sourceTree = ""; };
+ 228F7DDC2ACF703A006C9644 /* SwiftMessageModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftMessageModifier.swift; sourceTree = ""; };
+ 228F7DDD2ACF703A006C9644 /* MessageViewConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageViewConvertible.swift; sourceTree = ""; };
+ 22982C162B6030B000852311 /* HapticMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticMessage.swift; sourceTree = ""; };
2298C2041EE47DC900E2DDC1 /* Weak.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = ""; };
2298C2061EE480D000E2DDC1 /* Animator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animator.swift; sourceTree = ""; };
2298C2081EE486E300E2DDC1 /* TopBottomAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopBottomAnimation.swift; sourceTree = ""; };
- 229F778025FAB1E9008C2ACB /* UIWindow+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIWindow+Extensions.swift"; sourceTree = ""; };
+ 229F778025FAB1E9008C2ACB /* UIWindow+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIWindow+Extensions.swift"; sourceTree = ""; };
22A2EA6E24EC6CFA00BB2540 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; };
+ 22D3B4552B1CEF76002D8665 /* Task+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Task+Extensions.swift"; sourceTree = ""; };
22DFC9151EFF30F6001B1CA1 /* CenteredView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CenteredView.xib; path = Resources/CenteredView.xib; sourceTree = ""; };
22DFC9171F00674E001B1CA1 /* PhysicsPanHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhysicsPanHandler.swift; sourceTree = ""; };
22E01F631E74EC8B00ACE19A /* MaskingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MaskingView.swift; sourceTree = ""; };
@@ -211,7 +229,8 @@
children = (
220655111FAF82B600F4E00F /* MarginAdjustable+Extensions.swift */,
22774B9F20B5EF2A00813732 /* UIEdgeInsets+Extensions.swift */,
- 229F778025FAB1E9008C2ACB /* UIWindow+Extensions.swift */,
+ 229F778025FAB1E9008C2ACB /* UIWindow+Extensions.swift */,
+ 22D3B4552B1CEF76002D8665 /* Task+Extensions.swift */,
);
name = Extensions;
sourceTree = "";
@@ -233,6 +252,17 @@
name = Frameworks;
sourceTree = "";
};
+ 228F7DDA2ACF7029006C9644 /* SwiftUI */ = {
+ isa = PBXGroup;
+ children = (
+ 223DE69C2C29E50B000161E5 /* MessageGeometryProxy.swift */,
+ 228F7DDB2ACF7039006C9644 /* MessageHostingView.swift */,
+ 228F7DDD2ACF703A006C9644 /* MessageViewConvertible.swift */,
+ 228F7DDC2ACF703A006C9644 /* SwiftMessageModifier.swift */,
+ );
+ name = SwiftUI;
+ sourceTree = "";
+ };
22D4779B20BF1C54005D0D71 /* View Controllers */ = {
isa = PBXGroup;
children = (
@@ -292,18 +322,22 @@
864495571D4F7C490056EB2A /* Base */ = {
isa = PBXGroup;
children = (
- 86589D461D64B6E40041676C /* BaseView.swift */,
- 86AAF82C1D580F410031EE32 /* Theme.swift */,
- 8644955C1D4FAF7C0056EB2A /* WindowViewController.swift */,
- 867BED201D622793005212E3 /* BackgroundViewable.swift */,
- 864495551D4F7C390056EB2A /* Identifiable.swift */,
- 86AAF81D1D5549680031EE32 /* MarginAdjustable.swift */,
22E307FE1E74C5B100E35893 /* AccessibleMessage.swift */,
- 86AAF82A1D580DD70031EE32 /* Error.swift */,
2298C2061EE480D000E2DDC1 /* Animator.swift */,
- 2298C2041EE47DC900E2DDC1 /* Weak.swift */,
+ 867BED201D622793005212E3 /* BackgroundViewable.swift */,
+ 86589D461D64B6E40041676C /* BaseView.swift */,
22F27950210CE25900273E7F /* CornerRoundingView.swift */,
+ 86AAF82A1D580DD70031EE32 /* Error.swift */,
+ 22982C162B6030B000852311 /* HapticMessage.swift */,
+ 864495551D4F7C390056EB2A /* Identifiable.swift */,
225304652293000C00A03ACF /* KeyboardTrackingView.swift */,
+ 86AAF81D1D5549680031EE32 /* MarginAdjustable.swift */,
+ 86AAF82C1D580F410031EE32 /* Theme.swift */,
+ 224C3C922C28BC4400B50B18 /* TopBottomAnimationStyle.swift */,
+ 224C3C8F2C28A2F900B50B18 /* TopBottomPresentable.swift */,
+ 2298C2041EE47DC900E2DDC1 /* Weak.swift */,
+ 0797E40D26EE12B400691606 /* WindowScene.swift */,
+ 8644955C1D4FAF7C0056EB2A /* WindowViewController.swift */,
);
name = Base;
sourceTree = "";
@@ -349,6 +383,7 @@
862C0CD81D5A396900D06168 /* Resources */,
2244656C1EF1D62700C50413 /* Animations */,
22D4779B20BF1C54005D0D71 /* View Controllers */,
+ 228F7DDA2ACF7029006C9644 /* SwiftUI */,
864495571D4F7C490056EB2A /* Base */,
220D38682597A9FD00BB2B88 /* Extensions */,
867E218E1D4D3DFD00594A41 /* Internal */,
@@ -431,8 +466,9 @@
867E21471D4D01D500594A41 /* Project object */ = {
isa = PBXProject;
attributes = {
+ BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 0730;
- LastUpgradeCheck = 1200;
+ LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "SwiftKick Mobile";
TargetAttributes = {
86B48AEB1D5A41C900063E2B = {
@@ -535,17 +571,24 @@
22F27951210CE25900273E7F /* CornerRoundingView.swift in Sources */,
86BBA9011D5E040600FE8F16 /* PassthroughWindow.swift in Sources */,
2298C2071EE480D000E2DDC1 /* Animator.swift in Sources */,
+ 22D3B4562B1CEF76002D8665 /* Task+Extensions.swift in Sources */,
+ 22982C172B6030B000852311 /* HapticMessage.swift in Sources */,
86BBA9031D5E040600FE8F16 /* UIViewController+Extensions.swift in Sources */,
+ 228F7DDF2ACF703A006C9644 /* SwiftMessageModifier.swift in Sources */,
224FB69921153B440081D4DE /* CALayer+Extensions.swift in Sources */,
22E01F641E74EC8B00ACE19A /* MaskingView.swift in Sources */,
+ 224C3C932C28BC4900B50B18 /* TopBottomAnimationStyle.swift in Sources */,
2298C2051EE47DC900E2DDC1 /* Weak.swift in Sources */,
+ 228F7DE02ACF703A006C9644 /* MessageViewConvertible.swift in Sources */,
86BBA9001D5E040600FE8F16 /* PassthroughView.swift in Sources */,
22DFC9181F00674E001B1CA1 /* PhysicsPanHandler.swift in Sources */,
227BA6D920BF224A00E5A843 /* SwiftMessagesSegue.swift in Sources */,
220655121FAF82B600F4E00F /* MarginAdjustable+Extensions.swift in Sources */,
+ 228F7DDE2ACF703A006C9644 /* MessageHostingView.swift in Sources */,
22E307FF1E74C5B100E35893 /* AccessibleMessage.swift in Sources */,
220D386E2597AA5B00BB2B88 /* SwiftMessages.Config+Extensions.swift in Sources */,
2270044B1FAFA6DD0045DDC3 /* PhysicsAnimation.swift in Sources */,
+ 224C3C902C28A2F900B50B18 /* TopBottomPresentable.swift in Sources */,
86BBA9041D5E040600FE8F16 /* NSBundle+Extensions.swift in Sources */,
86BBA8FD1D5E03F800FE8F16 /* SwiftMessages.swift in Sources */,
86BBA9021D5E040600FE8F16 /* WindowViewController.swift in Sources */,
@@ -555,7 +598,9 @@
86BBA9081D5E040C00FE8F16 /* Error.swift in Sources */,
2298C2091EE486E300E2DDC1 /* TopBottomAnimation.swift in Sources */,
86589D471D64B6E40041676C /* BaseView.swift in Sources */,
+ 0797E40E26EE12B400691606 /* WindowScene.swift in Sources */,
225304622290C76E00A03ACF /* NSLayoutConstraint+Extensions.swift in Sources */,
+ 223DE69D2C29E50C000161E5 /* MessageGeometryProxy.swift in Sources */,
86BBA9071D5E040C00FE8F16 /* MarginAdjustable.swift in Sources */,
867BED211D622793005212E3 /* BackgroundViewable.swift in Sources */,
);
@@ -584,6 +629,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@@ -615,6 +661,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -629,7 +676,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.1;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -642,6 +689,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
@@ -673,6 +721,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -681,10 +730,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.1;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
- SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 3.0;
VALIDATE_PRODUCT = YES;
};
@@ -694,17 +744,24 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ CODE_SIGN_IDENTITY = "";
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
INFOPLIST_FILE = SwiftMessages/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.SwiftMessages;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -722,17 +779,24 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+ CODE_SIGN_IDENTITY = "";
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
INFOPLIST_FILE = SwiftMessages/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.SwiftMessages;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -749,7 +813,11 @@
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = SwiftMessagesTests/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.SwiftMessagesTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = On;
@@ -761,7 +829,11 @@
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = SwiftMessagesTests/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.SwiftMessagesTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = On;
diff --git a/SwiftMessages.xcodeproj/xcshareddata/xcschemes/SwiftMessages.xcscheme b/SwiftMessages.xcodeproj/xcshareddata/xcschemes/SwiftMessages.xcscheme
index ad3b2531..ba0f414b 100644
--- a/SwiftMessages.xcodeproj/xcshareddata/xcschemes/SwiftMessages.xcscheme
+++ b/SwiftMessages.xcodeproj/xcshareddata/xcschemes/SwiftMessages.xcscheme
@@ -1,6 +1,6 @@
Void
-public protocol AnimationDelegate: class {
+@MainActor
+public protocol AnimationDelegate: AnyObject {
func hide(animator: Animator)
func panStarted(animator: Animator)
func panEnded(animator: Animator)
@@ -58,7 +59,8 @@ public class AnimationContext {
}
}
-public protocol Animator: class {
+@MainActor
+public protocol Animator: AnyObject {
/// Adopting classes should declare as `weak`.
var delegate: AnimationDelegate? { get set }
diff --git a/SwiftMessages/BaseView.swift b/SwiftMessages/BaseView.swift
index 9480b87d..c41d166b 100644
--- a/SwiftMessages/BaseView.swift
+++ b/SwiftMessages/BaseView.swift
@@ -280,6 +280,11 @@ open class BaseView: UIView, BackgroundViewable, MarginAdjustable {
private var layoutConstraints: [NSLayoutConstraint] = []
private var regularWidthLayoutConstraints: [NSLayoutConstraint] = []
+
+ open override func layoutSubviews() {
+ super.layoutSubviews()
+ updateShadowPath()
+ }
}
/*
@@ -293,7 +298,7 @@ extension BaseView {
/// because the background view may be masked. So, when modifying the drop shadow,
/// be sure to set the shadow properties of this view's layer. The shadow path is
/// updated for you automatically.
- open func configureDropShadow() {
+ public func configureDropShadow() {
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
layer.shadowRadius = 6.0
@@ -303,7 +308,7 @@ extension BaseView {
}
/// A convenience function to turn off drop shadow
- open func configureNoDropShadow() {
+ public func configureNoDropShadow() {
layer.shadowOpacity = 0
}
@@ -334,11 +339,6 @@ extension BaseView {
// Update the layer's `shadowPath` without animation
layer.shadowPath = shadowPath }
}
-
- open override func layoutSubviews() {
- super.layoutSubviews()
- updateShadowPath()
- }
}
/*
diff --git a/SwiftMessages/HapticMessage.swift b/SwiftMessages/HapticMessage.swift
new file mode 100644
index 00000000..fa86614f
--- /dev/null
+++ b/SwiftMessages/HapticMessage.swift
@@ -0,0 +1,16 @@
+//
+// HapticMessage.swift
+// SwiftMessages
+//
+// Created by Timothy Moose on 1/23/24.
+// Copyright © 2024 SwiftKick Mobile. All rights reserved.
+//
+
+import Foundation
+
+/**
+ Message views that conform to `HapticMessage` can specify a haptic feedback to be used when presented.
+ */
+protocol HapticMessage {
+ var defaultHaptic: SwiftMessages.Haptic? { get }
+}
diff --git a/SwiftMessages/Identifiable.swift b/SwiftMessages/Identifiable.swift
index 4594b2d0..2985410f 100644
--- a/SwiftMessages/Identifiable.swift
+++ b/SwiftMessages/Identifiable.swift
@@ -17,6 +17,7 @@ import Foundation
This protocol is optional. Message views that don't adopt `Identifiable` will not
have duplicates removed.
*/
+
public protocol Identifiable {
var id: String { get }
}
diff --git a/SwiftMessages/KeyboardTrackingView.swift b/SwiftMessages/KeyboardTrackingView.swift
index 69ce6b0b..173af175 100644
--- a/SwiftMessages/KeyboardTrackingView.swift
+++ b/SwiftMessages/KeyboardTrackingView.swift
@@ -8,7 +8,7 @@
import UIKit
-public protocol KeyboardTrackingViewDelegate: class {
+public protocol KeyboardTrackingViewDelegate: AnyObject {
func keyboardTrackingViewWillChange(change: KeyboardTrackingView.Change, userInfo: [AnyHashable : Any])
func keyboardTrackingViewDidChange(change: KeyboardTrackingView.Change, userInfo: [AnyHashable : Any])
}
@@ -44,6 +44,18 @@ open class KeyboardTrackingView: UIView {
/// The margin to maintain between the keyboard and the top of the view.
@IBInspectable open var topMargin: CGFloat = 0
+ /// Subclasses can override this to do something before the change.
+ open func willChange(
+ change: KeyboardTrackingView.Change,
+ userInfo: [AnyHashable : Any]
+ ) {}
+
+ /// Subclasses can override this to do something after the change.
+ open func didChange(
+ change: KeyboardTrackingView.Change,
+ userInfo: [AnyHashable : Any]
+ ) {}
+
override public init(frame: CGRect) {
super.init(frame: frame)
postInit()
@@ -60,6 +72,7 @@ open class KeyboardTrackingView: UIView {
private var isAutomaticallyPaused = false
private var heightConstraint: NSLayoutConstraint!
+ private var lastObservedKeyboardRect: CGRect?
private func postInit() {
translatesAutoresizingMaskIntoConstraints = false
@@ -97,33 +110,39 @@ open class KeyboardTrackingView: UIView {
isAutomaticallyPaused = false
}
+ open override func layoutSubviews() {
+ super.layoutSubviews()
+ heightConstraint.constant = calculateHeightConstant()
+ }
+
private func show(change: Change, _ notification: Notification) {
guard !(isPaused || isAutomaticallyPaused),
let userInfo = (notification as NSNotification).userInfo,
let value = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
- let keyboardRect = value.cgRectValue
- let thisRect = convert(bounds, to: nil)
- let newHeight = max(0, thisRect.maxY - keyboardRect.minY) + topMargin
- guard heightConstraint.constant != newHeight else { return }
+ willChange(change: change, userInfo: userInfo)
delegate?.keyboardTrackingViewWillChange(change: change, userInfo: userInfo)
+ lastObservedKeyboardRect = value.cgRectValue
+ let newHeight = calculateHeightConstant()
+ guard heightConstraint.constant != newHeight else { return }
animateKeyboardChange(change: change, height: newHeight, userInfo: userInfo)
}
private func animateKeyboardChange(change: Change, height: CGFloat, userInfo: [AnyHashable: Any]) {
- self.heightConstraint.constant = height
- if let durationNumber = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber,
- let curveNumber = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber {
- CATransaction.begin()
- CATransaction.setCompletionBlock {
+ if let durationNumber = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber {
+ UIView.animate(withDuration: durationNumber.doubleValue, delay: 0, options: .curveEaseInOut, animations: {
+ self.heightConstraint.constant = height
+ self.updateConstraintsIfNeeded()
+ self.superview?.layoutIfNeeded()
+ }) { completed in
+ self.didChange(change: change, userInfo: userInfo)
self.delegate?.keyboardTrackingViewDidChange(change: change, userInfo: userInfo)
}
- UIView.beginAnimations(nil, context: nil)
- UIView.setAnimationDuration(durationNumber.doubleValue)
- UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: curveNumber.intValue)!)
- UIView.setAnimationBeginsFromCurrentState(true)
- self.superview?.layoutIfNeeded()
- UIView.commitAnimations()
- CATransaction.commit()
}
}
+
+ private func calculateHeightConstant() -> CGFloat {
+ guard let keyboardRect = lastObservedKeyboardRect else { return 0 }
+ let thisRect = convert(bounds, to: nil)
+ return max(0, thisRect.maxY - keyboardRect.minY) + topMargin
+ }
}
diff --git a/SwiftMessages/MarginAdjustable+Extensions.swift b/SwiftMessages/MarginAdjustable+Extensions.swift
index b6277885..da8f46d1 100644
--- a/SwiftMessages/MarginAdjustable+Extensions.swift
+++ b/SwiftMessages/MarginAdjustable+Extensions.swift
@@ -13,25 +13,8 @@ extension MarginAdjustable where Self: UIView {
var layoutMargins: UIEdgeInsets = layoutMarginAdditions
var safeAreaInsets: UIEdgeInsets = {
guard respectSafeArea else { return .zero }
- if #available(iOS 11, *) {
- insetsLayoutMarginsFromSafeArea = false
- return self.safeAreaInsets
- } else {
- #if SWIFTMESSAGES_APP_EXTENSIONS
- let application: UIApplication? = nil
- #else
- let application: UIApplication? = UIApplication.shared
- #endif
- if !context.safeZoneConflicts.isDisjoint(with: [.statusBar]),
- let app = application,
- app.statusBarOrientation == .portrait || app.statusBarOrientation == .portraitUpsideDown {
- let frameInWindow = convert(bounds, to: window)
- let top = max(0, 20 - frameInWindow.minY)
- return UIEdgeInsets(top: top, left: 0, bottom: 0, right: 0)
- } else {
- return .zero
- }
- }
+ insetsLayoutMarginsFromSafeArea = false
+ return self.safeAreaInsets
}()
if !context.safeZoneConflicts.isDisjoint(with: .overStatusBar) {
safeAreaInsets.top = 0
diff --git a/SwiftMessages/MaskingView.swift b/SwiftMessages/MaskingView.swift
index 0ce4c428..d6313427 100644
--- a/SwiftMessages/MaskingView.swift
+++ b/SwiftMessages/MaskingView.swift
@@ -65,6 +65,15 @@ class MaskingView: PassthroughView {
guard let keyboardTrackingView = keyboardTrackingView,
view != keyboardTrackingView,
view != backgroundView else { return }
- keyboardTrackingView.topAnchor.constraint(greaterThanOrEqualTo: view.bottomAnchor).with(priority: UILayoutPriority(250)).isActive = true
+ let offset: CGFloat
+ if let adjustable = view as? MarginAdjustable {
+ offset = -adjustable.bounceAnimationOffset
+ } else {
+ offset = 0
+ }
+ keyboardTrackingView.topAnchor.constraint(
+ greaterThanOrEqualTo: view.bottomAnchor,
+ constant: offset
+ ).with(priority: UILayoutPriority(250)).isActive = true
}
}
diff --git a/SwiftMessages/MessageGeometryProxy.swift b/SwiftMessages/MessageGeometryProxy.swift
new file mode 100644
index 00000000..73d2d785
--- /dev/null
+++ b/SwiftMessages/MessageGeometryProxy.swift
@@ -0,0 +1,17 @@
+//
+// MessageGeometryProxy.swift
+// SwiftMessages
+//
+// Created by Timothy Moose on 6/24/24.
+// Copyright © 2024 SwiftKick Mobile. All rights reserved.
+//
+
+import SwiftUI
+
+/// A data type that mimicks `GeomtryProxy` and is used with `swiftMessage()` modifier when the geomtry metrics of the container view
+/// are needed, particularly because `GeometryReader` doesn't work inside the view builder due to the way the message view is being
+/// displayed from UIKit.
+public struct MessageGeometryProxy {
+ public var size: CGSize
+ public var safeAreaInsets: EdgeInsets
+}
diff --git a/SwiftMessages/MessageHostingView.swift b/SwiftMessages/MessageHostingView.swift
new file mode 100644
index 00000000..4301090e
--- /dev/null
+++ b/SwiftMessages/MessageHostingView.swift
@@ -0,0 +1,114 @@
+//
+// MessageHostingView.swift
+// SwiftMessages
+//
+// Created by Timothy Moose on 10/5/23.
+//
+
+import SwiftUI
+import UIKit
+
+/// A rudimentary hosting view for SwiftUI messages.
+@available(iOS 14.0, *)
+public class MessageHostingView: UIView, Identifiable where Content: View {
+
+ // MARK: - API
+
+ public let id: String
+
+ public init(id: String, content: Content) {
+ self.id = id
+ self.content = { _ in content }
+ super.init(frame: .zero)
+ backgroundColor = .clear
+ }
+
+ public init(
+ message: Message,
+ @ViewBuilder content: @escaping (Message, MessageGeometryProxy) -> Content
+ ) where Message: Identifiable {
+ self.id = message.id
+ self.content = { geom in content(message, geom) }
+ super.init(frame: .zero)
+ backgroundColor = .clear
+ }
+
+ convenience public init(message: Message) where Message: MessageViewConvertible, Message.Content == Content {
+ self.init(id: message.id, content: message.asMessageView() )
+ }
+
+ // MARK: - Constants
+
+ // MARK: - Variables
+
+ private var hostVC: UIHostingController?
+ private let content: (MessageGeometryProxy) -> Content
+
+ // MARK: - Lifecycle
+
+ @available(*, unavailable)
+ required init?(coder _: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
+ guard let view = super.hitTest(point, with: event) else { return nil }
+ // Touches should pass through unless they land on a view that is rendering a SwiftUI element.
+ if view == self { return nil }
+ // In iOS 18 beta, the hit testing behavior changed in a weird way: when a SwiftUI element is tapped,
+ // the first hit test returns the view that renders the SwiftUI element. However, a second identical hit
+ // test is performed(!) and on the second test, the `UIHostingController`'s view is returned. We want touches
+ // to pass through that view. In iOS 17, we would just return `nil` in that case. However, in iOS 18, the
+ // second hit test is actuall essential to touches being delivered to the SwiftUI elements. The new approach
+ // is to iterate overall all of the subviews, which are all presumably rendering SwiftUI elements, and
+ // only return `nil` if the point is not inside any of these subviews.
+ if view.superview == self {
+ for subview in view.subviews {
+ let subviewPoint = self.convert(point, to: subview)
+ if subview.point(inside: subviewPoint, with: event) {
+ return view
+ }
+ }
+ return nil
+ }
+ return view
+ }
+
+ public override func didMoveToSuperview() {
+ guard let superview = self.superview else { return }
+ let size = superview.bounds.size
+ let insets = superview.safeAreaInsets
+ let ltr = superview.effectiveUserInterfaceLayoutDirection == .leftToRight
+ let proxy = MessageGeometryProxy(
+ size: CGSize(
+ width: size.width - insets.left - insets.right,
+ height: size.height - insets.top - insets.bottom
+ ),
+ safeAreaInsets: EdgeInsets(
+ top: insets.top,
+ leading: ltr ? insets.left : insets.right,
+ bottom: insets.bottom,
+ trailing: ltr ? insets.right : insets.left
+ )
+ )
+ let hostVC = UIHostingController(rootView: content(proxy))
+ self.hostVC = hostVC
+ hostVC.loadViewIfNeeded()
+ installContentView(hostVC.view)
+ hostVC.view.backgroundColor = .clear
+
+ }
+
+ // MARK: - Configuration
+
+ private func installContentView(_ contentView: UIView) {
+ contentView.translatesAutoresizingMaskIntoConstraints = false
+ addSubview(contentView)
+ NSLayoutConstraint.activate([
+ contentView.topAnchor.constraint(equalTo: topAnchor),
+ contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
+ contentView.leftAnchor.constraint(equalTo: leftAnchor),
+ contentView.rightAnchor.constraint(equalTo: rightAnchor),
+ ])
+ }
+}
diff --git a/SwiftMessages/MessageView.swift b/SwiftMessages/MessageView.swift
index d9be31d9..6384917e 100644
--- a/SwiftMessages/MessageView.swift
+++ b/SwiftMessages/MessageView.swift
@@ -10,8 +10,15 @@ import UIKit
/*
*/
-open class MessageView: BaseView, Identifiable, AccessibleMessage {
+open class MessageView: BaseView, Identifiable, AccessibleMessage, HapticMessage {
+
+ /*
+ MARK: - Haptic feedback
+ */
+ /// The default haptic feedback to be used when the message is presented.
+ open var defaultHaptic: SwiftMessages.Haptic?
+
/*
MARK: - Button tap handler
*/
@@ -248,27 +255,76 @@ extension MessageView {
- Parameter theme: The theme type to use.
- Parameter iconStyle: The icon style to use. Defaults to `.Default`.
+ - Parameter useHaptics: If `true`, configures an appropriate haptic based on theme. Defaults to `false`.
*/
- public func configureTheme(_ theme: Theme, iconStyle: IconStyle = .default) {
+ public func configureTheme(_ theme: Theme, iconStyle: IconStyle = .default, includeHaptic: Bool = false) {
let iconImage = iconStyle.image(theme: theme)
+ let backgroundColor: UIColor
+ let foregroundColor: UIColor
+ let defaultBackgroundColor: UIColor
+ switch theme {
+ case .info:
+ defaultBackgroundColor = UIColor(red: 225.0/255.0, green: 225.0/255.0, blue: 225.0/255.0, alpha: 1.0)
+ case .success:
+ defaultBackgroundColor = UIColor(red: 97.0/255.0, green: 161.0/255.0, blue: 23.0/255.0, alpha: 1.0)
+ case .warning:
+ defaultBackgroundColor = UIColor(red: 246.0/255.0, green: 197.0/255.0, blue: 44.0/255.0, alpha: 1.0)
+ case .error:
+ defaultBackgroundColor = UIColor(red: 249.0/255.0, green: 66.0/255.0, blue: 47.0/255.0, alpha: 1.0)
+ }
+ if includeHaptic {
+ switch theme {
+ case .success, .info:
+ defaultHaptic = SwiftMessages.Haptic.success
+ case .warning:
+ defaultHaptic = SwiftMessages.Haptic.warning
+ case .error:
+ defaultHaptic = SwiftMessages.Haptic.error
+ }
+ }
switch theme {
case .info:
- let backgroundColor = UIColor(red: 225.0/255.0, green: 225.0/255.0, blue: 225.0/255.0, alpha: 1.0)
- let foregroundColor = UIColor.darkText
- configureTheme(backgroundColor: backgroundColor, foregroundColor: foregroundColor, iconImage: iconImage)
+ backgroundColor = UIColor {
+ switch $0.userInterfaceStyle {
+ case .dark, .unspecified: return UIColor(red: 125/255.0, green: 125/255.0, blue: 125/255.0, alpha: 1.0)
+ case .light: fallthrough
+ @unknown default:
+ return defaultBackgroundColor
+ }
+ }
+ foregroundColor = .label
case .success:
- let backgroundColor = UIColor(red: 97.0/255.0, green: 161.0/255.0, blue: 23.0/255.0, alpha: 1.0)
- let foregroundColor = UIColor.white
- configureTheme(backgroundColor: backgroundColor, foregroundColor: foregroundColor, iconImage: iconImage)
+ backgroundColor = UIColor {
+ switch $0.userInterfaceStyle {
+ case .dark, .unspecified: return UIColor(red: 55/255.0, green: 122/255.0, blue: 0/255.0, alpha: 1.0)
+ case .light: fallthrough
+ @unknown default:
+ return defaultBackgroundColor
+ }
+ }
+ foregroundColor = .white
case .warning:
- let backgroundColor = UIColor(red: 238.0/255.0, green: 189.0/255.0, blue: 34.0/255.0, alpha: 1.0)
- let foregroundColor = UIColor.white
- configureTheme(backgroundColor: backgroundColor, foregroundColor: foregroundColor, iconImage: iconImage)
+ backgroundColor = UIColor {
+ switch $0.userInterfaceStyle {
+ case .dark, .unspecified: return UIColor(red: 239/255.0, green: 184/255.0, blue: 10/255.0, alpha: 1.0)
+ case .light: fallthrough
+ @unknown default:
+ return defaultBackgroundColor
+ }
+ }
+ foregroundColor = .white
case .error:
- let backgroundColor = UIColor(red: 249.0/255.0, green: 66.0/255.0, blue: 47.0/255.0, alpha: 1.0)
- let foregroundColor = UIColor.white
- configureTheme(backgroundColor: backgroundColor, foregroundColor: foregroundColor, iconImage: iconImage)
+ backgroundColor = UIColor {
+ switch $0.userInterfaceStyle {
+ case .dark, .unspecified: return UIColor(red: 195/255.0, green: 12/255.0, blue: 12/255.0, alpha: 1.0)
+ case .light: fallthrough
+ @unknown default:
+ return defaultBackgroundColor
+ }
+ }
+ foregroundColor = .white
}
+ configureTheme(backgroundColor: backgroundColor, foregroundColor: foregroundColor, iconImage: iconImage)
}
/**
diff --git a/SwiftMessages/MessageViewConvertible.swift b/SwiftMessages/MessageViewConvertible.swift
new file mode 100644
index 00000000..6dc168bf
--- /dev/null
+++ b/SwiftMessages/MessageViewConvertible.swift
@@ -0,0 +1,16 @@
+//
+// MessageViewConvertible.swift
+// SwiftUIDemo
+//
+// Created by Timothy Moose on 10/5/23.
+//
+
+import SwiftUI
+
+@available(iOS 14.0, *)
+/// A protocol used to display a SwiftUI message view using the `swiftMessage()` modifier.
+public protocol MessageViewConvertible: Equatable, Identifiable {
+ associatedtype Content: View
+ func asMessageView() -> Content
+}
+
diff --git a/SwiftMessages/PhysicsAnimation.swift b/SwiftMessages/PhysicsAnimation.swift
index 0fb976c5..ef8476f7 100644
--- a/SwiftMessages/PhysicsAnimation.swift
+++ b/SwiftMessages/PhysicsAnimation.swift
@@ -8,6 +8,7 @@
import UIKit
+@MainActor
public class PhysicsAnimation: NSObject, Animator {
public enum Placement {
@@ -36,7 +37,12 @@ public class PhysicsAnimation: NSObject, Animator {
}
public func show(context: AnimationContext, completion: @escaping AnimationCompletion) {
- NotificationCenter.default.addObserver(self, selector: #selector(adjustMargins), name: UIDevice.orientationDidChangeNotification, object: nil)
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(adjustMargins),
+ name: UIDevice.orientationDidChangeNotification,
+ object: nil
+ )
install(context: context)
showAnimation(context: context, completion: completion)
}
@@ -55,12 +61,24 @@ public class PhysicsAnimation: NSObject, Animator {
view.transform = CGAffineTransform.identity
completion(true)
}
- UIView.animate(withDuration: hideDuration, delay: 0, options: [.beginFromCurrentState, .curveEaseIn, .allowUserInteraction], animations: {
- view.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
- }, completion: nil)
- UIView.animate(withDuration: hideDuration, delay: 0, options: [.beginFromCurrentState, .curveEaseIn, .allowUserInteraction], animations: {
- view.alpha = 0
- }, completion: nil)
+ UIView.animate(
+ withDuration: hideDuration,
+ delay: 0,
+ options: [.beginFromCurrentState, .curveEaseIn, .allowUserInteraction],
+ animations: {
+ view.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
+ },
+ completion: nil
+ )
+ UIView.animate(
+ withDuration: hideDuration,
+ delay: 0,
+ options: [.beginFromCurrentState, .curveEaseIn, .allowUserInteraction],
+ animations: {
+ view.alpha = 0
+ },
+ completion: nil
+ )
CATransaction.commit()
}
@@ -74,14 +92,31 @@ public class PhysicsAnimation: NSObject, Animator {
container.addSubview(view)
switch placement {
case .center:
- view.centerYAnchor.constraint(equalTo: container.centerYAnchor).with(priority: UILayoutPriority(200)).isActive = true
+ view.centerYAnchor.constraint(
+ equalTo: container.centerYAnchor
+ )
+ .with(priority: UILayoutPriority(200))
+ .isActive = true
case .top:
- view.topAnchor.constraint(equalTo: container.topAnchor).with(priority: UILayoutPriority(200)).isActive = true
+ view.topAnchor.constraint(
+ equalTo: container.topAnchor
+ )
+ .with(priority: UILayoutPriority(200))
+ .isActive = true
case .bottom:
- view.bottomAnchor.constraint(equalTo: container.bottomAnchor).with(priority: UILayoutPriority(200)).isActive = true
+ view.bottomAnchor.constraint(
+ equalTo: container.bottomAnchor
+ )
+ .with(priority: UILayoutPriority(200))
+ .isActive = true
}
- NSLayoutConstraint(item: view, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 0).isActive = true
- NSLayoutConstraint(item: view, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
+ NSLayoutConstraint.activate([
+ view.leadingAnchor.constraint(equalTo: container.leadingAnchor),
+ view.trailingAnchor.constraint(equalTo: container.trailingAnchor),
+ // Don't allow the message to spill outside of the top or bottom of the container.
+ view.topAnchor.constraint(greaterThanOrEqualTo: container.topAnchor),
+ view.bottomAnchor.constraint(lessThanOrEqualTo: container.bottomAnchor),
+ ])
// Important to layout now in order to get the right safe area insets
container.layoutIfNeeded()
adjustMargins()
@@ -93,9 +128,7 @@ public class PhysicsAnimation: NSObject, Animator {
guard let adjustable = messageView as? MarginAdjustable & UIView,
let context = context else { return }
adjustable.preservesSuperviewLayoutMargins = false
- if #available(iOS 11, *) {
- adjustable.insetsLayoutMarginsFromSafeArea = false
- }
+ adjustable.insetsLayoutMarginsFromSafeArea = false
adjustable.layoutMargins = adjustable.defaultMarginAdjustment(context: context)
}
diff --git a/SwiftMessages/PhysicsPanHandler.swift b/SwiftMessages/PhysicsPanHandler.swift
index da82fc18..86187974 100644
--- a/SwiftMessages/PhysicsPanHandler.swift
+++ b/SwiftMessages/PhysicsPanHandler.swift
@@ -8,6 +8,7 @@
import UIKit
+@MainActor
open class PhysicsPanHandler {
public var hideDelay: TimeInterval = 0.2
@@ -17,6 +18,7 @@ open class PhysicsPanHandler {
var time: CFAbsoluteTime
}
+ @MainActor
public final class State {
weak var messageView: UIView?
@@ -97,7 +99,7 @@ open class PhysicsPanHandler {
return pan
}()
- func configure(context: AnimationContext, animator: Animator) {
+ public func configure(context: AnimationContext, animator: Animator) {
if let oldView = (messageView as? BackgroundViewable)?.backgroundView ?? messageView {
oldView.removeGestureRecognizer(pan)
}
@@ -127,7 +129,8 @@ open class PhysicsPanHandler {
let frame = containerView.convert(view.bounds, from: view)
if !containerView.bounds.intersects(frame) {
self.isOffScreen = true
- DispatchQueue.main.asyncAfter(deadline: .now() + self.hideDelay) {
+ Task {
+ try? await Task.sleep(seconds: self.hideDelay)
animator.delegate?.hide(animator: animator)
}
}
diff --git a/SwiftMessages/Presenter.swift b/SwiftMessages/Presenter.swift
index 11243f54..2010f22b 100644
--- a/SwiftMessages/Presenter.swift
+++ b/SwiftMessages/Presenter.swift
@@ -8,12 +8,14 @@
import UIKit
+@MainActor
protocol PresenterDelegate: AnimationDelegate {
func hide(presenter: Presenter)
}
+@MainActor
class Presenter: NSObject {
-
+
// MARK: - API
init(config: SwiftMessages.Config, view: UIView, delegate: PresenterDelegate) {
@@ -68,8 +70,16 @@ class Presenter: NSObject {
return duration
}
+ /// Detects the scenario where the view was shown, but the containing view heirarchy was removed before the view
+ /// was hidden. This unusual scenario could result in the message queue being blocked because the presented
+ /// view was not properly hidden by SwiftMessages. `isOrphaned` allows the queuing logic to unblock the queue.
+ var isOrphaned: Bool {
+ return installed && view.window == nil
+ }
+
// MARK: - Constants
+ @MainActor
enum PresentationContext {
case viewController(_: Weak)
case view(_: Weak)
@@ -97,7 +107,7 @@ class Presenter: NSObject {
private weak var delegate: PresenterDelegate?
private var presentationContext = PresentationContext.viewController(Weak(value: nil))
-
+ private var installed = false
private var interactivelyHidden = false;
// MARK: - Showing and hiding
@@ -119,7 +129,13 @@ class Presenter: NSObject {
func show(completion: @escaping AnimationCompletion) throws {
try presentationContext = getPresentationContext()
install()
- self.config.eventListeners.forEach { $0(.willShow) }
+ self.config.eventListeners.forEach { $0(.willShow(self.view)) }
+ switch (self.view as? HapticMessage)?.defaultHaptic ?? config.haptic {
+ case .error?: UINotificationFeedbackGenerator().notificationOccurred(.error)
+ case .warning?: UINotificationFeedbackGenerator().notificationOccurred(.warning)
+ case .success?: UINotificationFeedbackGenerator().notificationOccurred(.success)
+ default: break
+ }
showAnimation() { completed in
completion(completed)
if completed {
@@ -128,7 +144,7 @@ class Presenter: NSObject {
} else {
self.showAccessibilityAnnouncement()
}
- self.config.eventListeners.forEach { $0(.didShow) }
+ self.config.eventListeners.forEach { $0(.didShow(self.view)) }
}
}
}
@@ -181,7 +197,7 @@ class Presenter: NSObject {
func hide(animated: Bool, completion: @escaping AnimationCompletion) {
isHiding = true
- self.config.eventListeners.forEach { $0(.willHide) }
+ self.config.eventListeners.forEach { $0(.willHide(self.view)) }
let context = animationContext()
let action = {
if let viewController = self.presentationContext.viewControllerValue() as? WindowViewController {
@@ -189,7 +205,7 @@ class Presenter: NSObject {
}
self.maskingView.removeFromSuperview()
completion(true)
- self.config.eventListeners.forEach { $0(.didHide) }
+ self.config.eventListeners.forEach { $0(.didHide(self.view)) }
}
guard animated else {
action()
@@ -229,7 +245,7 @@ class Presenter: NSObject {
}
private func safeZoneConflicts() -> SafeZoneConflicts {
- guard let window = maskingView.window else { return [] }
+ guard let _ = maskingView.window else { return [] }
let windowLevel: UIWindow.Level = {
if let vc = presentationContext.viewControllerValue() as? WindowViewController {
return vc.config.windowLevel ?? .normal
@@ -246,47 +262,34 @@ class Presenter: NSObject {
if let vc = presentationContext.viewControllerValue() as? UITabBarController { return vc.sm_isVisible(view: vc.tabBar) }
return false
}()
- if #available(iOS 11, *) {
- if windowLevel > .normal {
- // TODO seeing `maskingView.safeAreaInsets.top` value of 20 on
- // iPhone 8 with status bar window level. This seems like an iOS bug since
- // the message view's window is above the status bar. Applying a special rule
- // to allow the animator to revove this amount from the layout margins if needed.
- // This may need to be reworked if any future device has a legitimate 20pt top safe area,
- // such as with a potentially smaller notch.
- if maskingView.safeAreaInsets.top == 20 {
- return [.overStatusBar]
- } else {
- var conflicts: SafeZoneConflicts = []
- if maskingView.safeAreaInsets.top > 0 {
- conflicts.formUnion(.sensorNotch)
- }
- if maskingView.safeAreaInsets.bottom > 0 {
- conflicts.formUnion(.homeIndicator)
- }
- return conflicts
+ if windowLevel > .normal {
+ // TODO seeing `maskingView.safeAreaInsets.top` value of 20 on
+ // iPhone 8 with status bar window level. This seems like an iOS bug since
+ // the message view's window is above the status bar. Applying a special rule
+ // to allow the animator to revove this amount from the layout margins if needed.
+ // This may need to be reworked if any future device has a legitimate 20pt top safe area,
+ // such as with a potentially smaller notch.
+ if maskingView.safeAreaInsets.top == 20 {
+ return [.overStatusBar]
+ } else {
+ var conflicts: SafeZoneConflicts = []
+ if maskingView.safeAreaInsets.top > 0 {
+ conflicts.formUnion(.sensorNotch)
}
+ if maskingView.safeAreaInsets.bottom > 0 {
+ conflicts.formUnion(.homeIndicator)
+ }
+ return conflicts
}
- var conflicts: SafeZoneConflicts = []
- if !underNavigationBar {
- conflicts.formUnion(.sensorNotch)
- }
- if !underTabBar {
- conflicts.formUnion(.homeIndicator)
- }
- return conflicts
- } else {
- #if SWIFTMESSAGES_APP_EXTENSIONS
- return []
- #else
- if UIApplication.shared.isStatusBarHidden { return [] }
- if (windowLevel > UIWindow.Level.normal) || underNavigationBar { return [] }
- let statusBarFrame = UIApplication.shared.statusBarFrame
- let statusBarWindowFrame = window.convert(statusBarFrame, from: nil)
- let statusBarViewFrame = maskingView.convert(statusBarWindowFrame, from: nil)
- return statusBarViewFrame.intersects(maskingView.bounds) ? SafeZoneConflicts.statusBar : []
- #endif
}
+ var conflicts: SafeZoneConflicts = []
+ if !underNavigationBar {
+ conflicts.formUnion(.sensorNotch)
+ }
+ if !underTabBar {
+ conflicts.formUnion(.homeIndicator)
+ }
+ return conflicts
}
private func getPresentationContext() throws -> PresentationContext {
@@ -412,6 +415,7 @@ class Presenter: NSObject {
maskingView.accessibleElements = elements
}
+ installed = true
guard let containerView = presentationContext.viewValue() else { return }
(presentationContext.viewControllerValue() as? WindowViewController)?.install()
installMaskingView(containerView: containerView)
diff --git a/SwiftMessages/SwiftMessageModifier.swift b/SwiftMessages/SwiftMessageModifier.swift
new file mode 100644
index 00000000..e44d6871
--- /dev/null
+++ b/SwiftMessages/SwiftMessageModifier.swift
@@ -0,0 +1,133 @@
+//
+// SwiftMessageModifier.swift
+// SwiftUIDemo
+//
+// Created by Timothy Moose on 10/5/23.
+//
+
+import SwiftUI
+
+@available(iOS 14.0, *)
+public extension View {
+ /// A view modifier for displaying a message using similar semantics to the `.sheet()` modifier.
+ func swiftMessage(
+ message: Binding,
+ config: SwiftMessages.Config? = nil,
+ swiftMessages: SwiftMessages? = nil,
+ @ViewBuilder messageContent: @escaping (Message) -> MessageContent
+ ) -> some View where Message: Equatable & Identifiable, MessageContent: View {
+ swiftMessage(message: message, config: config, swiftMessages: swiftMessages) { message, _ in
+ messageContent(message)
+ }
+ }
+
+ /// A view modifier for displaying a message using similar semantics to the `.sheet()` modifier. This variant provides a
+ /// `SwiftMessageGeometryProxy`. The proxy is useful when one needs to know the geometry metrics of the container view,
+ /// particularly because `GeometryReader` doesn't work inside the view builder due to the way the message view is being
+ /// displayed from UIKit.
+ func swiftMessage(
+ message: Binding,
+ config: SwiftMessages.Config? = nil,
+ swiftMessages: SwiftMessages? = nil,
+ @ViewBuilder messageContent: @escaping (Message, MessageGeometryProxy) -> MessageContent
+ ) -> some View where Message: Equatable & Identifiable, MessageContent: View {
+ modifier(
+ SwiftMessageModifier(
+ message: message,
+ config: config,
+ swiftMessages: swiftMessages,
+ messageContent: messageContent
+ )
+ )
+ }
+
+ /// A state-based modifier for displaying a message when `Message` conforms to `MessageViewConvertible`. This variant should be used if the message
+ /// view can be represented as pure data. If the message requires a delegate, has callbacks, etc., consider using the variant that takes a message view builder.
+ func swiftMessage(
+ message: Binding,
+ config: SwiftMessages.Config? = nil,
+ swiftMessages: SwiftMessages? = nil
+ ) -> some View where Message: MessageViewConvertible {
+ swiftMessage(message: message, config: config, swiftMessages: swiftMessages) { content in
+ content.asMessageView()
+ }
+ }
+}
+
+@available(iOS 14.0, *)
+private struct SwiftMessageModifier: ViewModifier where Message: Equatable & Identifiable, MessageContent: View {
+
+ // MARK: - API
+
+ fileprivate init(
+ message: Binding,
+ config: SwiftMessages.Config? = nil,
+ swiftMessages: SwiftMessages? = nil,
+ @ViewBuilder messageContent: @escaping (Message) -> MessageContent
+ ) {
+ _message = message
+ self.config = config
+ self.swiftMessages = swiftMessages
+ self.messageContent = { message, _ in
+ messageContent(message)
+ }
+ }
+
+ fileprivate init(
+ message: Binding,
+ config: SwiftMessages.Config? = nil,
+ swiftMessages: SwiftMessages? = nil,
+ @ViewBuilder messageContent: @escaping (Message, MessageGeometryProxy) -> MessageContent
+ ) {
+ _message = message
+ self.config = config
+ self.swiftMessages = swiftMessages
+ self.messageContent = messageContent
+ }
+
+ fileprivate init(
+ message: Binding,
+ config: SwiftMessages.Config? = nil,
+ swiftMessages: SwiftMessages? = nil
+ ) where Message: MessageViewConvertible, Message.Content == MessageContent {
+ _message = message
+ self.config = config
+ self.swiftMessages = swiftMessages
+ self.messageContent = { message, _ in
+ message.asMessageView()
+ }
+ }
+
+ // MARK: - Constants
+
+ // MARK: - Variables
+
+ @Binding private var message: Message?
+ private let config: SwiftMessages.Config?
+ private let swiftMessages: SwiftMessages?
+ @ViewBuilder private let messageContent: (Message, MessageGeometryProxy) -> MessageContent
+
+ // MARK: - Body
+
+ func body(content: Content) -> some View {
+ content
+ .onChange(of: message) { message in
+ let show: @MainActor (SwiftMessages.Config, UIView) -> Void = swiftMessages?.show(config:view:) ?? SwiftMessages.show(config:view:)
+ let hideAll: @MainActor () -> Void = swiftMessages?.hideAll ?? SwiftMessages.hideAll
+ switch message {
+ case let message?:
+ let view = MessageHostingView(message: message, content: messageContent)
+ var config = config ?? swiftMessages?.defaultConfig ?? SwiftMessages.defaultConfig
+ config.eventListeners.append { event in
+ if case .didHide = event, event.id == self.message?.id {
+ self.message = nil
+ }
+ }
+ hideAll()
+ show(config, view)
+ case .none:
+ hideAll()
+ }
+ }
+ }
+}
diff --git a/SwiftMessages/SwiftMessages.Config+Extensions.swift b/SwiftMessages/SwiftMessages.Config+Extensions.swift
index 9682b5b9..a17a9f22 100644
--- a/SwiftMessages/SwiftMessages.Config+Extensions.swift
+++ b/SwiftMessages/SwiftMessages.Config+Extensions.swift
@@ -17,10 +17,10 @@ extension SwiftMessages.Config {
}
}
- @available (iOS 13.0, *)
+ @available(iOS 13.0, *)
var windowScene: UIWindowScene? {
switch presentationContext {
- case .windowScene(let scene, _): return scene
+ case .windowScene(let scene, _): return scene as? UIWindowScene
default:
#if SWIFTMESSAGES_APP_EXTENSIONS
return nil
diff --git a/SwiftMessages/SwiftMessages.swift b/SwiftMessages/SwiftMessages.swift
index dfae859c..f05ad494 100644
--- a/SwiftMessages/SwiftMessages.swift
+++ b/SwiftMessages/SwiftMessages.swift
@@ -15,6 +15,7 @@ private let globalInstance = SwiftMessages()
It behaves like a queue, only showing one message at a time. Message views that
adopt the `Identifiable` protocol (as `MessageView` does) will have duplicates removed.
*/
+@MainActor
open class SwiftMessages {
/**
@@ -77,10 +78,10 @@ open class SwiftMessages {
of any message view that adopts the `MarginInsetting` protocol (as `MessageView` does)
to account for the status bar. As of iOS 13, windows can no longer cover the
status bar. The only alternative is to set `Config.prefersStatusBarHidden = true`
- to hide it.
+ to hide it. The `WindowScene` protocol works around the change in Xcode 13 that prevents
+ using `@availability` attribute with `enum` cases containing associated values.
*/
- @available(iOS 13.0, *)
- case windowScene(_: UIWindowScene, windowLevel: UIWindow.Level)
+ case windowScene(_: WindowScene, windowLevel: UIWindow.Level)
/**
Displays the message view under navigation bars and tab bars if an
@@ -151,6 +152,19 @@ open class SwiftMessages {
case indefinite(delay: TimeInterval, minimum: TimeInterval)
}
+ /**
+ Specifies notification's haptic feedback to be used on `MessageView` display
+ */
+
+ /**
+ Specifies an optional haptic feedback to be used on `MessageView` display
+ */
+ public enum Haptic {
+ case success
+ case warning
+ case error
+ }
+
/**
Specifies options for dimming the background behind the message view
similar to a popover view controller.
@@ -219,10 +233,23 @@ open class SwiftMessages {
Specifies events in the message lifecycle.
*/
public enum Event {
- case willShow
- case didShow
- case willHide
- case didHide
+ case willShow(UIView)
+ case didShow(UIView)
+ case willHide(UIView)
+ case didHide(UIView)
+
+ public var view: UIView {
+ switch self {
+ case .willShow(let view): return view
+ case .didShow(let view): return view
+ case .willHide(let view): return view
+ case .didHide(let view): return view
+ }
+ }
+
+ public var id: String? {
+ return (view as? Identifiable)?.id
+ }
}
/**
@@ -262,6 +289,13 @@ open class SwiftMessages {
*/
public var dimMode = DimMode.none
+
+ /**
+ Specifies notification's haptic feedback to be played on `MessageView` display.
+ No default value is provided.
+ */
+ public var haptic: Haptic? = nil
+
/**
Specifies whether or not the interactive pan-to-hide gesture is enabled
on the message view. For views that implement the `BackgroundViewable`
@@ -359,13 +393,18 @@ open class SwiftMessages {
Supply an instance of `KeyboardTrackingView` to have the message view avoid the keyboard.
*/
public var keyboardTrackingView: KeyboardTrackingView?
+
+ /**
+ Specify a positive or negative priority to influence the position of a message in the queue based on it's relative priority.
+ */
+ public var priority: Int = 0
}
/**
Not much to say here.
*/
- public init() {}
-
+ nonisolated public init() {}
+
/**
Adds the given configuration and view to the message queue to be displayed.
@@ -374,9 +413,7 @@ open class SwiftMessages {
*/
open func show(config: Config, view: UIView) {
let presenter = Presenter(config: config, view: view, delegate: self)
- messageQueue.sync {
- enqueue(presenter: presenter)
- }
+ enqueue(presenter: presenter)
}
/**
@@ -403,11 +440,11 @@ open class SwiftMessages {
- Parameter config: The configuration options.
- Parameter viewProvider: A block that returns the view to be displayed.
*/
- open func show(config: Config, viewProvider: @escaping ViewProvider) {
- DispatchQueue.main.async { [weak self] in
- guard let strongSelf = self else { return }
+ nonisolated open func show(config: Config, viewProvider: @escaping ViewProvider) {
+ Task { @MainActor [weak self] in
+ guard let self else { return }
let view = viewProvider()
- strongSelf.show(config: config, view: view)
+ self.show(config: config, view: view)
}
}
@@ -429,9 +466,7 @@ open class SwiftMessages {
Hide the current message being displayed by animating it away.
*/
open func hide(animated: Bool = true) {
- messageQueue.sync {
- hideCurrent(animated: animated)
- }
+ hideCurrent(animated: animated)
}
/**
@@ -439,12 +474,10 @@ open class SwiftMessages {
clear the message queue.
*/
open func hideAll() {
- messageQueue.sync {
- queue.removeAll()
- delays.removeAll()
- counts.removeAll()
- hideCurrent()
- }
+ queue.removeAll()
+ delays.removeAll()
+ counts.removeAll()
+ hideCurrent()
}
/**
@@ -454,14 +487,12 @@ open class SwiftMessages {
- Parameter id: The identifier of the message to remove.
*/
open func hide(id: String) {
- messageQueue.sync {
- if id == _current?.id {
- hideCurrent()
- }
- queue = queue.filter { $0.id != id }
- delays.remove(id: id)
- counts[id] = nil
+ if id == _current?.id {
+ hideCurrent()
}
+ queue = queue.filter { $0.id != id }
+ delays.remove(id: id)
+ counts[id] = nil
}
/**
@@ -470,21 +501,19 @@ open class SwiftMessages {
shown from multiple code paths to ensure that all paths are ready to hide.
*/
open func hideCounted(id: String) {
- messageQueue.sync {
- if let count = counts[id] {
- if count < 2 {
- counts[id] = nil
- } else {
- counts[id] = count - 1
- return
- }
- }
- if id == _current?.id {
- hideCurrent()
+ if let count = counts[id] {
+ if count < 2 {
+ counts[id] = nil
+ } else {
+ counts[id] = count - 1
+ return
}
- queue = queue.filter { $0.id != id }
- delays.remove(id: id)
}
+ if id == _current?.id {
+ hideCurrent()
+ }
+ queue = queue.filter { $0.id != id }
+ delays.remove(id: id)
}
/**
@@ -516,6 +545,7 @@ open class SwiftMessages {
open var pauseBetweenMessages: TimeInterval = 0.5
/// Type for keeping track of delayed presentations
+ @MainActor
fileprivate class Delays {
fileprivate func add(presenter: Presenter) {
@@ -541,20 +571,17 @@ open class SwiftMessages {
}
func show(presenter: Presenter) {
- messageQueue.sync {
- enqueue(presenter: presenter)
- }
+ enqueue(presenter: presenter)
}
- fileprivate let messageQueue = DispatchQueue(label: "it.swiftkick.SwiftMessages", attributes: [])
fileprivate var queue: [Presenter] = []
fileprivate var delays = Delays()
fileprivate var counts: [String : Int] = [:]
fileprivate var _current: Presenter? = nil {
didSet {
if oldValue != nil {
- let delayTime = DispatchTime.now() + pauseBetweenMessages
- messageQueue.asyncAfter(deadline: delayTime) { [weak self] in
+ Task { [weak self] in
+ try? await Task.sleep(seconds: self?.pauseBetweenMessages ?? 0)
self?.dequeueNext()
}
}
@@ -564,7 +591,10 @@ open class SwiftMessages {
fileprivate func enqueue(presenter: Presenter) {
if presenter.config.ignoreDuplicates {
counts[presenter.id] = (counts[presenter.id] ?? 0) + 1
- if _current?.id == presenter.id && _current?.isHiding == false { return }
+ if let _current,
+ _current.id == presenter.id,
+ !_current.isHiding,
+ !_current.isOrphaned { return }
if queue.filter({ $0.id == presenter.id }).count > 0 { return }
}
func doEnqueue() {
@@ -573,9 +603,10 @@ open class SwiftMessages {
}
if let delay = presenter.delayShow {
delays.add(presenter: presenter)
- messageQueue.asyncAfter(deadline: .now() + delay) { [weak self] in
+ Task { [weak self] in
+ try? await Task.sleep(seconds: delay)
// Don't enqueue if the view has been hidden during the delay window.
- guard let strongSelf = self, strongSelf.delays.remove(presenter: presenter) else { return }
+ guard let self, self.delays.remove(presenter: presenter) else { return }
doEnqueue()
}
} else {
@@ -584,7 +615,12 @@ open class SwiftMessages {
}
fileprivate func dequeueNext() {
- guard self._current == nil, queue.count > 0 else { return }
+ guard queue.count > 0 else { return }
+ if let _current, !_current.isOrphaned { return }
+ // Sort by priority
+ queue = queue.sorted { left, right in
+ left.config.priority > right.config.priority
+ }
let current = queue.removeFirst()
self._current = current
// Set `autohideToken` before the animation starts in case
@@ -592,26 +628,19 @@ open class SwiftMessages {
// block on animation completion.
self.autohideToken = current
current.showDate = CACurrentMediaTime()
- DispatchQueue.main.async { [weak self] in
- guard let strongSelf = self else { return }
- do {
- try current.show { completed in
- guard let strongSelf = self else { return }
- guard completed else {
- strongSelf.messageQueue.sync {
- strongSelf.internalHide(presenter: current)
- }
- return
- }
- if current === strongSelf.autohideToken {
- strongSelf.queueAutoHide()
- }
+ do {
+ try current.show { [weak self] completed in
+ guard let self else { return }
+ guard completed else {
+ self.internalHide(presenter: current)
+ return
}
- } catch {
- strongSelf.messageQueue.sync {
- strongSelf._current = nil
+ if current === self.autohideToken {
+ self.queueAutoHide()
}
}
+ } catch {
+ _current = nil
}
}
@@ -628,38 +657,39 @@ open class SwiftMessages {
guard let current = _current, !current.isHiding else { return }
let action = { [weak self] in
current.hide(animated: animated) { (completed) in
- guard completed, let strongSelf = self else { return }
- strongSelf.messageQueue.sync {
- guard strongSelf._current === current else { return }
- strongSelf.counts[current.id] = nil
- strongSelf._current = nil
- }
+ guard completed, let self else { return }
+ guard self._current === current else { return }
+ self.counts[current.id] = nil
+ self._current = nil
}
}
let delay = current.delayHide ?? 0
- DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
+ Task {
+ try? await Task.sleep(seconds: delay)
action()
}
}
- fileprivate weak var autohideToken: AnyObject?
+ fileprivate weak var autohideToken: Presenter?
fileprivate func queueAutoHide() {
guard let current = _current else { return }
autohideToken = current
if let pauseDuration = current.pauseDuration {
- let delayTime = DispatchTime.now() + pauseDuration
- messageQueue.asyncAfter(deadline: delayTime, execute: {
+ Task { [weak self] in
+ try? await Task.sleep(seconds: pauseDuration)
// Make sure we've still got a green light to auto-hide.
- if self.autohideToken !== current { return }
+ guard let self, self.autohideToken == current else { return }
self.internalHide(presenter: current)
- })
+ }
}
}
deinit {
- // Prevent orphaned messages
- hideCurrent()
+ guard let current = _current else { return }
+ Task { @MainActor [current] in
+ current.hide(animated: true) { _ in }
+ }
}
}
@@ -675,11 +705,7 @@ extension SwiftMessages {
- Returns: The view of type `T` if it is currently being shown or hidden.
*/
public func current() -> T? {
- var view: T?
- messageQueue.sync {
- view = _current?.view as? T
- }
- return view
+ _current?.view as? T
}
/**
@@ -689,13 +715,7 @@ extension SwiftMessages {
- Returns: The view with matching id if currently being shown or hidden.
*/
public func current(id: String) -> T? {
- var view: T?
- messageQueue.sync {
- if let current = _current, current.id == id {
- view = current.view as? T
- }
- }
- return view
+ _current?.id == id ? _current?.view as? T : nil
}
/**
@@ -705,13 +725,7 @@ extension SwiftMessages {
- Returns: The view with matching id if currently queued to be shown.
*/
public func queued(id: String) -> T? {
- var view: T?
- messageQueue.sync {
- if let queued = queue.first(where: { $0.id == id }) {
- view = queued.view as? T
- }
- }
- return view
+ queue.first { $0.id == id }?.view as? T
}
/**
@@ -733,16 +747,12 @@ extension SwiftMessages {
extension SwiftMessages: PresenterDelegate {
func hide(presenter: Presenter) {
- messageQueue.sync {
- self.internalHide(presenter: presenter)
- }
+ self.internalHide(presenter: presenter)
}
public func hide(animator: Animator) {
- messageQueue.sync {
- guard let presenter = self.presenter(forAnimator: animator) else { return }
- self.internalHide(presenter: presenter)
- }
+ guard let presenter = self.presenter(forAnimator: animator) else { return }
+ self.internalHide(presenter: presenter)
}
public func panStarted(animator: Animator) {
@@ -864,10 +874,10 @@ extension SwiftMessages {
a set of static APIs that wrap calls to this instance. For example, `SwiftMessages.show()`
is equivalent to `SwiftMessages.sharedInstance.show()`.
*/
- public static var sharedInstance: SwiftMessages {
+ nonisolated public static var sharedInstance: SwiftMessages {
return globalInstance
}
-
+
public static func show(viewProvider: @escaping ViewProvider) {
globalInstance.show(viewProvider: viewProvider)
}
diff --git a/SwiftMessages/SwiftMessagesSegue.swift b/SwiftMessages/SwiftMessagesSegue.swift
index d4cba417..35687e14 100644
--- a/SwiftMessages/SwiftMessagesSegue.swift
+++ b/SwiftMessages/SwiftMessagesSegue.swift
@@ -208,6 +208,7 @@ open class SwiftMessagesSegue: UIStoryboardSegue {
override open func perform() {
(source as? WindowViewController)?.install()
selfRetainer = self
+ startReleaseMonitor()
if overrideModalPresentationStyle {
destination.modalPresentationStyle = .custom
}
@@ -222,6 +223,20 @@ open class SwiftMessagesSegue: UIStoryboardSegue {
}
fileprivate let safeAreaWorkaroundViewController = UIViewController()
+
+ /// The self-retainer will not allow the segue, presenting and presented view controllers to be released if the presenting view controller
+ /// is removed without first dismissing. This monitor handles that scenario by setting `self.selfRetainer = nil` if
+ /// the presenting view controller is no longer in the heirarchy.
+ private func startReleaseMonitor() {
+ Task { @MainActor [weak self] in
+ try? await Task.sleep(seconds: 2)
+ guard let self = self else { return }
+ switch self.source.view.window {
+ case .none: self.selfRetainer = nil
+ case .some: self.startReleaseMonitor()
+ }
+ }
+ }
}
extension SwiftMessagesSegue {
@@ -288,12 +303,13 @@ extension SwiftMessagesSegue {
extension SwiftMessagesSegue: UIViewControllerTransitioningDelegate {
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let shower = TransitioningPresenter(segue: self)
- messenger.defaultConfig.eventListeners.append { [unowned self] in
+ let hider = self.hider
+ messenger.defaultConfig.eventListeners.append { [weak self] in
switch $0 {
case .didShow:
shower.completeTransition?(true)
case .didHide:
- if let completeTransition = self.hider.completeTransition {
+ if let completeTransition = hider.completeTransition {
completeTransition(true)
} else {
// Case where message is internally hidden by SwiftMessages, such as with a
@@ -301,7 +317,7 @@ extension SwiftMessagesSegue: UIViewControllerTransitioningDelegate {
source.dismiss(animated: false, completion: nil)
}
(source as? WindowViewController)?.uninstall()
- self.selfRetainer = nil
+ self?.selfRetainer = nil
default: break
}
}
@@ -333,14 +349,6 @@ extension SwiftMessagesSegue {
transitionContext.completeTransition(false)
return
}
- if #available(iOS 12, *) {}
- else if #available(iOS 11.0, *) {
- // This works around a bug in iOS 11 where the safe area of `messageView` (
- // and all ancestor views) is not set except on iPhone X. By assigning `messageView`
- // to a view controller, its safe area is set consistently. This bug has been resolved as
- // of Xcode 10 beta 2.
- segue.safeAreaWorkaroundViewController.view = segue.presenter.maskingView
- }
completeTransition = transitionContext.completeTransition
let transitionContainer = transitionContext.containerView
toView.translatesAutoresizingMaskIntoConstraints = false
diff --git a/SwiftMessages/Task+Extensions.swift b/SwiftMessages/Task+Extensions.swift
new file mode 100644
index 00000000..9d6f1c3f
--- /dev/null
+++ b/SwiftMessages/Task+Extensions.swift
@@ -0,0 +1,15 @@
+//
+// Task+Extensions.swift
+// SwiftMessages
+//
+// Created by Timothy Moose on 12/3/23.
+// Copyright © 2023 SwiftKick Mobile. All rights reserved.
+//
+
+import Foundation
+
+extension Task where Success == Never, Failure == Never {
+ static func sleep(seconds: TimeInterval) async throws {
+ try await sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
+ }
+}
diff --git a/SwiftMessages/TopBottomAnimation.swift b/SwiftMessages/TopBottomAnimation.swift
index d9f1f3a4..49a45adc 100644
--- a/SwiftMessages/TopBottomAnimation.swift
+++ b/SwiftMessages/TopBottomAnimation.swift
@@ -8,28 +8,24 @@
import UIKit
+@MainActor
public class TopBottomAnimation: NSObject, Animator {
- public enum Style {
- case top
- case bottom
- }
-
public weak var delegate: AnimationDelegate?
- public let style: Style
+ public let style: TopBottomAnimationStyle
- open var showDuration: TimeInterval = 0.4
+ public var showDuration: TimeInterval = 0.4
- open var hideDuration: TimeInterval = 0.2
+ public var hideDuration: TimeInterval = 0.2
- open var springDamping: CGFloat = 0.8
+ public var springDamping: CGFloat = 0.8
- open var closeSpeedThreshold: CGFloat = 750.0;
+ public var closeSpeedThreshold: CGFloat = 750.0;
- open var closePercentThreshold: CGFloat = 0.33;
+ public var closePercentThreshold: CGFloat = 0.33;
- open var closeAbsoluteThreshold: CGFloat = 75.0;
+ public var closeAbsoluteThreshold: CGFloat = 75.0;
public private(set) lazy var panGestureRecognizer: UIPanGestureRecognizer = {
let pan = UIPanGestureRecognizer()
@@ -41,11 +37,11 @@ public class TopBottomAnimation: NSObject, Animator {
weak var containerView: UIView?
var context: AnimationContext?
- public init(style: Style) {
+ public init(style: TopBottomAnimationStyle) {
self.style = style
}
- init(style: Style, delegate: AnimationDelegate) {
+ init(style: TopBottomAnimationStyle, delegate: AnimationDelegate) {
self.style = style
self.delegate = delegate
}
@@ -130,9 +126,7 @@ public class TopBottomAnimation: NSObject, Animator {
guard let adjustable = messageView as? MarginAdjustable & UIView,
let context = context else { return }
adjustable.preservesSuperviewLayoutMargins = false
- if #available(iOS 11, *) {
- adjustable.insetsLayoutMarginsFromSafeArea = false
- }
+ adjustable.insetsLayoutMarginsFromSafeArea = false
var layoutMargins = adjustable.defaultMarginAdjustment(context: context)
switch style {
case .top:
diff --git a/SwiftMessages/TopBottomAnimationStyle.swift b/SwiftMessages/TopBottomAnimationStyle.swift
new file mode 100644
index 00000000..626ad09c
--- /dev/null
+++ b/SwiftMessages/TopBottomAnimationStyle.swift
@@ -0,0 +1,12 @@
+//
+// TopBottomAnimationStyle.swift
+// SwiftMessages
+//
+// Created by Timothy Moose on 6/23/24.
+// Copyright © 2024 SwiftKick Mobile. All rights reserved.
+//
+
+public enum TopBottomAnimationStyle {
+ case top
+ case bottom
+}
diff --git a/SwiftMessages/TopBottomPresentable.swift b/SwiftMessages/TopBottomPresentable.swift
new file mode 100644
index 00000000..18804160
--- /dev/null
+++ b/SwiftMessages/TopBottomPresentable.swift
@@ -0,0 +1,49 @@
+//
+// File.swift
+//
+//
+// Created by Julien Di Marco on 23/04/2024.
+//
+
+import Foundation
+
+// MARK: - TopBottom Presentable Definition
+
+@MainActor
+protocol TopBottomPresentable {
+ var topBottomStyle: TopBottomAnimationStyle? { get }
+}
+
+// MARK: - TopBottom Presentable Conformances
+
+extension TopBottomAnimation: TopBottomPresentable {
+ var topBottomStyle: TopBottomAnimationStyle? { return style }
+}
+
+extension PhysicsAnimation: TopBottomPresentable {
+ var topBottomStyle: TopBottomAnimationStyle? {
+ switch placement {
+ case .top: return .top
+ case .bottom: return .bottom
+ default: return nil
+ }
+ }
+}
+
+// MARK: - Presentation Style Convenience
+
+extension SwiftMessages.PresentationStyle {
+ /// A temporary workaround to allow custom presentation contexts using `TopBottomAnimation`
+ /// to display properly behind bars. THe long term solution is to refactor all of the
+ /// presentation context logic to work with safe area insets.
+ @MainActor
+ var topBottomStyle: TopBottomAnimationStyle? {
+ switch self {
+ case .top: return .top
+ case .bottom: return .bottom
+ case .custom(let animator as TopBottomPresentable): return animator.topBottomStyle
+ case .center: return nil
+ default: return nil
+ }
+ }
+}
diff --git a/SwiftMessages/UIViewController+Extensions.swift b/SwiftMessages/UIViewController+Extensions.swift
index 7fbd4dd1..1400e03c 100644
--- a/SwiftMessages/UIViewController+Extensions.swift
+++ b/SwiftMessages/UIViewController+Extensions.swift
@@ -85,17 +85,3 @@ extension UIViewController {
return true
}
}
-
-extension SwiftMessages.PresentationStyle {
- /// A temporary workaround to allow custom presentation contexts using `TopBottomAnimation`
- /// to display properly behind bars. THe long term solution is to refactor all of the
- /// presentation context logic to work with safe area insets.
- var topBottomStyle: TopBottomAnimation.Style? {
- switch self {
- case .top: return .top
- case .bottom: return .bottom
- case .custom(let animator): return (animator as? TopBottomAnimation)?.style
- case .center: return nil
- }
- }
-}
diff --git a/SwiftMessages/UIWindow+Extensions.swift b/SwiftMessages/UIWindow+Extensions.swift
index d98c6caf..6a626f4e 100644
--- a/SwiftMessages/UIWindow+Extensions.swift
+++ b/SwiftMessages/UIWindow+Extensions.swift
@@ -11,16 +11,24 @@ import UIKit
extension UIWindow {
#if !SWIFTMESSAGES_APP_EXTENSIONS
static var keyWindow: UIWindow? {
- if #available(iOS 13.0, *) {
- return UIApplication.shared.connectedScenes
- .filter { $0.activationState == .foregroundActive }
- .compactMap { $0 as? UIWindowScene }
- .first?.windows
- .filter { $0.isKeyWindow }
- .first
- } else {
- return UIApplication.shared.keyWindow
- }
+ return UIApplication.shared.connectedScenes
+ .sorted { $0.activationState.sortPriority < $1.activationState.sortPriority }
+ .compactMap { $0 as? UIWindowScene }
+ .compactMap { $0.windows.first { $0.isKeyWindow } }
+ .first
}
#endif
}
+
+@available(iOS 13.0, *)
+private extension UIScene.ActivationState {
+ var sortPriority: Int {
+ switch self {
+ case .foregroundActive: return 1
+ case .foregroundInactive: return 2
+ case .background: return 3
+ case .unattached: return 4
+ @unknown default: return 5
+ }
+ }
+}
diff --git a/SwiftMessages/WindowScene.swift b/SwiftMessages/WindowScene.swift
new file mode 100644
index 00000000..a3bbb4ca
--- /dev/null
+++ b/SwiftMessages/WindowScene.swift
@@ -0,0 +1,9 @@
+import Foundation
+import UIKit
+
+/// A workaround for the change in Xcode 13 that prevents using `@availability` attribute
+/// with `enum` cases containing associated values.
+public protocol WindowScene {}
+
+@available(iOS 13.0, *)
+extension UIWindowScene: WindowScene {}
diff --git a/SwiftMessages/WindowViewController.swift b/SwiftMessages/WindowViewController.swift
index 96b30095..30d77f9a 100644
--- a/SwiftMessages/WindowViewController.swift
+++ b/SwiftMessages/WindowViewController.swift
@@ -27,24 +27,18 @@ open class WindowViewController: UIViewController
self.view = view
window.rootViewController = self
window.windowLevel = config.windowLevel ?? UIWindow.Level.normal
- if #available(iOS 13, *) {
- window.overrideUserInterfaceStyle = config.overrideUserInterfaceStyle
- }
+ window.overrideUserInterfaceStyle = config.overrideUserInterfaceStyle
}
func install() {
- if #available(iOS 13, *) {
- window?.windowScene = config.windowScene
- #if !SWIFTMESSAGES_APP_EXTENSIONS
- previousKeyWindow = UIWindow.keyWindow
- #endif
- show(
- becomeKey: config.shouldBecomeKeyWindow,
- frame: config.windowScene?.coordinateSpace.bounds
- )
- } else {
- show(becomeKey: config.shouldBecomeKeyWindow)
- }
+ window?.windowScene = config.windowScene
+ #if !SWIFTMESSAGES_APP_EXTENSIONS
+ previousKeyWindow = UIWindow.keyWindow
+ #endif
+ show(
+ becomeKey: config.shouldBecomeKeyWindow,
+ frame: config.windowScene?.coordinateSpace.bounds
+ )
}
private func show(becomeKey: Bool, frame: CGRect? = nil) {
@@ -59,11 +53,9 @@ open class WindowViewController: UIViewController
func uninstall() {
if window?.isKeyWindow == true {
- previousKeyWindow?.makeKeyAndVisible()
- }
- if #available(iOS 13, *) {
- window?.windowScene = nil
+ previousKeyWindow?.makeKey()
}
+ window?.windowScene = nil
window?.isHidden = true
window = nil
}
diff --git a/SwiftUIDemo/SwiftUIDemo.xcodeproj/project.pbxproj b/SwiftUIDemo/SwiftUIDemo.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..f1519c40
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo.xcodeproj/project.pbxproj
@@ -0,0 +1,446 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 56;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 22549DC02B55CFE8005E3E21 /* DemoMessageWithButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22549DBF2B55CFE8005E3E21 /* DemoMessageWithButtonView.swift */; };
+ 228F7DAD2ACF17E8006C9644 /* SwiftUIDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228F7DAC2ACF17E8006C9644 /* SwiftUIDemoApp.swift */; };
+ 228F7DAF2ACF17E8006C9644 /* DemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228F7DAE2ACF17E8006C9644 /* DemoView.swift */; };
+ 228F7DB12ACF17E9006C9644 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 228F7DB02ACF17E9006C9644 /* Assets.xcassets */; };
+ 228F7DB42ACF17E9006C9644 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 228F7DB32ACF17E9006C9644 /* Preview Assets.xcassets */; };
+ 228F7DC82ACF1E63006C9644 /* SwiftMessages.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 228F7DC32ACF1E1E006C9644 /* SwiftMessages.framework */; };
+ 228F7DC92ACF1E63006C9644 /* SwiftMessages.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 228F7DC32ACF1E1E006C9644 /* SwiftMessages.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 228F7DD52ACF59E4006C9644 /* DemoMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228F7DD42ACF59E4006C9644 /* DemoMessage.swift */; };
+ 228F7DD72ACF5C2E006C9644 /* DemoMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228F7DD62ACF5C2E006C9644 /* DemoMessageView.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 228F7DC22ACF1E1E006C9644 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 228F7DBD2ACF1E1E006C9644 /* SwiftMessages.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 86B48AEC1D5A41C900063E2B;
+ remoteInfo = SwiftMessages;
+ };
+ 228F7DC42ACF1E1E006C9644 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 228F7DBD2ACF1E1E006C9644 /* SwiftMessages.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 86B48AF51D5A41C900063E2B;
+ remoteInfo = SwiftMessagesTests;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 228F7DCA2ACF1E63006C9644 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 228F7DC92ACF1E63006C9644 /* SwiftMessages.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 22549DBF2B55CFE8005E3E21 /* DemoMessageWithButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoMessageWithButtonView.swift; sourceTree = ""; };
+ 228F7DA92ACF17E8006C9644 /* SwiftUIDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUIDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 228F7DAC2ACF17E8006C9644 /* SwiftUIDemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIDemoApp.swift; sourceTree = ""; };
+ 228F7DAE2ACF17E8006C9644 /* DemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoView.swift; sourceTree = ""; };
+ 228F7DB02ACF17E9006C9644 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 228F7DB32ACF17E9006C9644 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
+ 228F7DBB2ACF1DB5006C9644 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; };
+ 228F7DBD2ACF1E1E006C9644 /* SwiftMessages.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SwiftMessages.xcodeproj; path = ../SwiftMessages.xcodeproj; sourceTree = ""; };
+ 228F7DD42ACF59E4006C9644 /* DemoMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoMessage.swift; sourceTree = ""; };
+ 228F7DD62ACF5C2E006C9644 /* DemoMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoMessageView.swift; sourceTree = ""; };
+ 2291AA492AD1E3EC0084868E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; };
+ 2291AA4C2AD1E4520084868E /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 228F7DA62ACF17E8006C9644 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 228F7DC82ACF1E63006C9644 /* SwiftMessages.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 228F7DA02ACF17E8006C9644 = {
+ isa = PBXGroup;
+ children = (
+ 2291AA4C2AD1E4520084868E /* CHANGELOG.md */,
+ 2291AA492AD1E3EC0084868E /* README.md */,
+ 228F7DAB2ACF17E8006C9644 /* SwiftUIDemo */,
+ 228F7DAA2ACF17E8006C9644 /* Products */,
+ 228F7DC72ACF1E63006C9644 /* Frameworks */,
+ 228F7DBD2ACF1E1E006C9644 /* SwiftMessages.xcodeproj */,
+ );
+ sourceTree = "";
+ };
+ 228F7DAA2ACF17E8006C9644 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 228F7DA92ACF17E8006C9644 /* SwiftUIDemo.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 228F7DAB2ACF17E8006C9644 /* SwiftUIDemo */ = {
+ isa = PBXGroup;
+ children = (
+ 228F7DBB2ACF1DB5006C9644 /* Info.plist */,
+ 228F7DAC2ACF17E8006C9644 /* SwiftUIDemoApp.swift */,
+ 228F7DAE2ACF17E8006C9644 /* DemoView.swift */,
+ 228F7DD42ACF59E4006C9644 /* DemoMessage.swift */,
+ 228F7DD62ACF5C2E006C9644 /* DemoMessageView.swift */,
+ 22549DBF2B55CFE8005E3E21 /* DemoMessageWithButtonView.swift */,
+ 228F7DB02ACF17E9006C9644 /* Assets.xcassets */,
+ 228F7DB22ACF17E9006C9644 /* Preview Content */,
+ );
+ path = SwiftUIDemo;
+ sourceTree = "";
+ };
+ 228F7DB22ACF17E9006C9644 /* Preview Content */ = {
+ isa = PBXGroup;
+ children = (
+ 228F7DB32ACF17E9006C9644 /* Preview Assets.xcassets */,
+ );
+ path = "Preview Content";
+ sourceTree = "";
+ };
+ 228F7DBE2ACF1E1E006C9644 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 228F7DC32ACF1E1E006C9644 /* SwiftMessages.framework */,
+ 228F7DC52ACF1E1E006C9644 /* SwiftMessagesTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 228F7DC72ACF1E63006C9644 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 228F7DA82ACF17E8006C9644 /* SwiftUIDemo */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 228F7DB72ACF17E9006C9644 /* Build configuration list for PBXNativeTarget "SwiftUIDemo" */;
+ buildPhases = (
+ 228F7DA52ACF17E8006C9644 /* Sources */,
+ 228F7DA62ACF17E8006C9644 /* Frameworks */,
+ 228F7DA72ACF17E8006C9644 /* Resources */,
+ 228F7DCA2ACF1E63006C9644 /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = SwiftUIDemo;
+ productName = SwiftUIDemo;
+ productReference = 228F7DA92ACF17E8006C9644 /* SwiftUIDemo.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 228F7DA12ACF17E8006C9644 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1500;
+ LastUpgradeCheck = 1500;
+ TargetAttributes = {
+ 228F7DA82ACF17E8006C9644 = {
+ CreatedOnToolsVersion = 15.0;
+ };
+ };
+ };
+ buildConfigurationList = 228F7DA42ACF17E8006C9644 /* Build configuration list for PBXProject "SwiftUIDemo" */;
+ compatibilityVersion = "Xcode 14.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 228F7DA02ACF17E8006C9644;
+ productRefGroup = 228F7DAA2ACF17E8006C9644 /* Products */;
+ projectDirPath = "";
+ projectReferences = (
+ {
+ ProductGroup = 228F7DBE2ACF1E1E006C9644 /* Products */;
+ ProjectRef = 228F7DBD2ACF1E1E006C9644 /* SwiftMessages.xcodeproj */;
+ },
+ );
+ projectRoot = "";
+ targets = (
+ 228F7DA82ACF17E8006C9644 /* SwiftUIDemo */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+ 228F7DC32ACF1E1E006C9644 /* SwiftMessages.framework */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.framework;
+ path = SwiftMessages.framework;
+ remoteRef = 228F7DC22ACF1E1E006C9644 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 228F7DC52ACF1E1E006C9644 /* SwiftMessagesTests.xctest */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.cfbundle;
+ path = SwiftMessagesTests.xctest;
+ remoteRef = 228F7DC42ACF1E1E006C9644 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 228F7DA72ACF17E8006C9644 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 228F7DB42ACF17E9006C9644 /* Preview Assets.xcassets in Resources */,
+ 228F7DB12ACF17E9006C9644 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 228F7DA52ACF17E8006C9644 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 228F7DD52ACF59E4006C9644 /* DemoMessage.swift in Sources */,
+ 228F7DD72ACF5C2E006C9644 /* DemoMessageView.swift in Sources */,
+ 228F7DAF2ACF17E8006C9644 /* DemoView.swift in Sources */,
+ 228F7DAD2ACF17E8006C9644 /* SwiftUIDemoApp.swift in Sources */,
+ 22549DC02B55CFE8005E3E21 /* DemoMessageWithButtonView.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 228F7DB52ACF17E9006C9644 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 228F7DB62ACF17E9006C9644 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 228F7DB82ACF17E9006C9644 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "\"SwiftUIDemo/Preview Content\"";
+ DEVELOPMENT_TEAM = "";
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = SwiftUIDemo/Info.plist;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.swiftkickmobile.SwiftMessages.SwiftUIDemo;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 228F7DB92ACF17E9006C9644 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "\"SwiftUIDemo/Preview Content\"";
+ DEVELOPMENT_TEAM = "";
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = SwiftUIDemo/Info.plist;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.swiftkickmobile.SwiftMessages.SwiftUIDemo;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 228F7DA42ACF17E8006C9644 /* Build configuration list for PBXProject "SwiftUIDemo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 228F7DB52ACF17E9006C9644 /* Debug */,
+ 228F7DB62ACF17E9006C9644 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 228F7DB72ACF17E9006C9644 /* Build configuration list for PBXNativeTarget "SwiftUIDemo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 228F7DB82ACF17E9006C9644 /* Debug */,
+ 228F7DB92ACF17E9006C9644 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 228F7DA12ACF17E8006C9644 /* Project object */;
+}
diff --git a/SwiftUIDemo/SwiftUIDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SwiftUIDemo/SwiftUIDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/SwiftUIDemo/SwiftUIDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftUIDemo/SwiftUIDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/AccentColor.colorset/Contents.json b/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000..eb878970
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..13613e3e
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,13 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/Contents.json b/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/Demo message background.colorset/Contents.json b/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/Demo message background.colorset/Contents.json
new file mode 100644
index 00000000..15b79d47
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/Assets.xcassets/Demo message background.colorset/Contents.json
@@ -0,0 +1,20 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xEB",
+ "green" : "0xE1",
+ "red" : "0xAC"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SwiftUIDemo/SwiftUIDemo/DemoMessage.swift b/SwiftUIDemo/SwiftUIDemo/DemoMessage.swift
new file mode 100644
index 00000000..32624de1
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/DemoMessage.swift
@@ -0,0 +1,23 @@
+//
+// DemoMessage.swift
+// SwiftUIDemo
+//
+// Created by Timothy Moose on 10/5/23.
+//
+
+import SwiftUI
+import SwiftMessages
+
+struct DemoMessage: Identifiable {
+ let title: String
+ let body: String
+ let style: DemoMessageView.Style
+
+ var id: String { title + body }
+}
+
+extension DemoMessage: MessageViewConvertible {
+ func asMessageView() -> DemoMessageView {
+ DemoMessageView(message: self, style: style)
+ }
+}
diff --git a/SwiftUIDemo/SwiftUIDemo/DemoMessageView.swift b/SwiftUIDemo/SwiftUIDemo/DemoMessageView.swift
new file mode 100644
index 00000000..11cf9611
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/DemoMessageView.swift
@@ -0,0 +1,70 @@
+//
+// DemoMessageView.swift
+// SwiftUIDemo
+//
+// Created by Timothy Moose on 10/5/23.
+//
+
+import SwiftUI
+
+// A message view with a title and message.
+struct DemoMessageView: View {
+
+ // MARK: - API
+
+ enum Style {
+ case standard
+ case card
+ case tab
+ }
+
+ let message: DemoMessage
+ let style: Style
+
+
+ // MARK: - Variables
+
+ // MARK: - Constants
+
+ // MARK: - Body
+
+ var body: some View {
+ switch style {
+ case .standard:
+ content()
+ // Mask the content and extend background into the safe area.
+ .mask {
+ Rectangle()
+ .edgesIgnoringSafeArea(.top)
+ }
+ case .card:
+ content()
+ // Mask the content with a rounded rectangle
+ .mask {
+ RoundedRectangle(cornerRadius: 15)
+ }
+ // External padding around the card
+ .padding(10)
+ case .tab:
+ content()
+ // Mask the content with rounded bottom edge and extend background into the safe area.
+ .mask {
+ UnevenRoundedRectangle(bottomLeadingRadius: 15, bottomTrailingRadius: 15)
+ .edgesIgnoringSafeArea(.top)
+ }
+ }
+ }
+
+ @ViewBuilder private func content() -> some View {
+ VStack(alignment: .leading) {
+ Text(message.title).font(.system(size: 20, weight: .bold))
+ Text(message.body)
+ }
+ .multilineTextAlignment(.leading)
+ // Internal padding of the card
+ .padding(30)
+ // Greedy width
+ .frame(maxWidth: .infinity)
+ .background(.demoMessageBackground)
+ }
+}
diff --git a/SwiftUIDemo/SwiftUIDemo/DemoMessageWithButtonView.swift b/SwiftUIDemo/SwiftUIDemo/DemoMessageWithButtonView.swift
new file mode 100644
index 00000000..e74172ef
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/DemoMessageWithButtonView.swift
@@ -0,0 +1,77 @@
+//
+// DemoMessageWithButtonView.swift
+// SwiftUIDemo
+//
+// Created by Timothy Moose on 1/15/24.
+//
+
+import SwiftUI
+
+// A message view with a title, message and button.
+struct DemoMessageWithButtonView: View where Button: View {
+
+ // MARK: - API
+
+ enum Style {
+ case standard
+ case card
+ case tab
+ }
+
+ init(message: DemoMessage, style: Style, @ViewBuilder button: @escaping () -> Button) {
+ self.message = message
+ self.style = style
+ self.button = button
+ }
+
+ // MARK: - Variables
+
+ let message: DemoMessage
+ let style: Style
+ @ViewBuilder let button: () -> Button
+
+ // MARK: - Constants
+
+ // MARK: - Body
+
+ var body: some View {
+ switch style {
+ case .standard:
+ content()
+ // Mask the content and extend background into the safe area.
+ .mask {
+ Rectangle()
+ .edgesIgnoringSafeArea(.top)
+ }
+ case .card:
+ content()
+ // Mask the content with a rounded rectangle
+ .mask {
+ RoundedRectangle(cornerRadius: 15)
+ }
+ // External padding around the card
+ .padding(10)
+ case .tab:
+ content()
+ // Mask the content with rounded bottom edge and extend background into the safe area.
+ .mask {
+ UnevenRoundedRectangle(bottomLeadingRadius: 15, bottomTrailingRadius: 15)
+ .edgesIgnoringSafeArea(.top)
+ }
+ }
+ }
+
+ @ViewBuilder private func content() -> some View {
+ VStack() {
+ Text(message.title).font(.system(size: 20, weight: .bold))
+ Text(message.body)
+ button()
+ }
+ .multilineTextAlignment(.center)
+ // Internal padding of the card
+ .padding(30)
+ // Greedy width
+ .frame(maxWidth: .infinity)
+ .background(.demoMessageBackground)
+ }
+}
diff --git a/SwiftUIDemo/SwiftUIDemo/DemoView.swift b/SwiftUIDemo/SwiftUIDemo/DemoView.swift
new file mode 100644
index 00000000..b6562cfe
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/DemoView.swift
@@ -0,0 +1,65 @@
+//
+// DemoView.swift
+// SwiftUIDemo
+//
+// Created by Timothy Moose on 10/5/23.
+//
+
+import SwiftUI
+import SwiftMessages
+
+struct DemoView: View {
+
+ /// Demonstrates purely data-driven message presentation.
+ @State var message: DemoMessage?
+
+ /// Demonstrates message presentation with a view builder.
+ @State var messageWithButton: DemoMessage?
+
+ var body: some View {
+ VStack {
+ Button("Show standard message") {
+ message = DemoMessage(
+ title: "Demo",
+ body: "This is a sample SwiftUI card-style message! This content should be long enough to wrap.",
+ style: .standard
+ )
+ }
+ Button("Show card message") {
+ message = DemoMessage(
+ title: "Demo",
+ body: "This is a sample SwiftUI card-style message! This content should be long enough to wrap.",
+ style: .card
+ )
+ }
+ Button("Show tab message") {
+ message = DemoMessage(
+ title: "Demo",
+ body: "This is a sample SwiftUI card-style message! This content should be long enough to wrap.",
+ style: .tab
+ )
+ }
+ Button("Show message with button") {
+ messageWithButton = DemoMessage(
+ title: "Demo",
+ body: "This message view has a button was constructed with a view builder.",
+ style: .card
+ )
+ }
+ }
+ .buttonStyle(.bordered)
+ .swiftMessage(message: $message)
+ .swiftMessage(message: $messageWithButton) { message in
+ DemoMessageWithButtonView(message: message, style: .card) {
+ Button("Tap Me") {
+ print("Tap")
+ }
+ .buttonStyle(.bordered)
+ }
+ }
+ }
+}
+
+#Preview {
+ DemoView()
+}
diff --git a/SwiftUIDemo/SwiftUIDemo/Info.plist b/SwiftUIDemo/SwiftUIDemo/Info.plist
new file mode 100644
index 00000000..0c67376e
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/Info.plist
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/SwiftUIDemo/SwiftUIDemo/Preview Content/Preview Assets.xcassets/Contents.json b/SwiftUIDemo/SwiftUIDemo/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/SwiftUIDemo/SwiftUIDemo/SwiftUIDemoApp.swift b/SwiftUIDemo/SwiftUIDemo/SwiftUIDemoApp.swift
new file mode 100644
index 00000000..fc444543
--- /dev/null
+++ b/SwiftUIDemo/SwiftUIDemo/SwiftUIDemoApp.swift
@@ -0,0 +1,17 @@
+//
+// SwiftUIDemoApp.swift
+// SwiftUIDemo
+//
+// Created by Timothy Moose on 10/5/23.
+//
+
+import SwiftUI
+
+@main
+struct SwiftUIDemoApp: App {
+ var body: some Scene {
+ WindowGroup {
+ DemoView()
+ }
+ }
+}
diff --git a/iMessageDemo/Podfile.lock b/iMessageDemo/Podfile.lock
index b7d82fb3..400dd99d 100644
--- a/iMessageDemo/Podfile.lock
+++ b/iMessageDemo/Podfile.lock
@@ -1,5 +1,5 @@
PODS:
- - SwiftMessages/AppExtension (9.0.3)
+ - SwiftMessages/AppExtension (10.0.1)
DEPENDENCIES:
- SwiftMessages/AppExtension (from `../`)
@@ -9,8 +9,8 @@ EXTERNAL SOURCES:
:path: "../"
SPEC CHECKSUMS:
- SwiftMessages: 077f19126c24033fe24042237ecc20261adb46e4
+ SwiftMessages: 759b4a0bf5c3a116a0d7e8a34b098ba83c458625
-PODFILE CHECKSUM: dde250cdcd60ccf6ec0a51da5a633d36b9f83a3b
+PODFILE CHECKSUM: 2eb9a33592d0c52131c37a9dd169a8c4604ffd7b
-COCOAPODS: 1.10.0
+COCOAPODS: 1.15.2
diff --git a/iMessageDemo/Pods/Local Podspecs/SwiftMessages.podspec.json b/iMessageDemo/Pods/Local Podspecs/SwiftMessages.podspec.json
index 6d9e93c9..a117b2dd 100644
--- a/iMessageDemo/Pods/Local Podspecs/SwiftMessages.podspec.json
+++ b/iMessageDemo/Pods/Local Podspecs/SwiftMessages.podspec.json
@@ -1,20 +1,20 @@
{
"name": "SwiftMessages",
- "version": "9.0.3",
+ "version": "10.0.1",
"license": {
"type": "MIT"
},
"homepage": "https://github.com/SwiftKickMobile/SwiftMessages",
"authors": {
- "Timothy Moose": "tim@swiftkick.it"
+ "Timothy Moose": "tim@swiftkickmobile.com"
},
"summary": "A very flexible message bar for iOS written in Swift.",
"source": {
"git": "https://github.com/SwiftKickMobile/SwiftMessages.git",
- "tag": "9.0.3"
+ "tag": "10.0.1"
},
"platforms": {
- "ios": "9.0"
+ "ios": "13.0"
},
"swift_versions": "5.0",
"frameworks": "UIKit",
diff --git a/iMessageDemo/Pods/Manifest.lock b/iMessageDemo/Pods/Manifest.lock
index b7d82fb3..400dd99d 100644
--- a/iMessageDemo/Pods/Manifest.lock
+++ b/iMessageDemo/Pods/Manifest.lock
@@ -1,5 +1,5 @@
PODS:
- - SwiftMessages/AppExtension (9.0.3)
+ - SwiftMessages/AppExtension (10.0.1)
DEPENDENCIES:
- SwiftMessages/AppExtension (from `../`)
@@ -9,8 +9,8 @@ EXTERNAL SOURCES:
:path: "../"
SPEC CHECKSUMS:
- SwiftMessages: 077f19126c24033fe24042237ecc20261adb46e4
+ SwiftMessages: 759b4a0bf5c3a116a0d7e8a34b098ba83c458625
-PODFILE CHECKSUM: dde250cdcd60ccf6ec0a51da5a633d36b9f83a3b
+PODFILE CHECKSUM: 2eb9a33592d0c52131c37a9dd169a8c4604ffd7b
-COCOAPODS: 1.10.0
+COCOAPODS: 1.15.2
diff --git a/iMessageDemo/Pods/Pods.xcodeproj/project.pbxproj b/iMessageDemo/Pods/Pods.xcodeproj/project.pbxproj
index 468092a5..cf4122eb 100644
--- a/iMessageDemo/Pods/Pods.xcodeproj/project.pbxproj
+++ b/iMessageDemo/Pods/Pods.xcodeproj/project.pbxproj
@@ -3,110 +3,119 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 50;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
- 00468531530F8A70E3D83622BD482026 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E6941E59EF0D89949E3DF3D89488430 /* Weak.swift */; };
- 0303F738260F2C9BAE20B79DE84E82BC /* PhysicsPanHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2337E6D5F621F3CE9A1E3761984EBE87 /* PhysicsPanHandler.swift */; };
- 0B42A04122166EAD384BCE37FD450FAF /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8901520225CE89F44E6DE88688F29C10 /* MessageView.swift */; };
- 0CE00BF7FB0F6376D89B0AFF1CFD7510 /* PassthroughView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C557E1DC95AFA417E94ED01301F9F2 /* PassthroughView.swift */; };
- 0E7AE1B3CE2734B39ACCE812B4320B44 /* MarginAdjustable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B91AD9D9E743D17D553D48103BE27C46 /* MarginAdjustable+Extensions.swift */; };
- 138A7742F76993FB9EE3555FD2808562 /* successIconLight.png in Resources */ = {isa = PBXBuildFile; fileRef = A80433B71162112A79043CB64261DB51 /* successIconLight.png */; };
- 16047C447B00FAA7F42764EC4167C33B /* NSLayoutConstraint+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0970322A5DD8B4A8373C35ED051DE156 /* NSLayoutConstraint+Extensions.swift */; };
- 1F4159921A25C7B2A0E9C587387D829C /* errorIconLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1B04615682E8B787C964824435BC6616 /* errorIconLight@2x.png */; };
- 1F6162906845BE72A5BCDAF14D6E14B9 /* warningIconSubtle.png in Resources */ = {isa = PBXBuildFile; fileRef = 4512DEEA328AB0AAA95D77C84C89E02C /* warningIconSubtle.png */; };
- 2181E713FC7C00EE871FDA6CB62C7E8C /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5607E285A85AC7163B0B8FD447FD27E /* Theme.swift */; };
- 2558CABB502ED605BC21DCFC55A9C0B7 /* MessageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = AE62AA801971C530345349D18AFCCB82 /* MessageView.xib */; };
- 2B23A9DFDAACFE9C0AD5EB6899E63475 /* infoIconSubtle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4FE466AC4123CDAE9FDA9FCF4FB9CC60 /* infoIconSubtle@2x.png */; };
- 33785D52B8888C2EA02BD0495408E352 /* successIconLight@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 72A865FFBAE49EF66A35CB9D709E8D7E /* successIconLight@3x.png */; };
- 3505AB28DBFA49FBE5C8250F3E067E60 /* KeyboardTrackingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536660614870EA0E051BF6BDDF495798 /* KeyboardTrackingView.swift */; };
- 365594AAFDD4EE947EB33E6E86A8578E /* errorIconSubtle.png in Resources */ = {isa = PBXBuildFile; fileRef = A850F76D7E3FEDC20FFB877454069171 /* errorIconSubtle.png */; };
- 425F2D4CE95436680D77C263FF15221B /* SwiftMessages_SwiftMessages.bundle in Resources */ = {isa = PBXBuildFile; fileRef = BEBF018059B0DFCAC8494ABD1C578AD9 /* SwiftMessages_SwiftMessages.bundle */; };
- 4267FACE20717FF3F51C2ACAB8C395A4 /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D0E8B0020635F606875DD02735C502 /* BaseView.swift */; };
- 44150A4B5B2D251FCBB6CA07DC9872B5 /* Animator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 267E35F9851AAE50DFB8FA0DCA7F2980 /* Animator.swift */; };
- 447E8A096C1ABD2E0AC9674E65A827E3 /* warningIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 0259757860B26C6072E2640B84EC6D45 /* warningIcon.png */; };
- 44C0F194D748EE88027414A5B2094E9B /* successIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C8155ED78358FA5CF39089176FBDE501 /* successIcon@3x.png */; };
- 461760E2818D72B948B60B4835E7B1ED /* NSBundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FCF161BD9F1C2CAAFBCADE5E59BD3FD /* NSBundle+Extensions.swift */; };
- 462CDC24C5C8DD6905C4112B6B4BD2ED /* successIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = FC73E6C784AF6CA1F956F57F82ED2803 /* successIcon.png */; };
- 46CCA4DDFDBBDD2A9426BB96C08E4255 /* Pods-iMessageExtensionDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F40CA14AD738DD186B4DA8FD14AE5BD /* Pods-iMessageExtensionDemo-dummy.m */; };
- 492F085489FCAB0EEBE74B098F7D3F4D /* StatusLine.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A6801849037A728E9BC50E06CE8AD2F /* StatusLine.xib */; };
- 4C78ED4E1780F5609E25CE03429C1DE4 /* errorIconLight@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 848251807107C20960A3DABAB27F7475 /* errorIconLight@3x.png */; };
- 4DB5D1FB08693DDDC32BCF19CC1B1AA0 /* warningIconLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 874D31DE863C88B1D699E1EBFBE0641B /* warningIconLight@2x.png */; };
- 4E703B2A80C64CF1142872BE31263940 /* errorIconLight.png in Resources */ = {isa = PBXBuildFile; fileRef = 0892E032AE12339D1AD84BDCC78A3C07 /* errorIconLight.png */; };
- 4E9CCFC43646B6CDBE3B787AB09A0147 /* CardView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 42F1E753E61B6EDED3908AC66994649C /* CardView.xib */; };
- 5260BD3289BAC54A20457099C4A57EFF /* errorIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9D1FEAC04417D847EDC10783E054988F /* errorIcon.png */; };
- 52F4ED7F78829270B2AFE5EDBA9EEE2F /* UIEdgeInsets+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A13E69643D651322647C28E3F9E9C9 /* UIEdgeInsets+Extensions.swift */; };
- 560A2B1056FEFE42AC6524A2A1742CA2 /* Pods-iMessageExtensionDemo-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CAB201AD00CAB811B045E2FFB5C03A8 /* Pods-iMessageExtensionDemo-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 57072960EA4F0D307171ED90697D3FAF /* TabView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B7FD0618783A3E6B90D3A3323633959F /* TabView.xib */; };
- 5720D965B3CE67653082137053FBEC9C /* infoIconLight.png in Resources */ = {isa = PBXBuildFile; fileRef = 9C110C924ED12D2D32ECC27503018A31 /* infoIconLight.png */; };
- 5750C24C3A9CAE11C7E36B37434912D0 /* MaskingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CF61FDEFE095F0486E9914F2262ADB9 /* MaskingView.swift */; };
- 5DF3F4808ED4A6932839C11A5D742B93 /* successIconSubtle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 54B278C80D821C120FA70ABB6CAF1F46 /* successIconSubtle@3x.png */; };
- 6320AE79D41E8D1F52AF66A670542561 /* CornerRoundingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F8D7902822221456B697BB41111E450 /* CornerRoundingView.swift */; };
- 64595C731B826EB19E9757D524E0BF76 /* warningIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2CFBBC25592C97C925B6F81B53BE57CB /* warningIcon@3x.png */; };
- 683A7897D1E9A73F4717198B1C054D29 /* errorIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 60268FE48AA4DC9FD5060B1E5CE68453 /* errorIcon@2x.png */; };
- 6A539682FDDA0E5DA23E1B5F2BA133C1 /* errorIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B6DEAADC09FEB1A8D5B90108103EE478 /* errorIcon@3x.png */; };
- 6C7DAA6A68AFDACD67F2C127CEF4DD6F /* UIWindow+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 397C8F928170138667B9326F9655D75D /* UIWindow+Extensions.swift */; };
- 755DA479621A9D2BB8B84540DE648A7A /* warningIconSubtle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 667DECE93ABCC6869A071FFEB0F83EA5 /* warningIconSubtle@3x.png */; };
- 7ACCA07CCFD868899D61F4B4AE5774DB /* infoIconSubtle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B7213B7DC5432DA2B272F25E17AA364 /* infoIconSubtle@3x.png */; };
- 7BC52E6F0D9D19B05E62E623E53FCE82 /* Pods-iMessageDemo-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D0ECE831FB5E0EE1D68E837671320C7 /* Pods-iMessageDemo-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 7E5ADF1F3B6849D5A0DF8E2B9C1861C5 /* PhysicsAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE3BED1799B2F867F5C984C2A051E36A /* PhysicsAnimation.swift */; };
- 8148CD8F2B38FB38B7B9CCC12E93ECFB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAB6F611E86A4758835A715E4B4184F6 /* Foundation.framework */; };
- 89ACEF0F9E524BD21D6C2460FEC375F8 /* warningIconSubtle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = ED86D7B42F55040DCE162653FB3C7EDB /* warningIconSubtle@2x.png */; };
- 966B9C1EE6B73E430F03D51A4FD26D20 /* MarginAdjustable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C56947CAF8EFCC7858E1E2F1273427 /* MarginAdjustable.swift */; };
- 9CEE0E569456D932AA34329D2038DF98 /* SwiftMessagesSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFFDB7DDC6B6B064B9D12281E2531BFA /* SwiftMessagesSegue.swift */; };
- 9E3D4CA932041E99B6FD56D4E79A726F /* SwiftMessages-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C614EECDDFE644AF0BF7CB16A3D74404 /* SwiftMessages-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 9FFE65CB6E825ECAE4838D53E7BA4C06 /* infoIconLight@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4412F313361EFEE7A0193533A5AC5999 /* infoIconLight@3x.png */; };
- A11E7A379B288CCA3AE6785B83FA4316 /* AccessibleMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDAF8A2C45FDDDFD5AFA59203137551 /* AccessibleMessage.swift */; };
- A1734DB0A8A558B2397AF54E63F64416 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8341EF04E2B20C2BF6D4AF2240F80A56 /* Error.swift */; };
- A9EB0C8E49AB748B05CF7941ACAF8475 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAB6F611E86A4758835A715E4B4184F6 /* Foundation.framework */; };
- AC4F13F50EB63B484292D67692BC1F9D /* successIconSubtle.png in Resources */ = {isa = PBXBuildFile; fileRef = 1DC54BB09EFDFEDE11F484FE73BEDC60 /* successIconSubtle.png */; };
- B2862C6DDAA6543BB2C8F4541F044564 /* SwiftMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3216CF40D770C387D45C3B4AF2CC9E0 /* SwiftMessages.swift */; };
- B2CC8FF0F9FF3FE929180CDB32B69F18 /* infoIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F7004673F1B51EEC365B32F0E060E564 /* infoIcon@2x.png */; };
- B42A7A38C8014DBDE2632B909F71C355 /* successIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 62755A788F910CA88887B6F63BBC545F /* successIcon@2x.png */; };
- B60094A8D1343B7351F0EF9C51F2F0DB /* infoIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 337B3108412E3812254B85BCC4F90EB8 /* infoIcon@3x.png */; };
- BB16D1E73A5D6B27DC4212926986107F /* warningIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BFEF746702215C33B51BEE64C4E48F0A /* warningIcon@2x.png */; };
- BBFE3BAFFCE67F4EACE0C67A7B7FFC3A /* successIconSubtle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0B3A169D83BDA3613A661845D1607FC0 /* successIconSubtle@2x.png */; };
- BC4618CE535404A9540D4D110B5767A1 /* TopBottomAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 396FCF260E9C2B3F11080A91E3D72334 /* TopBottomAnimation.swift */; };
- BDCF9C5E4F88B2B0AB6D4595E5A281E1 /* successIconLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F898B2AC5C3404C266BBCA3B6D22B5E4 /* successIconLight@2x.png */; };
- C384FB76A48C06F7581D0F7850F2F4F1 /* Pods-iMessageDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 14BF989232A1D55A0FDAAB70B5A8E1BF /* Pods-iMessageDemo-dummy.m */; };
- C3CDAED707B153A58674CB1AC4A33FB2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAB6F611E86A4758835A715E4B4184F6 /* Foundation.framework */; };
- C4598A458697C49C961BFDCD090B3A8F /* infoIconSubtle.png in Resources */ = {isa = PBXBuildFile; fileRef = 06F2F626BE8417F1806CC5B17F210C90 /* infoIconSubtle.png */; };
- C5FFE932D5EBE2CC8F2ABFEA893D8E9C /* infoIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 70E7AB099F856140EF93D5B94A967418 /* infoIcon.png */; };
- C6E73F201545CF5ED055C69CD4DB2EFF /* warningIconLight@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C17CFEF9B761A322945F74D86CA88036 /* warningIconLight@3x.png */; };
- C8F46E0A5853739D3F632B4828FDE9CC /* PassthroughWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382E6375390EBB09F829519F8ACCB7D7 /* PassthroughWindow.swift */; };
- CCF5CC8F6022DFD410DDCC99A90D58B0 /* warningIconLight.png in Resources */ = {isa = PBXBuildFile; fileRef = 79E38069364BC5DF4EA88F352E28B242 /* warningIconLight.png */; };
- D49E06426C51C49E9058371138972A69 /* CALayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B5EF40CEC1F6DB23F428DC159D4BC2 /* CALayer+Extensions.swift */; };
- D61D59BCDFD2C3C7993CFE883DE60692 /* infoIconLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6516EE9BFCB9C53C151359F9D0247562 /* infoIconLight@2x.png */; };
- D8BDC20F1566606BF64001B6E96B14B6 /* errorIconSubtle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 453BC098B6A052020C40DE576F684C7B /* errorIconSubtle@3x.png */; };
- D9077478C2FCD7C3DD1EBE9373281728 /* UIViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15C4C88778A19C193D1EC77FA77BAE4C /* UIViewController+Extensions.swift */; };
- E88597F65A00A5AF50EEF2ABA2392B2F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D245E0514AAC1A2B9A6D5EA2F383E90F /* UIKit.framework */; };
- E94E8711BDDA31B178AA032D3346C307 /* BackgroundViewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB58A9F688DD39112E6CDF20C8969C0 /* BackgroundViewable.swift */; };
- EDA146BF3FF59593677F8B2AA785D8A5 /* errorIconSubtle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3C313FB1D33EF4B477FD5D3D6179A8BE /* errorIconSubtle@2x.png */; };
- F27E3EEAFDDAA6CB4E7407D6A46DAA5D /* CenteredView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3DF091E68C4659506DB08B0876BD274C /* CenteredView.xib */; };
- F50FE2EE47422AD39AC8F7F115081E7E /* SwiftMessages.Config+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3C32E945AE3E6394EF85CD6BEC714B4 /* SwiftMessages.Config+Extensions.swift */; };
- F5642C087197B9033252FF10FBA92B59 /* WindowViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8566413BFA3FE6827E63E30515C9B3 /* WindowViewController.swift */; };
- FA6CA270F521DF68A75E527954A2DDAD /* SwiftMessages-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F9031B7FA82A17F04D4403091DF836 /* SwiftMessages-dummy.m */; };
- FCB6832EB4D32EF085E770E8B4A9BC2C /* Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0DE01AB7D0935933E7AFBDF2945814 /* Identifiable.swift */; };
- FF01BC94FADD5A72282AEE043DF523A7 /* Presenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A965DD26246DCA8CE39FBAB348ABA24 /* Presenter.swift */; };
+ 00A4A53E0D1EFC70DA1EFAEFA82C4BA1 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = 442F9FAEDF369CADB9E6E8FF3668FD95 /* Weak.swift */; };
+ 03C7FA1BE62CCD280D63F4C7CDDFB27F /* AccessibleMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E8A4D41E0A0E52C839F7197C4CE2A0 /* AccessibleMessage.swift */; };
+ 04BCE3C12FDE7E91C9EF1FE154E80E2B /* SwiftMessageModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19DACBEC8DD85FB850D299DFB7A0374 /* SwiftMessageModifier.swift */; };
+ 04C771EB4784B94AC6CA28ED6A919D50 /* WindowScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB9866F195EB0AED31A33AEC27B67C1 /* WindowScene.swift */; };
+ 096A6663757CB52C8A34B3E3146279C8 /* infoIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2CF5DC74916EBE02E70234620CD80150 /* infoIcon@2x.png */; };
+ 0ADA608E3B9307694DCF4D0BC426E7FE /* warningIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B241F1B5D295E7806363B802DF03A085 /* warningIcon@2x.png */; };
+ 0C093AF5F4D753325F4CE92F2F943CB4 /* MarginAdjustable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DF8F43552C4F1C4CAEFBAFD9A77004 /* MarginAdjustable.swift */; };
+ 0D53997CCCD141D2303E87C29CEFE211 /* MessageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A4D741EF8B40F5472CE2C1C1A34426BD /* MessageView.xib */; };
+ 1441D5A06DD6903562EB794FC1AE8822 /* TabView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C33B3DA756F19F5C93D498D557BEB820 /* TabView.xib */; };
+ 158EF18377B720081C31C2F2100DDA7A /* Presenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D18688EA04EA2F2BC48ED05D1FE5404 /* Presenter.swift */; };
+ 19C0F7897044E72D3A13DE703920FFE1 /* MessageHostingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F989D33CAC3BCDA9B3C4B18F23A4DBE /* MessageHostingView.swift */; };
+ 1E507663F3C402CBCA0C50A876F472EF /* errorIconLight@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D6A205E933052E98349DC710D78304CB /* errorIconLight@3x.png */; };
+ 205A52121D20436063240893A461B29C /* errorIconSubtle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 38D28C78FCAE6706F1294CEFA6B25F88 /* errorIconSubtle@2x.png */; };
+ 24DA4886290040A0EC88B63D82EF412D /* warningIconSubtle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E02B1554754BA1F8EF318D567875139 /* warningIconSubtle@3x.png */; };
+ 274D5DE28E0446655163A48E2272860A /* CornerRoundingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86872F4639B26435D20BED0CE7E35C7F /* CornerRoundingView.swift */; };
+ 28D6446B971F186BACDED83CD8DE4D44 /* SwiftMessages-SwiftMessages_SwiftMessages in Resources */ = {isa = PBXBuildFile; fileRef = BEBF018059B0DFCAC8494ABD1C578AD9 /* SwiftMessages-SwiftMessages_SwiftMessages */; };
+ 31660027252C457325491844F16AEBDD /* successIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 74793ADC8ADF7674EA6C3041E2B54354 /* successIcon.png */; };
+ 330C59E62D17601422EDB29513B9856C /* SwiftMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC10D7DB2D227D4AA8F41CDFDDC24F37 /* SwiftMessages.swift */; };
+ 3451B2775584DD75A3BDF9BF28A03F76 /* SwiftMessagesSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17EF282E0A0F329DDF17226E83706F8 /* SwiftMessagesSegue.swift */; };
+ 3479628DE3B6C9005316A7F591F5E68F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAB6F611E86A4758835A715E4B4184F6 /* Foundation.framework */; };
+ 360BEE76BBF40319E6638B3E027E1474 /* errorIconSubtle.png in Resources */ = {isa = PBXBuildFile; fileRef = EDFC2E756AAB98672C4B241097AAF0F6 /* errorIconSubtle.png */; };
+ 3613DBA9CBD7E092BD7DC1924E6648D2 /* infoIconLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = ACCCF5B75EA41D844ED936A92602C5B1 /* infoIconLight@2x.png */; };
+ 37A8DD40AF13BCABE7A3CD8EF15A4C53 /* successIconSubtle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D146564C0660B5F616D6736F6560E17C /* successIconSubtle@2x.png */; };
+ 3C566158256E9CC711B73810B2C64E3B /* KeyboardTrackingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A99EE79DBFA3C24A372DDB69162E36C /* KeyboardTrackingView.swift */; };
+ 3F53FCE80C716C7DE7A3B1D8D65077BC /* CALayer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35F5FB13175EC0E13D53B445CE31395A /* CALayer+Extensions.swift */; };
+ 422B422F962FD298F963F84934A21529 /* infoIconLight.png in Resources */ = {isa = PBXBuildFile; fileRef = C3701E97FDB85EFCE90E06D8CBC8ED41 /* infoIconLight.png */; };
+ 436D8C63FBE86FF8271ABC32F9A52A9D /* Pods-iMessageDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 14BF989232A1D55A0FDAAB70B5A8E1BF /* Pods-iMessageDemo-dummy.m */; };
+ 450919BA3A4D984DC24822CD80483419 /* TopBottomPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A102DE8896931D80E06F2C74C2B003AC /* TopBottomPresentable.swift */; };
+ 45A4B95F1F73413A3001B95E1F91D697 /* UIWindow+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBC808D0C98F682E01DC43B544ED093F /* UIWindow+Extensions.swift */; };
+ 4609D5EC365EABFE507781BF911B887B /* warningIconLight.png in Resources */ = {isa = PBXBuildFile; fileRef = 004EE347886E2355CCB352F2AE7A35D6 /* warningIconLight.png */; };
+ 469995B0FC2E3748F59D0E3A4445E197 /* CenteredView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 975A42F4C16B65B38769916EC5468D2A /* CenteredView.xib */; };
+ 47CC2A912B82A499DB2F5E2ADBE664CF /* HapticMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8BFA921A44ED7AEAA0E08BCE17EDDA /* HapticMessage.swift */; };
+ 47EFB870CC797D2F54396F0AEBC8F576 /* errorIconLight.png in Resources */ = {isa = PBXBuildFile; fileRef = EC387534FD34764D4CC28B276A500AD6 /* errorIconLight.png */; };
+ 48ADA8D4633776558E7D216434057A2A /* SwiftMessages.Config+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83D1CC131CDAC7CA2C5BFD8701E8A79 /* SwiftMessages.Config+Extensions.swift */; };
+ 4980F404C2B391C621CE2BCF05E08BDF /* errorIconSubtle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C12E87D0EA045A56FC0B45F072C74D2 /* errorIconSubtle@3x.png */; };
+ 4C9FED5D268E82325C17309FB8904C43 /* warningIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B8B9892D8D5596E09F02DA2D71BF60B4 /* warningIcon@3x.png */; };
+ 52A01853AC4F2D0E720579D7F8A531CF /* WindowViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9908774A62A95BF5BA8FE65842868043 /* WindowViewController.swift */; };
+ 60F02D519964FFEE8A21221FF5E0B0C5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAB6F611E86A4758835A715E4B4184F6 /* Foundation.framework */; };
+ 61F532D30833A9FF1DE00EC98BC018C2 /* successIconLight@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5686DFC1E0B24A3051EF376AD8A162C0 /* successIconLight@3x.png */; };
+ 634267873AC0F98140DBAFFF9A180BA8 /* MarginAdjustable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B602E4E64DAEF1468DD2F5C2C5DFD00 /* MarginAdjustable+Extensions.swift */; };
+ 6583C565BE5BB9E52213854AA85F5A93 /* warningIconSubtle.png in Resources */ = {isa = PBXBuildFile; fileRef = 6924A0A48B715EA35080ABB106B96BF7 /* warningIconSubtle.png */; };
+ 67DC4D170143131B8AFFB558FDD00465 /* SwiftMessages-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D091A06E439F734980F26F81E9161EF2 /* SwiftMessages-dummy.m */; };
+ 67F7D444EAC4475021B3458B21B2A162 /* Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88398B7A70E5B00495B670516D964701 /* Identifiable.swift */; };
+ 698F203577674AFA4BBCDE93B01B9EEC /* UIViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EB79F63EA99ADBBDA408015A4599BB /* UIViewController+Extensions.swift */; };
+ 6A04C3E1C3437ED6BC6F096302B458AB /* errorIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8605740B16E17342A29212D21C380249 /* errorIcon@2x.png */; };
+ 6CDE06668BB53C70BF1456F8EEE845D9 /* infoIconSubtle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C40BBCF3A77C5C7A01708634BB4275C3 /* infoIconSubtle@3x.png */; };
+ 6DC0B420F622E197919276CFF2C063CE /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790409B3FBA491A9262ACCB929F30104 /* BaseView.swift */; };
+ 72026C4424B7A1650CBEED98ED9AA288 /* successIconSubtle.png in Resources */ = {isa = PBXBuildFile; fileRef = 7B9CEE1B17C98E76723E7404C532515D /* successIconSubtle.png */; };
+ 744EA4AF597AFEFEE5B398434F47CA26 /* warningIconLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 71699C81494BC6585B862CDFC1CDAF54 /* warningIconLight@2x.png */; };
+ 7590FF543E2634A83059E509D5B05583 /* Pods-iMessageExtensionDemo-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CAB201AD00CAB811B045E2FFB5C03A8 /* Pods-iMessageExtensionDemo-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 79BB175F4750822409AB9ED0A9D90D09 /* successIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6EA088414C10E47808EA8490D9BBF64F /* successIcon@3x.png */; };
+ 81B1015F29547CAB449757FC5ABCAFAB /* warningIconLight@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5FE01208AD70F1B35201AB04E50A23EC /* warningIconLight@3x.png */; };
+ 88D024879AEAE247899BFB50C8516515 /* Pods-iMessageDemo-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D0ECE831FB5E0EE1D68E837671320C7 /* Pods-iMessageDemo-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8CB7CCDF07E1CE6838C7278BC19968B6 /* infoIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 79A086337846180B9A63D8492CCE45B4 /* infoIcon.png */; };
+ 8F63F1ABAB21FA99A6CB1A54D5F2B5AA /* infoIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 560722B244EB84DF745199247129C164 /* infoIcon@3x.png */; };
+ 942468E4C1AEFCF10D5043243B4007E7 /* NSBundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 073CD58B2E8E255484D239A01D707B81 /* NSBundle+Extensions.swift */; };
+ 9FBBA6E4D3A2A4CA4BA8535E35D54CA8 /* successIconLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 44DAC25C8D6D72F70BD1CF9099BDAF10 /* successIconLight@2x.png */; };
+ A4724FC3B4F49DCBF50D395257BB6B8E /* CardView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 03B5D12D54ACC75F929389CFF15E91FF /* CardView.xib */; };
+ A52191D65F4C725EBF61D27F40FEA970 /* infoIconLight@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88237113DAAD8ECF8D5E3FAA27D8A02F /* infoIconLight@3x.png */; };
+ A836F848C83565B2A0A06AB23D491946 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BBC4BF82FBB152E34F2D1EF36A0FB1 /* MessageView.swift */; };
+ A865FCE8718103C8E725EC1EC0F9F4F6 /* BackgroundViewable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86C1C9601BF8A5BC563062C310C0E3B5 /* BackgroundViewable.swift */; };
+ A8C4AD87F73E9C51F797F34BCFF5AA17 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAB6F611E86A4758835A715E4B4184F6 /* Foundation.framework */; };
+ A8EF6BE26B31A8713E37E0629F6466EB /* successIconLight.png in Resources */ = {isa = PBXBuildFile; fileRef = 252A5DF0FE3237691B8C50B969857389 /* successIconLight.png */; };
+ A90C06B459CC64A243DD1184024E5D22 /* infoIconSubtle.png in Resources */ = {isa = PBXBuildFile; fileRef = A6C7FF0D9E13571262231B72D0553E49 /* infoIconSubtle.png */; };
+ B04AA81A8A8CE0BCEFD5FAFD841F461F /* Task+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9610DCAAD9A55CDF73BAD54D523E1273 /* Task+Extensions.swift */; };
+ B1B4D240B65FFC5A7A981B9504C55270 /* NSLayoutConstraint+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882EF814D0A1EE2AB28BFEE5C067EE6F /* NSLayoutConstraint+Extensions.swift */; };
+ B22AF07D79D1A82DE583AC49E3D472D3 /* Pods-iMessageExtensionDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F40CA14AD738DD186B4DA8FD14AE5BD /* Pods-iMessageExtensionDemo-dummy.m */; };
+ B61EBE61C31E78140BF12D32A008A43B /* StatusLine.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62332870F1E2AE3FA16EE5ADC85C104A /* StatusLine.xib */; };
+ B646E1CD53F14E0DEE53740799C06AC5 /* SwiftMessages-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 080E269BABA7D072D8C51E29E9AC4A91 /* SwiftMessages-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ B6D0E2FA454A946D6C9A7494648F49D9 /* MessageGeometryProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28456F0421422D16D71D14D426309B3F /* MessageGeometryProxy.swift */; };
+ B9FDFA90A4FB68CA5357AB70427B4DCD /* TopBottomAnimationStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61DD3C66AC7ADE1E9CCFE7A4A0BEA59 /* TopBottomAnimationStyle.swift */; };
+ BA4779B82B35B85EFE9F595624BA3943 /* UIEdgeInsets+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16B5B2B5429B01345E4DA361DE01904D /* UIEdgeInsets+Extensions.swift */; };
+ BAB66573FF29F6337237A8DA0EB9E3E8 /* PassthroughWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0398CE267904AF6C2FD03B3766511ECF /* PassthroughWindow.swift */; };
+ BBF31491EF3411DCFA7E649645229E2A /* successIconSubtle@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 47A710678B44C50DA470D1BCF94B87B8 /* successIconSubtle@3x.png */; };
+ C2031E7EC3C60B876F2AC04253D3AFC4 /* MessageViewConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06DB4311BFB347093FC18777A503B461 /* MessageViewConvertible.swift */; };
+ CCBFBDB03B2444C911BADDE6A6BA08AD /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9414B7316138752779A4B9C7E5C988DB /* Error.swift */; };
+ D3B31A631C754CAECABFA4DC672D3D0B /* warningIconSubtle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 85C6BEF274CDB3807F9BD452A26C68E6 /* warningIconSubtle@2x.png */; };
+ D8DFC45149C57377B5C90B3457090564 /* warningIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = D47C1471CCFFBEF214DF9F0ECBDD408B /* warningIcon.png */; };
+ D9519DD580DF2E744AB4C4087FA28BCD /* Animator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F67BD26788FA0C70E942A7629E41C5E8 /* Animator.swift */; };
+ DC6C4E22FA8DD6FA491066A738132EF1 /* infoIconSubtle@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8026A87EEBF1A86A282563F974C4225C /* infoIconSubtle@2x.png */; };
+ DD406A0C002AC1D02A6E5794B1729C22 /* PassthroughView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A865E97261799942A6F29092D3D7AE /* PassthroughView.swift */; };
+ DE2531A67AEBFED61A060DF13790C3BE /* errorIcon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 91013DDC05BDB2787740B0C9E5444D23 /* errorIcon@3x.png */; };
+ E03C930AD56F078DCD1357E5C83AD67A /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32459A84426DD5E88256AD61F3D4E91 /* Theme.swift */; };
+ E4906D67B9F27E11EE9F8E8F5C847A14 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D245E0514AAC1A2B9A6D5EA2F383E90F /* UIKit.framework */; };
+ E54040398A9AD751393A2171DC64D70E /* errorIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 71D489FBDD84B41576199298F717BC08 /* errorIcon.png */; };
+ EE9A5A3877D99BFCA5DA70BA4E049CA9 /* successIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DBF06E9B2F93F081139B8A6A922F569 /* successIcon@2x.png */; };
+ F2D3ABB0C7B6EB5B531E4E68C40AA89C /* PhysicsPanHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEEED9FBEA6C5DB7EE3B95E7D8115B3 /* PhysicsPanHandler.swift */; };
+ F516490B5276943A352D597431B079B6 /* TopBottomAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F1B1264A726D334A72AB1CB001CA9F /* TopBottomAnimation.swift */; };
+ F667CC7C7F3285FC39427F2AAA8E7665 /* PhysicsAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D7E08E0434930866B59A3F17A3A577B /* PhysicsAnimation.swift */; };
+ F8D0D33FC1C833490057108C322E56CB /* MaskingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC4854845AC1848C056C36719A5D7420 /* MaskingView.swift */; };
+ FC0E2BA3CD9C6C3ACF3681841D8EF5BA /* errorIconLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C49B964EF7710D818E0AF0321724EEF4 /* errorIconLight@2x.png */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
- 141E78A8858DBF8B2695DFEDCBDF5158 /* PBXContainerItemProxy */ = {
+ 146D3A65FCE17293CC3AD6CBEAE15F9A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 1FC5E8328653C350899229BDF89FACE5;
remoteInfo = "SwiftMessages-SwiftMessages_SwiftMessages";
};
- 4DB1DDB02425E67ED85C70C3B138E205 /* PBXContainerItemProxy */ = {
+ 70E6358901408AED062E820735630F69 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
remoteGlobalIDString = DAB613A18652334F6BFC5F27BADF515D;
remoteInfo = SwiftMessages;
};
- 900F05D1477FFAB64FF410CBB6B9B74D /* PBXContainerItemProxy */ = {
+ D297C84AE0F13FB6A76F2F391D21D679 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */;
proxyType = 1;
@@ -116,150 +125,246 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
- 0259757860B26C6072E2640B84EC6D45 /* warningIcon.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = warningIcon.png; path = SwiftMessages/Resources/warningIcon.png; sourceTree = ""; };
- 06F2F626BE8417F1806CC5B17F210C90 /* infoIconSubtle.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = infoIconSubtle.png; path = SwiftMessages/Resources/infoIconSubtle.png; sourceTree = ""; };
- 0892E032AE12339D1AD84BDCC78A3C07 /* errorIconLight.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = errorIconLight.png; path = SwiftMessages/Resources/errorIconLight.png; sourceTree = ""; };
+ 004EE347886E2355CCB352F2AE7A35D6 /* warningIconLight.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = warningIconLight.png; path = SwiftMessages/Resources/warningIconLight.png; sourceTree = ""; };
+ 0398CE267904AF6C2FD03B3766511ECF /* PassthroughWindow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PassthroughWindow.swift; path = SwiftMessages/PassthroughWindow.swift; sourceTree = ""; };
+ 03A865E97261799942A6F29092D3D7AE /* PassthroughView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PassthroughView.swift; path = SwiftMessages/PassthroughView.swift; sourceTree = ""; };
+ 03B5D12D54ACC75F929389CFF15E91FF /* CardView.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = CardView.xib; path = SwiftMessages/Resources/CardView.xib; sourceTree = ""; };
+ 06DB4311BFB347093FC18777A503B461 /* MessageViewConvertible.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MessageViewConvertible.swift; path = SwiftMessages/MessageViewConvertible.swift; sourceTree = ""; };
+ 073CD58B2E8E255484D239A01D707B81 /* NSBundle+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSBundle+Extensions.swift"; path = "SwiftMessages/NSBundle+Extensions.swift"; sourceTree = ""; };
+ 080E269BABA7D072D8C51E29E9AC4A91 /* SwiftMessages-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftMessages-umbrella.h"; sourceTree = ""; };
093D5BBE2A96A1A7AC0432A3AB933576 /* Pods-iMessageDemo-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iMessageDemo-Info.plist"; sourceTree = ""; };
- 0970322A5DD8B4A8373C35ED051DE156 /* NSLayoutConstraint+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSLayoutConstraint+Extensions.swift"; path = "SwiftMessages/NSLayoutConstraint+Extensions.swift"; sourceTree = ""; };
- 0A8566413BFA3FE6827E63E30515C9B3 /* WindowViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = WindowViewController.swift; path = SwiftMessages/WindowViewController.swift; sourceTree = ""; };
- 0B3A169D83BDA3613A661845D1607FC0 /* successIconSubtle@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIconSubtle@2x.png"; path = "SwiftMessages/Resources/successIconSubtle@2x.png"; sourceTree = ""; };
+ 0FD758C8718F3EEFB5E97E34A29CE1CC /* SwiftMessages-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "SwiftMessages-Info.plist"; sourceTree = ""; };
1341BB7116EC50FDF7062C6A91DEDF49 /* Pods-iMessageExtensionDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iMessageExtensionDemo-acknowledgements.plist"; sourceTree = ""; };
14BF989232A1D55A0FDAAB70B5A8E1BF /* Pods-iMessageDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-iMessageDemo-dummy.m"; sourceTree = ""; };
- 15C4C88778A19C193D1EC77FA77BAE4C /* UIViewController+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIViewController+Extensions.swift"; path = "SwiftMessages/UIViewController+Extensions.swift"; sourceTree = ""; };
- 19B5EF40CEC1F6DB23F428DC159D4BC2 /* CALayer+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "CALayer+Extensions.swift"; path = "SwiftMessages/CALayer+Extensions.swift"; sourceTree = ""; };
- 1B04615682E8B787C964824435BC6616 /* errorIconLight@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIconLight@2x.png"; path = "SwiftMessages/Resources/errorIconLight@2x.png"; sourceTree = ""; };
- 1DC54BB09EFDFEDE11F484FE73BEDC60 /* successIconSubtle.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = successIconSubtle.png; path = SwiftMessages/Resources/successIconSubtle.png; sourceTree = ""; };
- 2337E6D5F621F3CE9A1E3761984EBE87 /* PhysicsPanHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PhysicsPanHandler.swift; path = SwiftMessages/PhysicsPanHandler.swift; sourceTree = ""; };
- 267E35F9851AAE50DFB8FA0DCA7F2980 /* Animator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Animator.swift; path = SwiftMessages/Animator.swift; sourceTree = ""; };
- 2B6B36CBE6DC07B2F005E30EA2B121CB /* LICENSE.md */ = {isa = PBXFileReference; includeInIndex = 1; path = LICENSE.md; sourceTree = ""; };
- 2B7213B7DC5432DA2B272F25E17AA364 /* infoIconSubtle@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIconSubtle@3x.png"; path = "SwiftMessages/Resources/infoIconSubtle@3x.png"; sourceTree = ""; };
- 2CFBBC25592C97C925B6F81B53BE57CB /* warningIcon@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIcon@3x.png"; path = "SwiftMessages/Resources/warningIcon@3x.png"; sourceTree = ""; };
+ 14DF8F43552C4F1C4CAEFBAFD9A77004 /* MarginAdjustable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MarginAdjustable.swift; path = SwiftMessages/MarginAdjustable.swift; sourceTree = ""; };
+ 16B5B2B5429B01345E4DA361DE01904D /* UIEdgeInsets+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIEdgeInsets+Extensions.swift"; path = "SwiftMessages/UIEdgeInsets+Extensions.swift"; sourceTree = ""; };
+ 252A5DF0FE3237691B8C50B969857389 /* successIconLight.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = successIconLight.png; path = SwiftMessages/Resources/successIconLight.png; sourceTree = ""; };
+ 28456F0421422D16D71D14D426309B3F /* MessageGeometryProxy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MessageGeometryProxy.swift; path = SwiftMessages/MessageGeometryProxy.swift; sourceTree = ""; };
+ 2AB9866F195EB0AED31A33AEC27B67C1 /* WindowScene.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = WindowScene.swift; path = SwiftMessages/WindowScene.swift; sourceTree = ""; };
+ 2CF5DC74916EBE02E70234620CD80150 /* infoIcon@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIcon@2x.png"; path = "SwiftMessages/Resources/infoIcon@2x.png"; sourceTree = ""; };
2D0ECE831FB5E0EE1D68E837671320C7 /* Pods-iMessageDemo-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-iMessageDemo-umbrella.h"; sourceTree = ""; };
- 2E6941E59EF0D89949E3DF3D89488430 /* Weak.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Weak.swift; path = SwiftMessages/Weak.swift; sourceTree = ""; };
- 337B3108412E3812254B85BCC4F90EB8 /* infoIcon@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIcon@3x.png"; path = "SwiftMessages/Resources/infoIcon@3x.png"; sourceTree = ""; };
- 346718C2C7A108C86535F89FEB0EC176 /* SwiftMessages.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftMessages.modulemap; sourceTree = ""; };
- 382E6375390EBB09F829519F8ACCB7D7 /* PassthroughWindow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PassthroughWindow.swift; path = SwiftMessages/PassthroughWindow.swift; sourceTree = ""; };
- 396FCF260E9C2B3F11080A91E3D72334 /* TopBottomAnimation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TopBottomAnimation.swift; path = SwiftMessages/TopBottomAnimation.swift; sourceTree = ""; };
- 397C8F928170138667B9326F9655D75D /* UIWindow+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIWindow+Extensions.swift"; path = "SwiftMessages/UIWindow+Extensions.swift"; sourceTree = ""; };
- 3C313FB1D33EF4B477FD5D3D6179A8BE /* errorIconSubtle@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIconSubtle@2x.png"; path = "SwiftMessages/Resources/errorIconSubtle@2x.png"; sourceTree = ""; };
- 3DF091E68C4659506DB08B0876BD274C /* CenteredView.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = CenteredView.xib; path = SwiftMessages/Resources/CenteredView.xib; sourceTree = ""; };
- 42F1E753E61B6EDED3908AC66994649C /* CardView.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = CardView.xib; path = SwiftMessages/Resources/CardView.xib; sourceTree = ""; };
- 4412F313361EFEE7A0193533A5AC5999 /* infoIconLight@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIconLight@3x.png"; path = "SwiftMessages/Resources/infoIconLight@3x.png"; sourceTree = ""; };
- 4512DEEA328AB0AAA95D77C84C89E02C /* warningIconSubtle.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = warningIconSubtle.png; path = SwiftMessages/Resources/warningIconSubtle.png; sourceTree = ""; };
- 453BC098B6A052020C40DE576F684C7B /* errorIconSubtle@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIconSubtle@3x.png"; path = "SwiftMessages/Resources/errorIconSubtle@3x.png"; sourceTree = ""; };
- 4824F23D80FF9070A5F8A452DB11EB9A /* SwiftMessages.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftMessages.framework; path = SwiftMessages.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 4F8D7902822221456B697BB41111E450 /* CornerRoundingView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CornerRoundingView.swift; path = SwiftMessages/CornerRoundingView.swift; sourceTree = ""; };
- 4FE466AC4123CDAE9FDA9FCF4FB9CC60 /* infoIconSubtle@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIconSubtle@2x.png"; path = "SwiftMessages/Resources/infoIconSubtle@2x.png"; sourceTree = ""; };
- 536660614870EA0E051BF6BDDF495798 /* KeyboardTrackingView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyboardTrackingView.swift; path = SwiftMessages/KeyboardTrackingView.swift; sourceTree = ""; };
- 54B278C80D821C120FA70ABB6CAF1F46 /* successIconSubtle@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIconSubtle@3x.png"; path = "SwiftMessages/Resources/successIconSubtle@3x.png"; sourceTree = ""; };
- 5C0DE01AB7D0935933E7AFBDF2945814 /* Identifiable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Identifiable.swift; path = SwiftMessages/Identifiable.swift; sourceTree = ""; };
+ 35F5FB13175EC0E13D53B445CE31395A /* CALayer+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "CALayer+Extensions.swift"; path = "SwiftMessages/CALayer+Extensions.swift"; sourceTree = ""; };
+ 38D28C78FCAE6706F1294CEFA6B25F88 /* errorIconSubtle@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIconSubtle@2x.png"; path = "SwiftMessages/Resources/errorIconSubtle@2x.png"; sourceTree = ""; };
+ 442F9FAEDF369CADB9E6E8FF3668FD95 /* Weak.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Weak.swift; path = SwiftMessages/Weak.swift; sourceTree = ""; };
+ 44DAC25C8D6D72F70BD1CF9099BDAF10 /* successIconLight@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIconLight@2x.png"; path = "SwiftMessages/Resources/successIconLight@2x.png"; sourceTree = ""; };
+ 47A710678B44C50DA470D1BCF94B87B8 /* successIconSubtle@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIconSubtle@3x.png"; path = "SwiftMessages/Resources/successIconSubtle@3x.png"; sourceTree = ""; };
+ 4824F23D80FF9070A5F8A452DB11EB9A /* SwiftMessages */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftMessages; path = SwiftMessages.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4DBF06E9B2F93F081139B8A6A922F569 /* successIcon@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIcon@2x.png"; path = "SwiftMessages/Resources/successIcon@2x.png"; sourceTree = ""; };
+ 560722B244EB84DF745199247129C164 /* infoIcon@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIcon@3x.png"; path = "SwiftMessages/Resources/infoIcon@3x.png"; sourceTree = ""; };
+ 5686DFC1E0B24A3051EF376AD8A162C0 /* successIconLight@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIconLight@3x.png"; path = "SwiftMessages/Resources/successIconLight@3x.png"; sourceTree = ""; };
+ 573965F15F83BD70EEAC9E1E8B0F85C6 /* SwiftMessages.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = SwiftMessages.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
+ 57EB79F63EA99ADBBDA408015A4599BB /* UIViewController+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIViewController+Extensions.swift"; path = "SwiftMessages/UIViewController+Extensions.swift"; sourceTree = ""; };
5CAB201AD00CAB811B045E2FFB5C03A8 /* Pods-iMessageExtensionDemo-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-iMessageExtensionDemo-umbrella.h"; sourceTree = ""; };
- 60268FE48AA4DC9FD5060B1E5CE68453 /* errorIcon@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIcon@2x.png"; path = "SwiftMessages/Resources/errorIcon@2x.png"; sourceTree = ""; };
- 62755A788F910CA88887B6F63BBC545F /* successIcon@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIcon@2x.png"; path = "SwiftMessages/Resources/successIcon@2x.png"; sourceTree = ""; };
+ 5E02B1554754BA1F8EF318D567875139 /* warningIconSubtle@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIconSubtle@3x.png"; path = "SwiftMessages/Resources/warningIconSubtle@3x.png"; sourceTree = ""; };
+ 5F989D33CAC3BCDA9B3C4B18F23A4DBE /* MessageHostingView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MessageHostingView.swift; path = SwiftMessages/MessageHostingView.swift; sourceTree = ""; };
+ 5FE01208AD70F1B35201AB04E50A23EC /* warningIconLight@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIconLight@3x.png"; path = "SwiftMessages/Resources/warningIconLight@3x.png"; sourceTree = ""; };
+ 6228A8E37F7905D75917DD4CBE65B03C /* SwiftMessages.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftMessages.modulemap; sourceTree = ""; };
+ 62332870F1E2AE3FA16EE5ADC85C104A /* StatusLine.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = StatusLine.xib; path = SwiftMessages/Resources/StatusLine.xib; sourceTree = ""; };
6489B2A759075E9DC1D1406734F45B5F /* Pods-iMessageExtensionDemo.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-iMessageExtensionDemo.modulemap"; sourceTree = ""; };
- 6516EE9BFCB9C53C151359F9D0247562 /* infoIconLight@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIconLight@2x.png"; path = "SwiftMessages/Resources/infoIconLight@2x.png"; sourceTree = ""; };
- 667DECE93ABCC6869A071FFEB0F83EA5 /* warningIconSubtle@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIconSubtle@3x.png"; path = "SwiftMessages/Resources/warningIconSubtle@3x.png"; sourceTree = ""; };
- 6E9D4A0FDDEEDDC5A1A63D89B43C1F18 /* SwiftMessages.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftMessages.debug.xcconfig; sourceTree = ""; };
- 70E7AB099F856140EF93D5B94A967418 /* infoIcon.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = infoIcon.png; path = SwiftMessages/Resources/infoIcon.png; sourceTree = ""; };
- 7241B2130D211F0832CCE4928CBB6486 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; path = README.md; sourceTree = ""; };
- 72A865FFBAE49EF66A35CB9D709E8D7E /* successIconLight@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIconLight@3x.png"; path = "SwiftMessages/Resources/successIconLight@3x.png"; sourceTree = ""; };
- 79E38069364BC5DF4EA88F352E28B242 /* warningIconLight.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = warningIconLight.png; path = SwiftMessages/Resources/warningIconLight.png; sourceTree = ""; };
- 7A6801849037A728E9BC50E06CE8AD2F /* StatusLine.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = StatusLine.xib; path = SwiftMessages/Resources/StatusLine.xib; sourceTree = ""; };
- 7A965DD26246DCA8CE39FBAB348ABA24 /* Presenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Presenter.swift; path = SwiftMessages/Presenter.swift; sourceTree = ""; };
+ 6924A0A48B715EA35080ABB106B96BF7 /* warningIconSubtle.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = warningIconSubtle.png; path = SwiftMessages/Resources/warningIconSubtle.png; sourceTree = ""; };
+ 6C12E87D0EA045A56FC0B45F072C74D2 /* errorIconSubtle@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIconSubtle@3x.png"; path = "SwiftMessages/Resources/errorIconSubtle@3x.png"; sourceTree = ""; };
+ 6EA088414C10E47808EA8490D9BBF64F /* successIcon@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIcon@3x.png"; path = "SwiftMessages/Resources/successIcon@3x.png"; sourceTree = ""; };
+ 71699C81494BC6585B862CDFC1CDAF54 /* warningIconLight@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIconLight@2x.png"; path = "SwiftMessages/Resources/warningIconLight@2x.png"; sourceTree = ""; };
+ 71D489FBDD84B41576199298F717BC08 /* errorIcon.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = errorIcon.png; path = SwiftMessages/Resources/errorIcon.png; sourceTree = ""; };
+ 74793ADC8ADF7674EA6C3041E2B54354 /* successIcon.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = successIcon.png; path = SwiftMessages/Resources/successIcon.png; sourceTree = ""; };
+ 790409B3FBA491A9262ACCB929F30104 /* BaseView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseView.swift; path = SwiftMessages/BaseView.swift; sourceTree = ""; };
+ 79A086337846180B9A63D8492CCE45B4 /* infoIcon.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = infoIcon.png; path = SwiftMessages/Resources/infoIcon.png; sourceTree = ""; };
+ 7B9CEE1B17C98E76723E7404C532515D /* successIconSubtle.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = successIconSubtle.png; path = SwiftMessages/Resources/successIconSubtle.png; sourceTree = ""; };
7CC6A596A9C1659D8E93222DA4144414 /* Pods-iMessageExtensionDemo-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iMessageExtensionDemo-Info.plist"; sourceTree = ""; };
7F40CA14AD738DD186B4DA8FD14AE5BD /* Pods-iMessageExtensionDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-iMessageExtensionDemo-dummy.m"; sourceTree = ""; };
- 7FCF161BD9F1C2CAAFBCADE5E59BD3FD /* NSBundle+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSBundle+Extensions.swift"; path = "SwiftMessages/NSBundle+Extensions.swift"; sourceTree = ""; };
+ 8026A87EEBF1A86A282563F974C4225C /* infoIconSubtle@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIconSubtle@2x.png"; path = "SwiftMessages/Resources/infoIconSubtle@2x.png"; sourceTree = ""; };
820B743874F7AC9F9E3970D68E2E60FA /* Pods-iMessageDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iMessageDemo.release.xcconfig"; sourceTree = ""; };
- 8341EF04E2B20C2BF6D4AF2240F80A56 /* Error.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Error.swift; path = SwiftMessages/Error.swift; sourceTree = ""; };
- 848251807107C20960A3DABAB27F7475 /* errorIconLight@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIconLight@3x.png"; path = "SwiftMessages/Resources/errorIconLight@3x.png"; sourceTree = ""; };
- 874D31DE863C88B1D699E1EBFBE0641B /* warningIconLight@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIconLight@2x.png"; path = "SwiftMessages/Resources/warningIconLight@2x.png"; sourceTree = ""; };
- 8901520225CE89F44E6DE88688F29C10 /* MessageView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MessageView.swift; path = SwiftMessages/MessageView.swift; sourceTree = ""; };
+ 85C6BEF274CDB3807F9BD452A26C68E6 /* warningIconSubtle@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIconSubtle@2x.png"; path = "SwiftMessages/Resources/warningIconSubtle@2x.png"; sourceTree = ""; };
+ 8605740B16E17342A29212D21C380249 /* errorIcon@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIcon@2x.png"; path = "SwiftMessages/Resources/errorIcon@2x.png"; sourceTree = ""; };
+ 86872F4639B26435D20BED0CE7E35C7F /* CornerRoundingView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CornerRoundingView.swift; path = SwiftMessages/CornerRoundingView.swift; sourceTree = ""; };
+ 86C1C9601BF8A5BC563062C310C0E3B5 /* BackgroundViewable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BackgroundViewable.swift; path = SwiftMessages/BackgroundViewable.swift; sourceTree = ""; };
+ 88237113DAAD8ECF8D5E3FAA27D8A02F /* infoIconLight@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIconLight@3x.png"; path = "SwiftMessages/Resources/infoIconLight@3x.png"; sourceTree = ""; };
+ 882EF814D0A1EE2AB28BFEE5C067EE6F /* NSLayoutConstraint+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "NSLayoutConstraint+Extensions.swift"; path = "SwiftMessages/NSLayoutConstraint+Extensions.swift"; sourceTree = ""; };
+ 88398B7A70E5B00495B670516D964701 /* Identifiable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Identifiable.swift; path = SwiftMessages/Identifiable.swift; sourceTree = ""; };
+ 8940290DCDAD08810FE0CD944F376444 /* ResourceBundle-SwiftMessages_SwiftMessages-SwiftMessages-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-SwiftMessages_SwiftMessages-SwiftMessages-Info.plist"; sourceTree = ""; };
+ 8AEEED9FBEA6C5DB7EE3B95E7D8115B3 /* PhysicsPanHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PhysicsPanHandler.swift; path = SwiftMessages/PhysicsPanHandler.swift; sourceTree = ""; };
+ 8D18688EA04EA2F2BC48ED05D1FE5404 /* Presenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Presenter.swift; path = SwiftMessages/Presenter.swift; sourceTree = ""; };
8D54691037F1CA4653B76F0558E2AA82 /* Pods-iMessageExtensionDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-iMessageExtensionDemo-acknowledgements.markdown"; sourceTree = ""; };
- 90F364E0C9A6EFE24680868D0BD293F1 /* SwiftMessages-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "SwiftMessages-Info.plist"; sourceTree = ""; };
+ 8D7E08E0434930866B59A3F17A3A577B /* PhysicsAnimation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PhysicsAnimation.swift; path = SwiftMessages/PhysicsAnimation.swift; sourceTree = ""; };
+ 91013DDC05BDB2787740B0C9E5444D23 /* errorIcon@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIcon@3x.png"; path = "SwiftMessages/Resources/errorIcon@3x.png"; sourceTree = ""; };
915DE2E4E300BAD440BE13F72E49D731 /* Pods-iMessageDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iMessageDemo-acknowledgements.plist"; sourceTree = ""; };
- 9C110C924ED12D2D32ECC27503018A31 /* infoIconLight.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = infoIconLight.png; path = SwiftMessages/Resources/infoIconLight.png; sourceTree = ""; };
- 9CF61FDEFE095F0486E9914F2262ADB9 /* MaskingView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MaskingView.swift; path = SwiftMessages/MaskingView.swift; sourceTree = ""; };
- 9D1FEAC04417D847EDC10783E054988F /* errorIcon.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = errorIcon.png; path = SwiftMessages/Resources/errorIcon.png; sourceTree = ""; };
+ 9414B7316138752779A4B9C7E5C988DB /* Error.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Error.swift; path = SwiftMessages/Error.swift; sourceTree = ""; };
+ 9610DCAAD9A55CDF73BAD54D523E1273 /* Task+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Task+Extensions.swift"; path = "SwiftMessages/Task+Extensions.swift"; sourceTree = ""; };
+ 975A42F4C16B65B38769916EC5468D2A /* CenteredView.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = CenteredView.xib; path = SwiftMessages/Resources/CenteredView.xib; sourceTree = ""; };
+ 9908774A62A95BF5BA8FE65842868043 /* WindowViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = WindowViewController.swift; path = SwiftMessages/WindowViewController.swift; sourceTree = ""; };
+ 9A99EE79DBFA3C24A372DDB69162E36C /* KeyboardTrackingView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KeyboardTrackingView.swift; path = SwiftMessages/KeyboardTrackingView.swift; sourceTree = ""; };
+ 9B602E4E64DAEF1468DD2F5C2C5DFD00 /* MarginAdjustable+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "MarginAdjustable+Extensions.swift"; path = "SwiftMessages/MarginAdjustable+Extensions.swift"; sourceTree = ""; };
+ 9D5A75A919AE6C6D38A627636D7ADC11 /* LICENSE.md */ = {isa = PBXFileReference; includeInIndex = 1; path = LICENSE.md; sourceTree = ""; };
9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
- A0D0E8B0020635F606875DD02735C502 /* BaseView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BaseView.swift; path = SwiftMessages/BaseView.swift; sourceTree = ""; };
- A2F9031B7FA82A17F04D4403091DF836 /* SwiftMessages-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftMessages-dummy.m"; sourceTree = ""; };
- A3C32E945AE3E6394EF85CD6BEC714B4 /* SwiftMessages.Config+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "SwiftMessages.Config+Extensions.swift"; path = "SwiftMessages/SwiftMessages.Config+Extensions.swift"; sourceTree = ""; };
- A80433B71162112A79043CB64261DB51 /* successIconLight.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = successIconLight.png; path = SwiftMessages/Resources/successIconLight.png; sourceTree = ""; };
- A850F76D7E3FEDC20FFB877454069171 /* errorIconSubtle.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = errorIconSubtle.png; path = SwiftMessages/Resources/errorIconSubtle.png; sourceTree = ""; };
- A9A13E69643D651322647C28E3F9E9C9 /* UIEdgeInsets+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UIEdgeInsets+Extensions.swift"; path = "SwiftMessages/UIEdgeInsets+Extensions.swift"; sourceTree = ""; };
- AE62AA801971C530345349D18AFCCB82 /* MessageView.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = MessageView.xib; path = SwiftMessages/Resources/MessageView.xib; sourceTree = ""; };
+ A102DE8896931D80E06F2C74C2B003AC /* TopBottomPresentable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TopBottomPresentable.swift; path = SwiftMessages/TopBottomPresentable.swift; sourceTree = ""; };
+ A1F1B1264A726D334A72AB1CB001CA9F /* TopBottomAnimation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TopBottomAnimation.swift; path = SwiftMessages/TopBottomAnimation.swift; sourceTree = ""; };
+ A399825CF45C4427BF9B80A590BF2BD5 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; path = README.md; sourceTree = ""; };
+ A4D741EF8B40F5472CE2C1C1A34426BD /* MessageView.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = MessageView.xib; path = SwiftMessages/Resources/MessageView.xib; sourceTree = ""; };
+ A6C7FF0D9E13571262231B72D0553E49 /* infoIconSubtle.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = infoIconSubtle.png; path = SwiftMessages/Resources/infoIconSubtle.png; sourceTree = ""; };
+ ACCCF5B75EA41D844ED936A92602C5B1 /* infoIconLight@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIconLight@2x.png"; path = "SwiftMessages/Resources/infoIconLight@2x.png"; sourceTree = ""; };
AE7AEA9CE6B44DCC96AE4E68FA644DAA /* Pods-iMessageExtensionDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iMessageExtensionDemo.release.xcconfig"; sourceTree = ""; };
- AFC41396FB1BD59C9A69EE1DD82E47C2 /* Pods_iMessageDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_iMessageDemo.framework; path = "Pods-iMessageDemo.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
- AFFDB7DDC6B6B064B9D12281E2531BFA /* SwiftMessagesSegue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftMessagesSegue.swift; path = SwiftMessages/SwiftMessagesSegue.swift; sourceTree = ""; };
- B6DEAADC09FEB1A8D5B90108103EE478 /* errorIcon@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIcon@3x.png"; path = "SwiftMessages/Resources/errorIcon@3x.png"; sourceTree = ""; };
- B7FD0618783A3E6B90D3A3323633959F /* TabView.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = TabView.xib; path = SwiftMessages/Resources/TabView.xib; sourceTree = ""; };
- B91AD9D9E743D17D553D48103BE27C46 /* MarginAdjustable+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "MarginAdjustable+Extensions.swift"; path = "SwiftMessages/MarginAdjustable+Extensions.swift"; sourceTree = ""; };
+ AFC41396FB1BD59C9A69EE1DD82E47C2 /* Pods-iMessageDemo */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-iMessageDemo"; path = Pods_iMessageDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ B17EF282E0A0F329DDF17226E83706F8 /* SwiftMessagesSegue.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftMessagesSegue.swift; path = SwiftMessages/SwiftMessagesSegue.swift; sourceTree = ""; };
+ B19DACBEC8DD85FB850D299DFB7A0374 /* SwiftMessageModifier.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftMessageModifier.swift; path = SwiftMessages/SwiftMessageModifier.swift; sourceTree = ""; };
+ B241F1B5D295E7806363B802DF03A085 /* warningIcon@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIcon@2x.png"; path = "SwiftMessages/Resources/warningIcon@2x.png"; sourceTree = ""; };
+ B32459A84426DD5E88256AD61F3D4E91 /* Theme.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Theme.swift; path = SwiftMessages/Theme.swift; sourceTree = ""; };
+ B8B9892D8D5596E09F02DA2D71BF60B4 /* warningIcon@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIcon@3x.png"; path = "SwiftMessages/Resources/warningIcon@3x.png"; sourceTree = ""; };
+ B941E5186F814BF618D9D0458496379C /* SwiftMessages-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftMessages-prefix.pch"; sourceTree = ""; };
B99CBDE49D6502CF64EB9059C005BF31 /* Pods-iMessageDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iMessageDemo.debug.xcconfig"; sourceTree = ""; };
- BEBF018059B0DFCAC8494ABD1C578AD9 /* SwiftMessages_SwiftMessages.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = SwiftMessages_SwiftMessages.bundle; path = "SwiftMessages-SwiftMessages_SwiftMessages.bundle"; sourceTree = BUILT_PRODUCTS_DIR; };
+ BA61C87AB568667230DCD9DC2E423722 /* SwiftMessages.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftMessages.release.xcconfig; sourceTree = ""; };
+ BEBF018059B0DFCAC8494ABD1C578AD9 /* SwiftMessages-SwiftMessages_SwiftMessages */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = "SwiftMessages-SwiftMessages_SwiftMessages"; path = SwiftMessages_SwiftMessages.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
BF61E78F8E8EE539F4A63C5A9D43AC15 /* Pods-iMessageDemo-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-iMessageDemo-frameworks.sh"; sourceTree = ""; };
- BFEF746702215C33B51BEE64C4E48F0A /* warningIcon@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIcon@2x.png"; path = "SwiftMessages/Resources/warningIcon@2x.png"; sourceTree = ""; };
- C17CFEF9B761A322945F74D86CA88036 /* warningIconLight@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIconLight@3x.png"; path = "SwiftMessages/Resources/warningIconLight@3x.png"; sourceTree = ""; };
C306ACAFEE157959D78E71DBBBD675DC /* Pods-iMessageExtensionDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iMessageExtensionDemo.debug.xcconfig"; sourceTree = ""; };
- C3216CF40D770C387D45C3B4AF2CC9E0 /* SwiftMessages.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftMessages.swift; path = SwiftMessages/SwiftMessages.swift; sourceTree = ""; };
- C614EECDDFE644AF0BF7CB16A3D74404 /* SwiftMessages-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftMessages-umbrella.h"; sourceTree = ""; };
- C6C56947CAF8EFCC7858E1E2F1273427 /* MarginAdjustable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MarginAdjustable.swift; path = SwiftMessages/MarginAdjustable.swift; sourceTree = ""; };
- C7FE39695CB7C6997ACA39C8680B414A /* SwiftMessages.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = SwiftMessages.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
- C8155ED78358FA5CF39089176FBDE501 /* successIcon@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIcon@3x.png"; path = "SwiftMessages/Resources/successIcon@3x.png"; sourceTree = ""; };
- C9D915B60769D3C45A0DA3A5BA9514B8 /* ResourceBundle-SwiftMessages_SwiftMessages-SwiftMessages-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-SwiftMessages_SwiftMessages-SwiftMessages-Info.plist"; sourceTree = ""; };
- CAB58A9F688DD39112E6CDF20C8969C0 /* BackgroundViewable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BackgroundViewable.swift; path = SwiftMessages/BackgroundViewable.swift; sourceTree = ""; };
- CBC3F501D8BC852716D085B3022E68CA /* Pods_iMessageExtensionDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_iMessageExtensionDemo.framework; path = "Pods-iMessageExtensionDemo.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
+ C33B3DA756F19F5C93D498D557BEB820 /* TabView.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = TabView.xib; path = SwiftMessages/Resources/TabView.xib; sourceTree = ""; };
+ C3701E97FDB85EFCE90E06D8CBC8ED41 /* infoIconLight.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = infoIconLight.png; path = SwiftMessages/Resources/infoIconLight.png; sourceTree = ""; };
+ C40BBCF3A77C5C7A01708634BB4275C3 /* infoIconSubtle@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "infoIconSubtle@3x.png"; path = "SwiftMessages/Resources/infoIconSubtle@3x.png"; sourceTree = ""; };
+ C49B964EF7710D818E0AF0321724EEF4 /* errorIconLight@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIconLight@2x.png"; path = "SwiftMessages/Resources/errorIconLight@2x.png"; sourceTree = ""; };
+ C61DD3C66AC7ADE1E9CCFE7A4A0BEA59 /* TopBottomAnimationStyle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TopBottomAnimationStyle.swift; path = SwiftMessages/TopBottomAnimationStyle.swift; sourceTree = ""; };
+ C9BBC4BF82FBB152E34F2D1EF36A0FB1 /* MessageView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MessageView.swift; path = SwiftMessages/MessageView.swift; sourceTree = ""; };
+ C9E8A4D41E0A0E52C839F7197C4CE2A0 /* AccessibleMessage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AccessibleMessage.swift; path = SwiftMessages/AccessibleMessage.swift; sourceTree = ""; };
+ CBC3F501D8BC852716D085B3022E68CA /* Pods-iMessageExtensionDemo */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-iMessageExtensionDemo"; path = Pods_iMessageExtensionDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ CC4854845AC1848C056C36719A5D7420 /* MaskingView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MaskingView.swift; path = SwiftMessages/MaskingView.swift; sourceTree = ""; };
CC9152C843976F18EF9AE005786DCC80 /* Pods-iMessageDemo.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-iMessageDemo.modulemap"; sourceTree = ""; };
+ D091A06E439F734980F26F81E9161EF2 /* SwiftMessages-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftMessages-dummy.m"; sourceTree = ""; };
+ D146564C0660B5F616D6736F6560E17C /* successIconSubtle@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "successIconSubtle@2x.png"; path = "SwiftMessages/Resources/successIconSubtle@2x.png"; sourceTree = ""; };
D245E0514AAC1A2B9A6D5EA2F383E90F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
- D5607E285A85AC7163B0B8FD447FD27E /* Theme.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Theme.swift; path = SwiftMessages/Theme.swift; sourceTree = ""; };
- D7C557E1DC95AFA417E94ED01301F9F2 /* PassthroughView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PassthroughView.swift; path = SwiftMessages/PassthroughView.swift; sourceTree = ""; };
- E070773539E02C11297794CC4CB839D6 /* SwiftMessages.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftMessages.release.xcconfig; sourceTree = ""; };
+ D47C1471CCFFBEF214DF9F0ECBDD408B /* warningIcon.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = warningIcon.png; path = SwiftMessages/Resources/warningIcon.png; sourceTree = ""; };
+ D6A205E933052E98349DC710D78304CB /* errorIconLight@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "errorIconLight@3x.png"; path = "SwiftMessages/Resources/errorIconLight@3x.png"; sourceTree = ""; };
+ D83D1CC131CDAC7CA2C5BFD8701E8A79 /* SwiftMessages.Config+Extensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "SwiftMessages.Config+Extensions.swift"; path = "SwiftMessages/SwiftMessages.Config+Extensions.swift"; sourceTree = ""; };
+ DBF30CA29E00D6768396013A24DDE9E9 /* SwiftMessages.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftMessages.debug.xcconfig; sourceTree = ""; };
E473E4F019E816262A61B7F5E8B42373 /* Pods-iMessageDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-iMessageDemo-acknowledgements.markdown"; sourceTree = ""; };
+ EA8BFA921A44ED7AEAA0E08BCE17EDDA /* HapticMessage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HapticMessage.swift; path = SwiftMessages/HapticMessage.swift; sourceTree = ""; };
EAB6F611E86A4758835A715E4B4184F6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
- ED86D7B42F55040DCE162653FB3C7EDB /* warningIconSubtle@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "warningIconSubtle@2x.png"; path = "SwiftMessages/Resources/warningIconSubtle@2x.png"; sourceTree = ""; };
- EDDAF8A2C45FDDDFD5AFA59203137551 /* AccessibleMessage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AccessibleMessage.swift; path = SwiftMessages/AccessibleMessage.swift; sourceTree = "