Skip to content
6 min read Guides Swift

What's New in Swift SDK 1.3

Swift SDK 1.3 was tagged on 14 October 2026, under an hour after Swift SDK 1.2. It changes how the board looks, where its navigation lives and how its settings are reached from Swift 6 code, and it fixes defects that had been in the SDK since 1.0. If you are deciding which version to adopt, this is the one.

This release was originally tagged 3.0.0 on 14 October 2026 and renumbered on 17 October 2026 as 1.3.0, and it contains source-breaking changes despite the minor version number (the migration notes list each one).

The short version

  • New look: the Showcase list draws each request as a card. listStyle = .classic keeps the 1.2 list.
  • New menu: one Sort & Filter menu with Status and Category pickers, a "Filtered by" row, and filtering on the device.
  • New navigation: FeedbackListView(embedsNavigationStack:) for use inside your own stack, and a split view on iPad.
  • Renamed: config, theme and shared are now configuration, appearance and current, on the main actor. The old names still compile, with a warning.
  • Fixed: status and category filters reach the server, updateUser(customID:) takes effect at once, logs carry no personal data, and offline failures surface as networkError.

The Showcase list

FeedbackListView now draws Showcase cards by default. Each card has a status line at the top, the title, the whole description, a footer with the category and comment count, and a vote capsule. A Completed or Rejected request shows its vote count as plain text, and VoiceOver says that voting is closed. In Showcase the category is plain text, and appearance.categoryColors keeps colouring Classic rows and the detail screen.

To keep the 1.2 look, set the style before the list appears:

SwiftlyFeedback.configuration.listStyle = .classic

The list now draws supporting text with the system's secondary style and the vote capsule on a system fill, so light mode, dark mode and Increase Contrast work without extra colours. primaryColor and the status colours are used as you set them. The Improvement category's icon is now slider.horizontal.3, so it no longer looks like the vote arrow.

Sort & Filter

The toolbar has one Sort & Filter menu. It holds the sort order, a Status picker, a Category picker and, while a filter is on, Clear Filters. When a filter is active the icon fills, and a "Filtered by" row with a Clear button sits at the top of the list. A filter that matches nothing shows a No Matching Requests screen. The same menu also lets the reader pick the language that feedback is translated into on the device, where 1.2 always used the device language.

Filters now run on the device. The list loads once and filters what it has, so switching a filter no longer reloads. A new setting hides the Category picker, as statusFilter already hides the Status one:

SwiftlyFeedback.configuration.buttons.categoryFilter.display = false

Navigation and layout

On iPhone, FeedbackListView still brings its own NavigationStack. If you push it onto a stack you already own, pass embedsNavigationStack: false so there is one navigation bar, and the cards push the detail screen onto your stack:

NavigationStack {
    Form {
        NavigationLink("Feature requests") {
            FeedbackListView(embedsNavigationStack: false)
        }
    }
}

On iPad at regular width the list is now a split view, with the selected request beside it. The submit form moves Submit and Cancel from the toolbar to the bottom of the form on iPhone and iPad. On the Mac they stay in the toolbar, and Command-Return still submits.

Fixes

All five were present since 1.0.

  • Filters reach the server. getFeedback(status:) and getFeedback(category:) used to append the query to the path, where appendingPathComponent escaped the ?. The server saw a route that does not exist and answered 404. Queries are now built with URLComponents, so filter values arrive intact.
  • updateUser(customID:) changes the id on the live client. It used to save the id to the Keychain while requests kept the old one until the next launch. An id set before configure finishes is applied when the client comes up.
  • Logs no longer contain personal data. Request and response bodies, with emails, user ids and MRR, used to be logged. Log lines now carry the method, the path and the status code.
  • Offline and timeout failures are SwiftlyFeedbackError.networkError. They used to surface as URLError, so a catch for networkError never matched. Each request now times out after 30 seconds. The SDK does not retry, so a timed-out vote or submit is not sent a second time.
  • One unreadable row no longer blanks the list. A request with a status this build does not know is skipped and the rest are shown.

Breaking changes and how to migrate

Swift 6 language mode and main-actor settings

The package now compiles in Swift 6 language mode. Your app can stay in Swift 5 mode. The three global stores are now main-actor properties with new names:

// Swift SDK 1.2
SwiftlyFeedback.config.allowUndoVote = false
SwiftlyFeedback.theme.primaryColor = .adaptive(light: .green, dark: .mint)
let client = SwiftlyFeedback.shared

// Swift SDK 1.3, on the main actor
SwiftlyFeedback.configuration.allowUndoVote = false
SwiftlyFeedback.appearance.primaryColor = .adaptive(light: .green, dark: .mint)
let client = SwiftlyFeedback.current

The old names are deprecated shims that forward to the new properties, so existing code builds with warnings. Because they are main-actor isolated now, reach them from your App initialiser, a view or another @MainActor type.

The Strings enum is gone

1.2 removed six members of the public Strings enum. 1.3 removes the enum. If you borrowed its members to label your own screens, use your own strings. The package now generates its localised string symbols with a build plugin, so Xcode asks you once to trust it, and xcodebuild on CI needs -skipPackagePluginValidation.

New error cases

A 403 refusal for a role, access or suspension reason used to arrive as serverError(statusCode: 403) and is now forbidden(message:). A 429 is now rateLimited(message:), so you can back off instead of treating it as a server fault. Two more cases exist that the feedback, vote and comment calls do not raise. An exhaustive switch over SwiftlyFeedbackError needs the new cases or a default:

func message(for error: SwiftlyFeedbackError) -> String {
    switch error {
    case .networkError:
        "You appear to be offline."
    case .rateLimited:
        "Too many requests. Try again in a moment."
    case .forbidden(let message):
        message ?? "This action is not allowed."
    default:
        error.localizedDescription
    }
}

Deprecated

expandDescriptionInList has no effect, because both list styles show the whole description. Delete the line. It goes in the next major release.

Additive API

getFeedback and getComments(for:) gained page and perPage parameters. Leave page out and every page is read and joined, as before.

How FeedbackKit fits in

The cards make the board easier to scan, which matters when a request has hundreds of votes behind it. Here is how voting looks in FeedbackKit, with sample data from a fictional hiking app:

Votes from inside the app

Users vote and comment inside your app. A new request starts with its submitter's vote.

Upgrading

From 1.2: an Up to Next Major Version rule from 1.2.0 already moves to 1.3.0 on the next package update, so plan for the changes below. Set the lower bound to 1.3.0, rename config, theme and shared when the warnings appear, add the new error cases to any exhaustive switch, and decide whether you want Showcase or Classic. If you worked around the 404 on filtered reads, drop the workaround.

From 1.1.1: do the 1.2 migrations as well. buttons.segmentedControl became buttons.statusFilter, status and category colours became ThemeColor (wrap them in .color(_:)), and secondaryColor, tertiaryColor and bottomPadding were removed. The migration notes in the repository list each change from 1.1.1.

The SwiftUI tutorial walks through a full integration on 1.3, and the reference is in the Swift SDK docs. Apps that must stay on 1.2 for now can pin Up to Next Minor Version from 1.2.0 and read the notes for 1.2.

Every plan includes the Swift SDK. Start free and put the new board in your next TestFlight build.

Keep reading

Collect feedback in your own app

FeedbackKit ships SDKs for Swift, Kotlin, React Native, Flutter and JavaScript, and syncs what your users ask for into the tracker your team already uses.