Encatch
Welcome to Encatch Docs
Mobile & Native SDKs

macOS SDK

Run the Encatch Swift SDK on macOS via Mac Catalyst — in-app feedback and survey collection for Mac apps

The Encatch Swift SDK runs on macOS through Mac Catalyst. It is the same package as the iOS SDK — no separate dependency, no conditional code paths in your integration. This page covers what's specific to the Mac: enabling Catalyst, and building a UI around the SDK that feels like a real Mac app rather than a stretched phone screen.


Overview

  • Package: encatch-swift — the same Swift Package as iOS
  • Version: 0.1.7
  • Platforms: iOS 15+, macOS 12+
  • License: MIT

macOS support works in two tiers:

Target typeWhat works
Mac Catalyst appEverything — modal form overlay, inline forms, tracking, identify, sessions
Plain macOS (AppKit / SwiftUI-for-Mac) targetCore APIs compile and run — initialize, identifyUser, trackEvent, trackScreen, sessions — but the WebView form UI does not, because it requires UIKit

Why Catalyst?

The SDK's form UI (EncatchFormHost, EncatchInlineFormView, the WebView bridge) is built on UIKit and compiled behind #if canImport(UIKit). Mac Catalyst provides UIKit on macOS, so the full SDK — including forms — works in a Catalyst app with zero changes. A plain macOS target gets the headless tracking/identify core only.

For the complete API reference — configuration, identify, events, sessions, inline forms, interceptors — see the iOS SDK page. The API is identical.


Installation

1. Enable Mac Catalyst on your app target

In Xcode, select your iOS app target → GeneralSupported Destinations → add Mac (Mac Catalyst). Choose "Optimize Interface for Mac" for native-feeling controls, or "Scaled to Match iPad" for a literal iPad port (not recommended — see macOS-specific considerations below).

2. Add the package

File → Add Package Dependencies… → enter:

https://github.com/get-encatch/encatch-swift

Dependency rule: Up to Next Minor Version from 0.1.7, then add the Encatch product to your app target.

dependencies: [
    .package(url: "https://github.com/get-encatch/encatch-swift", from: "0.1.7"),
],
targets: [
    .target(
        name: "MyApp",
        dependencies: [.product(name: "Encatch", package: "encatch-swift")]
    ),
]

Pre-1.0 versioning

While versions are 0.x, minor bumps may contain breaking changes. SPM's from: "0.1.7" rule only auto-updates patch releases, which is the safe default.

No separate Catalyst build settings are needed — the package builds for the maccatalyst destination as-is.


Quick Start

Install the modal form host once at launch, initialize, and show a form. This is exactly the same code as iOS:

import SwiftUI
import Encatch

@main
struct MyMacApp: App {
    init() {
        EncatchFormHost.install()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
                .task {
                    try? await Encatch.shared.initialize(apiKey: "YOUR_API_KEY")
                    try? await Encatch.shared.identifyUser(userName: "user@example.com")
                }
        }
    }
}

Then trigger a form from anywhere — a button, a menu item, a keyboard shortcut:

Task {
    try await Encatch.shared.showForm("your-form-id")
}

The API surface is identical to iOS, so refer to the iOS page for:


macOS-specific considerations

The SDK needs nothing extra to run under Catalyst — but your app around it should behave like a Mac app. The guidance below comes from building a full Catalyst tester app against the SDK; the Catalyst limitations listed are real compiler/runtime behavior verified by building, not assumptions.

Design Mac-native, don't port the phone UI

Running an iPhone layout unmodified under Catalyst looks like a stretched phone screen. Patterns that translate well:

  • Sidebar, not bottom tabs — replace a TabView bottom bar with NavigationSplitView (Mail/Xcode-style). A sidebar also gives you a persistent, always-visible place to surface state — for example, a badge count of forms your onBeforeShowForm interceptor has queued for custom rendering — where a phone app would need floating chrome.
  • System controls over brand theming.borderedProminent/.bordered buttons, .roundedBorder text fields, Form with .formStyle(.grouped), LabeledContent, and Color.accentColor (the user's own system accent) instead of a hardcoded brand color. Hardcoded pill/capsule iOS theming is the fastest way to look non-native on the Mac.
  • A menu bar — expose feedback actions as menu commands with keyboard shortcuts:
var body: some Scene {
    WindowGroup { ContentView() }
        .commands {
            CommandMenu("Feedback") {
                Button("Send Feedback…") {
                    Task { try? await Encatch.shared.showForm("feedback-form") }
                }
                .keyboardShortcut("f", modifiers: [.command, .shift])
            }
        }
}
  • Drop soft-keyboard workarounds — keyboard-avoidance scroll hacks are meaningless without an on-screen keyboard. Pointer-driven UIs also favor Menu dropdowns over tap-target chip grids.

Settings / Preferences windows

SwiftUI's Settings { } and Window(_:id:) Scene types are hard-unavailable when compiling for Catalyst — a real compiler error, not a version gate. If you want a Preferences window (Cmd+,), your options are:

  1. A second WindowGroup(id:) opened via openWindow(id:), with CommandGroup(replacing: .appSettings) binding Cmd+,. This requires UIApplicationSceneManifest.UIApplicationSupportsMultipleScenes = true in Info.plist — Xcode injects it automatically for SwiftUI-lifecycle apps, but a custom Info.plist path bypasses that, so check yours.
  2. A sidebar destination — fold settings into the sidebar as a regular row and have Cmd+, select it:
.commands {
    CommandGroup(replacing: .appSettings) {
        Button("Preferences…") { sidebarSelection = .settings }
            .keyboardShortcut(",", modifiers: .command)
    }
}

Other Catalyst limitations to know about

Also verified by building against the SDK under Catalyst:

  • .menuStyle(.borderedButton) is unavailable under Catalyst — use the default menu style.
  • .toolbar item merging across a switched NavigationSplitView detail view can be unreliable (items silently disappearing on some destinations). If toolbar actions must survive detail-pane switches, render them in a persistent header view above the detail content instead.
  • None of these affect the SDK itself — they only constrain the host app UI you build around it.

Window sizing and the form overlay

The modal form presents over the topmost view controller of the active window scene, sized to that window — on the Mac that means it overlays your app's window, not the whole screen. Two implications:

  • Give your window a sensible floor so forms have room to render:
WindowGroup {
    ContentView()
        .frame(minWidth: 900, minHeight: 600)
}
.defaultSize(width: 1100, height: 720)

(.defaultSize and .windowResizability require a deployment target of iOS 17 / macOS 14 — see the note on targeting below.)

  • isFullScreen: true in EncatchConfig makes the overlay fill the window, not the display. The default (false) card-style presentation generally looks better on desktop.

Inline forms (EncatchInlineFormView) size themselves via onHeightChange exactly as on iOS and work unchanged in Catalyst layouts.

Theme

Encatch.shared.setTheme(_:) themes the SDK's own form content. Mac users notice when the form and the window disagree, so mirror the SDK theme onto your window's appearance:

extension Theme {
    var colorScheme: ColorScheme? {
        switch self {
        case .light: return .light
        case .dark: return .dark
        case .system: return nil   // follow the Mac's own appearance setting
        }
    }
}

// In your root view:
ContentView()
    .preferredColorScheme(currentEncatchTheme.colorScheme)

Deployment target

The SDK's floor is iOS 15 / macOS 12, but your Catalyst app can target higher than its dependency's minimum. Targeting iOS 17 gives you Table (sortable columns), .defaultSize/.windowResizability, and the two-parameter .onChange; Mac users tend to run current macOS versions, so a higher floor costs little reach.

Plain macOS (non-Catalyst) targets

In an AppKit or SwiftUI-for-Mac target (no UIKit), the SDK's core compiles and runs:

import Encatch

try await Encatch.shared.initialize(apiKey: "YOUR_API_KEY")
try await Encatch.shared.identifyUser(userName: "user@example.com")
try await Encatch.shared.trackEvent("exported_report")
try await Encatch.shared.trackScreen("EditorWindow")

What is not available without UIKit: EncatchFormHost, EncatchInlineFormView, and the WebView-based form rendering — so showForm has no presenter. If you need to collect responses in a plain macOS target, use the onBeforeShowForm interceptor pattern to receive the form's questionnaireFields and render your own AppKit/SwiftUI form, submitting via Encatch.shared.submitForm(_:) — see Build your own form UX on the iOS page. For the hosted form experience, ship a Catalyst target.


Support

Was this page helpful?