Convert Figma logo to code with AI

SwiftKickMobile logoSwiftMessages

A very flexible message bar for UIKit and SwiftUI.

7,307
742
7,307
46

Top Related Projects

Convenient & secure logging during development & release in Swift 4 & 5

3,757

:mega: Whisper is a component that will make the task of display messages and in-app notifications simple. It has three different views inside

A unified API to ask for permissions on iOS

A handy collection of more than 500 native Swift extensions to boost your productivity.

Quick Overview

SwiftMessages is a highly customizable message bar library for iOS written in Swift. It provides a flexible and easy-to-use API for displaying various types of messages, alerts, and notifications at the top or bottom of the screen with smooth animations and interactive gestures.

Pros

  • Highly customizable with numerous built-in layouts and styles
  • Supports interactive swipe-to-hide and tap-to-hide gestures
  • Offers queue management for multiple messages
  • Easy integration with existing projects

Cons

  • Limited to iOS platform only
  • May require additional setup for complex custom layouts
  • Learning curve for advanced customizations
  • Potential performance impact with excessive use of animations

Code Examples

  1. Displaying a simple message:
SwiftMessages.show {
    let view = MessageView.viewFromNib(layout: .cardView)
    view.configureTheme(.success)
    view.configureContent(title: "Success", body: "Operation completed successfully")
    view.button?.setTitle("OK", for: .normal)
    view.buttonTapHandler = { _ in SwiftMessages.hide() }
    return view
}
  1. Customizing message appearance:
let view = MessageView.viewFromNib(layout: .messageView)
view.configureTheme(.warning)
view.configureDropShadow()
view.configureContent(title: "Warning", body: "Please check your input")
view.button?.isHidden = true
var config = SwiftMessages.Config()
config.presentationStyle = .bottom
config.duration = .seconds(seconds: 3)
SwiftMessages.show(config: config, view: view)
  1. Using a custom view:
let customView = CustomView()
customView.configure(with: someData)
SwiftMessages.show(view: customView)

Getting Started

  1. Install SwiftMessages using CocoaPods, Carthage, or Swift Package Manager.
  2. Import the library in your Swift file:
import SwiftMessages
  1. Display a message using one of the built-in layouts:
SwiftMessages.show {
    let view = MessageView.viewFromNib(layout: .messageView)
    view.configureTheme(.info)
    view.configureContent(title: "Hello", body: "Welcome to SwiftMessages!")
    return view
}

Competitor Comparisons

Convenient & secure logging during development & release in Swift 4 & 5

Pros of SwiftyBeaver

  • Focused on logging and debugging, providing more advanced logging features
  • Supports multiple destinations for logs (console, file, cloud)
  • Offers log filtering and custom formatting options

Cons of SwiftyBeaver

  • Limited to logging functionality, not suitable for user-facing messages
  • Requires more setup and configuration for advanced features
  • May be overkill for simple projects that only need basic logging

Code Comparison

SwiftyBeaver:

import SwiftyBeaver
let log = SwiftyBeaver.self
log.addDestination(ConsoleDestination())
log.info("Hello World")

SwiftMessages:

import SwiftMessages
let view = MessageView.viewFromNib(layout: .cardView)
view.configureTheme(.success)
view.configureContent(title: "Success", body: "Hello World")
SwiftMessages.show(view: view)

SwiftyBeaver is primarily designed for logging and debugging purposes, offering advanced features like multiple log destinations and custom formatting. It's ideal for developers who need detailed logging capabilities throughout their application.

On the other hand, SwiftMessages focuses on displaying user-facing messages and notifications within the app. It provides a variety of customizable message views and presentation styles, making it more suitable for enhancing the user interface and experience.

While SwiftyBeaver excels in logging and debugging, SwiftMessages is better suited for creating visually appealing in-app notifications and alerts. The choice between the two depends on whether you need advanced logging capabilities or user-facing message displays in your Swift application.

3,757

:mega: Whisper is a component that will make the task of display messages and in-app notifications simple. It has three different views inside

Pros of Whisper

  • Lightweight and minimalistic design, focusing on simplicity
  • Easy to customize with a variety of pre-built themes
  • Supports both top and bottom message positioning

Cons of Whisper

  • Less feature-rich compared to SwiftMessages
  • Limited animation options for message presentation
  • Fewer message types and configurations available

Code Comparison

Whisper:

let message = Message(title: "Hello", textColor: .white, backgroundColor: .red)
Whisper.show(whisper: message, to: navigationController, action: .show)

SwiftMessages:

let view = MessageView.viewFromNib(layout: .cardView)
view.configureTheme(.warning)
view.configureContent(title: "Warning", body: "Consider yourself warned.")
SwiftMessages.show(view: view)

Both libraries offer straightforward ways to display messages, but SwiftMessages provides more configuration options and a wider range of layouts out of the box. Whisper focuses on simplicity, while SwiftMessages offers greater flexibility and customization.

Whisper is ideal for projects requiring quick, simple notifications, whereas SwiftMessages is better suited for applications needing more complex message presentations and interactions.

A unified API to ask for permissions on iOS

Pros of Permission

  • Focused specifically on iOS permission handling, providing a more specialized solution
  • Offers a clean, chainable API for requesting and managing various system permissions
  • Includes built-in permission request dialogs with customizable messages

Cons of Permission

  • Limited to permission management, lacking broader messaging capabilities
  • May require additional libraries for more general UI notifications or alerts
  • Less actively maintained, with fewer recent updates compared to SwiftMessages

Code Comparison

Permission:

Permission.contacts.request { status in
    switch status {
    case .authorized:    print("Contacts permission granted")
    case .denied:        print("Contacts permission denied")
    case .notDetermined: print("Contacts permission not determined")
    case .notSupported:  print("Contacts permission not supported")
    }
}

SwiftMessages:

let view = MessageView.viewFromNib(layout: .cardView)
view.configureTheme(.success)
view.configureContent(title: "Success", body: "Permission granted")
SwiftMessages.show(view: view)

Summary

Permission focuses on iOS permission handling with a clean API, while SwiftMessages offers broader messaging capabilities. Permission is more specialized but less actively maintained, whereas SwiftMessages provides a more general-purpose solution for displaying various types of messages and alerts in iOS apps.

A handy collection of more than 500 native Swift extensions to boost your productivity.

Pros of SwifterSwift

  • Broader scope: Offers a comprehensive collection of extensions for Swift types and UIKit
  • More frequent updates: Actively maintained with regular contributions
  • Modular structure: Allows importing specific extensions as needed

Cons of SwifterSwift

  • Larger codebase: May increase app size if not carefully managed
  • Potential conflicts: Some extensions might overlap with custom code or other libraries
  • Learning curve: Requires familiarity with a wide range of extensions

Code Comparison

SwifterSwift example (String extension):

let string = "Hello, World!"
let reversed = string.reversed // "!dlroW ,olleH"
let truncated = string.truncate(to: 5) // "Hello..."

SwiftMessages example (Showing a message):

let view = MessageView.viewFromNib(layout: .cardView)
view.configureTheme(.success)
view.configureContent(title: "Success", body: "Operation completed")
SwiftMessages.show(view: view)

While SwifterSwift provides general-purpose extensions, SwiftMessages focuses specifically on displaying various types of message views in iOS apps. SwifterSwift is more versatile but may require more careful integration, whereas SwiftMessages offers a more targeted solution for a specific use case.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

SwiftMessages

Twitter: @TimothyMoose Version License Platform Carthage compatible

Overview

🔥🔥🔥 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!

In addition to the numerous configuration options, SwiftMessages provides several good-looking layouts and themes. But SwiftMessages is also designer-friendly, which means you can fully and easily customize the view:

  • 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 View or UIView.

Installation

Swift Package Manager

Go to File | Swift Packages | Add Package Dependency... in Xcode and search for "SwiftMessages". If multiple results are found, select the one owned by SwiftKick Mobile.

CocoaPods

Add the following line to your Podfile:

pod 'SwiftMessages'

Carthage

Add the following line to your Cartfile:

github "SwiftKickMobile/SwiftMessages"

If the Carthage build fails, try using the script.

Manual

  1. Put SwiftMessages repo somewhere in your project directory.
  2. In Xcode, add SwiftMessages.xcodeproj to your project.
  3. On your app's target, add the SwiftMessages framework:
    1. as an embedded binary on the General tab.
    2. as a target dependency on the Build Phases tab.

Usage

Basics

SwiftMessages.show(view: myView)

Although you can show any instance of UIView, SwiftMessages provides a MessageView class and assortment of nib-based layouts that should handle most cases:

// Instantiate a message view from the provided card view layout. SwiftMessages searches for nib
// files in the main bundle first, so you can easily copy them into your project and make changes.
let view = MessageView.viewFromNib(layout: .cardView)

// Theme message elements with the warning style.
view.configureTheme(.warning)

// Add a drop shadow.
view.configureDropShadow()

// Set message title, body, and icon. Here, we're overriding the default warning
// image with an emoji character.
let iconText = ["🤔", "😳", "🙄", "😶"].randomElement()!
view.configureContent(title: "Warning", body: "Consider yourself warned.", iconText: iconText)

// Increase the external margin around the card. In general, the effect of this setting
// depends on how the given layout is constrained to the layout margins.
view.layoutMarginAdditions = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)

// Reduce the corner radius (applicable to layouts featuring rounded corners).
(view.backgroundView as? CornerRoundingView)?.cornerRadius = 10

// Show the message.
SwiftMessages.show(view: view)

You may wish to use the view provider variant show(viewProvider:) to ensure that your UIKit code is executed on the main queue:

SwiftMessages.show {
    let view = MessageView.viewFromNib(layout: .cardView)
    // ... configure the view
    return view
}

The SwiftMessages.Config struct provides numerous configuration options that can be passed to show():

var config = SwiftMessages.Config()

// Slide up from the bottom.
config.presentationStyle = .bottom

// Display in a window at the specified window level.
config.presentationContext = .window(windowLevel: .statusBar)

Note that, as of iOS 13, it is no longer possible to cover the status bar
regardless of the window level. A workaround is to hide the status bar instead.
config.prefersStatusBarHidden = true

// Disable the default auto-hiding behavior.
config.duration = .forever

// Dim the background like a popover view. Hide when the background is tapped.
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 id=\(String(describing: event.id)")
    }
}

SwiftMessages.show(config: config, view: view)

Specify default configuration options:

SwiftMessages.defaultConfig.presentationStyle = .bottom

// Show message with default config.
SwiftMessages.show(view: view)

// Customize config using the default as a base.
var config = SwiftMessages.defaultConfig
config.duration = .forever
SwiftMessages.show(config: config, view: view)

View Controllers

SwiftMessages can present view controllers using the SwiftMessagesSegue custom modal segue!

SwiftMessagesSegue 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

And check out our blog post Elegant Custom UIViewController Transitioning 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:

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:

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:

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.

extension DemoMessage: MessageViewConvertible {
    func asMessageView() -> DemoMessageView {
        DemoMessageView(message: self)
    }
}

Then you can drop the view builder when calling swiftMessage():

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.

  • The title and body of the message are combined into a single announcement when the message is shown. The MessageView.accessibilityPrefix property can be set to prepend additional clarifying text to the announcement.

    Sometimes, a message may contain important visual cues that aren't captured in the title or body. For example, a message may rely on a yellow background to convey a warning rather than having the word "warning" in the title or body. In this case, it might be helpful to set MessageView.accessibilityPrefix = "warning".

  • If the message is shown with a dim view using config.dimMode, elements below the dim view are not focusable until the message is hidden. If config.dimMode.interactive == true, the dim view itself will be focusable and read out "dismiss" followed by "button". The former text can be customized by setting the config.dimModeAccessibilityLabel property.

See the AccessibleMessage protocol for implementing proper accessibility support in custom views.

Keyboard Avoidance

The KeyboardTrackingView class can be used to cause the message view to avoid the keyboard by sliding up when the keyboard gets too close.

var config = SwiftMessages.defaultConfig
config.keyboardTrackingView = KeyboardTrackingView()

You can incorporate KeyboardTrackingView into your app even when you're not using SwiftMessages. Install into your view hierarchy by pinning KeyboardTrackingView to the bottom, leading, and trailing edges of the screen. Then pin the bottom of your content that should avoid the keyboard to the top KeyboardTrackingView. Use an equality constraint to strictly track the keyboard or an inequality constraint to only move when the keyboard gets too close. KeyboardTrackingView works by observing keyboard notifications and adjusting its height to maintain its top edge above the keyboard, thereby pushing your content up. See the comments in KeyboardTrackingView for configuration options.

Message Queueing

You can call SwiftMessages.show() as many times as you like. SwiftMessages maintains a queue and shows messages one at a time. If your view implements the Identifiable protocol (like MessageView), duplicate messages will be removed automatically. The pause between messages can be adjusted:

SwiftMessages.pauseBetweenMessages = 1.0

There are a few ways to hide messages programatically:

// Hide the current message.
SwiftMessages.hide()

// Or hide the current message and clear the queue.
SwiftMessages.hideAll()

// Or for a view that implements `Identifiable`:
SwiftMessages.hide(id: someId)

// Or hide when the number of calls to show() and hideCounted(id:) for a 
// given message ID are equal. This can be useful for messages that may be
// shown from  multiple code paths to ensure that all paths are ready to hide.
SwiftMessages.hideCounted(id: someId)

Multiple instances of SwiftMessages can be used to show more than one message at a time. Note that the static SwiftMessages.show() and other static APIs on SwiftMessage are just convenience wrappers around the shared instance SwiftMessages.sharedInstance). Instances must be retained, thus it should be a property of something (e.g. your view controller):

class SomeViewController: UIViewController {
    let otherMessages = SwiftMessages()	
	
    func someMethod() {
        SwiftMessages.show(...)
        otherMessages.show(...)
    }
}

Retrieving Messages

There are several APIs available for retrieving messages that are currently being shown, hidden, or queued to be shown. These APIs are useful for updating messages when some event happens without needing to keep temporary references around. See also eventListeners.

// Get a message view with the given ID if it is currently 
// being shown or hidden.
if let view = SwiftMessages.current(id: "some id") { ... }

// Get a message view with the given ID if is it currently 
// queued to be shown. 
if let view = SwiftMessages.queued(id: "some id") { ... }

// Get a message view with the given ID if it is currently being
// shown, hidden or in the queue to be shown.
if let view = SwiftMessages.currentOrQueued(id: "some id") { ... }

Customization

SwiftMessages can display any UIView. However, there are varying degrees of customization that can be done to the bundled views.

Nib Files

All of the message designs bundled with SwiftMessages have associated nib files. You are encouraged to copy any of these nib files into your project and modify them to suit your needs. SwiftMessages will load your copy of the file instead of the original. Nib files may be copied in Xcode using drag-and-drop.

To facilitate the use of nib-based layouts, MessageView provides some type-safe convenience methods for loading the bundled nibs:

let view = MessageView.viewFromNib(layout: .cardView)

In addition, the SwiftMessages class provides some generic loading methods:

// Instantiate MessageView from a named nib.
let view: MessageView = try! SwiftMessages.viewFromNib(named: "MyCustomNib")

// Instantiate MyCustomView from a nib named MyCustomView.nib.
let view: MyCustomView = try! SwiftMessages.viewFromNib()

MessageView Class

MessageView is a light-weight view that all of the bundled designs use. It primarily consists of the following optional @IBOutlet properties:

ElementDeclarationDescription
TitletitleLabel: UILabel?The message title.
Message bodybodyLabel: UILabel?The body of the message.
Image iconiconImageView: UIImageView?An image-based icon.
Text iconiconLabel: UILabel?A text-based (emoji) alternative to the image icon.
Buttonbutton: UIButton?An action button.

The SwiftMessages nib file use MessageView as the top-level view with content connected to these outlets. The layouts are done using stack views, which means that you can remove an element by simply hiding it:

view.titleLabel.isHidden = true

A common mistake is attempting to remove an element by setting the corresponding outlet to nil. This does not work because it does not remove the element from the view hierarchy.

Configuration

MessageView provides numerous methods that follow the configure* naming convention:

view.configureTheme(.warning, includeHaptic: true)
view.configureContent(title: "Warning", body: "Consider yourself warned.", iconText: "🤔")

All of these methods are shortcuts for quickly configuring the underlying view properties. SwiftMessages strives to avoid doing any internal magic in these methods, so you do not need to call them. You can configure the view properties directly or combine the two approaches.

Interaction

MessageView provides an optional block-based tap handler for the button and another for the view itself:

// Hide when button tapped
messageView.buttonTapHandler = { _ in SwiftMessages.hide() }

// Hide when message view tapped
messageView.tapHandler = { _ in SwiftMessages.hide() }

Extending

The suggested method for starting with MessageView as a base and adding new elements, such as additional buttons, is as follows:

  1. Copy one of the bundled nib files into your project or create a new one from scratch.
  2. Add new elements to the nib file.
  3. Sublcass MessageView and create outlets for the new elements.
  4. Assign the top-level view in the nib file to the subclass.
  5. Connect outlets between the nib file and the subclass.
  6. (recommended) override the implementation of Identifiable as needed to incorporate new elements into the message's identity.
  7. (recommended) override the implementation of AccessibleMessage as needed to incorporate new elements into Voice Over.
  8. Use one of the nib-loading methods above to load the view.

BaseView Class

BaseView is the superclass of MessageView and provides numerous options that aren't specific to the "title + body + icon + button" design of MessageView. Custom views that are significantly different from MessageView, such as a progress indicator, should subclass BaseView.

CornerRoundingView Class

CornerRoundingView is a custom view that messages can use for rounding all or a subset of corners with squircles (the smoother method of rounding corners that you see on app icons). The nib files that feature rounded corners have backgroundView assigned to a CornerRoundingView. It provides a roundsLeadingCorners option to dynamically round only the leading corners of the view when presented from top or bottom (a feature used for the tab-style layouts).

Animator Protocol

Animator is the protocol that SwiftMessages uses for presentation and dismissal animations. Custom animations can be done through the SwiftMessages.PresentationStyle.custom(animator:). Some related components:

  • TopBottomAnimation is a sliding implementation of Animator used internally by .top and .bottom presentation styles. It provides some customization options.
  • PhysicsAnimation is a scaling + opacity implementation of Animator used internally by the .center presentation style. It provides a fun physics-based dismissal gesture and provides customization options including .top and .bottom placement.
  • PhysicsPanHandler provides the physics-based dismissal gesture for PhysicsAnimation and can be incorporated into other Animator implementations.

High-quality PRs for cool Animator implementations are welcome!

MarginAdjustable Protocol

MarginAdjustable is a protocol adopted by BaseView. If the view being presented adopts MarginAdjustable, SwiftMessages takes ownership of the view's layout margins to ensure ideal spacing across the full range of presentation contexts.

BackgroundViewable Protocol

BackgroundViewable is a protocol adopted by BaseView and requires that a view provide a single backgroundView property. BaseView initializes backgroundView = self, which you can freely re-assign to any subview.

If the view being presented adopts BackgroundViewable, SwiftMessages will ignore touches outside of backgroundView. This is important because message views always span the full width of the device. Card and tab-style layouts appear inset from the edges of the device because the message view's background is transparent and backgroundView is assigned to a subview constrained to the layout margins. In these layouts, touches in the transparent margins should be ignored.

Identifiable Protocol

Identifiable is a protocol adopted by MessageView and requires that a view provide a single id property, which SwiftMessages uses for message deduplication.

MessageView computes the id based on the message content, but id can also be set explicitly as needed.

AccessibleMessage Protocol

AccessibleMessage is a protocol adopted by MessageView. If the view being presented adopts AccessibleMessage, SwiftMessages provides improved Voice Over.

About SwiftKick Mobile

We build high quality apps! Get in touch if you need help with a project.

License

SwiftMessages is distributed under the MIT license. See LICENSE for details.