iOS · SwiftUI · SDK 0.0.24

# Feature Plans

Let app users explore your plans, vote on features, and follow your roadmap.

Complete the [iOS quickstart](https://docs.mobilejoe.dev/ios/quickstart.md) first. These examples use the configured app and the `MobileJoeUI` package product.

## Create a feature plan

1. Select your app in the [dashboard](https://app.mobilejoe.dev/) and open **Feature Plans**.
2. Create a plan with a title and description. Keep its status **Open** for your first test.
3. Choose **Save**. Feature plans have no separate publishing step; saved plans are available through the API.

## Show the list

Add this screen to your app, or move its navigation link into your existing navigation stack:

```swift
import SwiftUI
import MobileJoeUI

struct FeedbackView: View {
  var body: some View {
    NavigationStack {
      List {
        NavigationLink("Feature Plans") {
          FeaturePlansView(
            configuration: .standard(recipient: "support@example.com"),
            presentationMode: .inNavigationStack
          )
        }
      }
      .navigationTitle("Feedback")
    }
  }
}
```

`FeaturePlansView` loads data, fetches additional pages while scrolling, and supports pull to refresh. Tapping a plan opens its details. The vote button adds a vote; tapping it again removes that vote.

Search matches plan titles and descriptions. The toolbar menu offers newest-first or score sorting, individual status filters, and a roadmap containing **Under review** , **Planned** , and **In progress**. The default **All** filter includes those statuses plus **Open** ; choose **Completed** to see completed plans. Closed plans are absent from these list filters.

## Present a sheet

Use `.asSheet` to get the SDK's navigation stack and close button:

```swift
import SwiftUI
import MobileJoeUI

struct FeaturePlansButton: View {
  @State private var showingPlans = false

  var body: some View {
    Button("Feature Plans") { showingPlans = true }
      .sheet(isPresented: $showingPlans) {
        FeaturePlansView(
          configuration: .standard(recipient: "support@example.com"),
          presentationMode: .asSheet
        )
      }
  }
}
```

## Suggestions and deep links

Replace `support@example.com` with your support address. The suggestion button opens the user's email app; it does not create a feature plan. Use `.standard(recipient: "")` to hide it. For custom localized email content, use `FeaturePlansView.Configuration(recipient:subject:body:)`.

To open a specific plan, pass its integer ID as `selectedFeaturePlanID:` after `presentationMode:`. The view loads that plan and opens its details. Your app handles incoming URLs and extracts the ID. In the dashboard, open **Feature Plans** , click the settings gear, and save the **Deep link base URL**. The dashboard appends `/ID` when you copy a plan's deep link.

## Use your own UI

Import `MobileJoe` and keep an observable `FeaturePlans()` model in `@State`. Use its `all` array for your list. Call `try await featurePlans.load()` for the next page, `reload()` to restart, `vote(plan)` to toggle a vote, or `featurePlan(id:)` to fetch one plan. Set `sorting` and `filtering`, or use `search(for:)` for debounced search. Use these APIs on the main actor after configuration and handle thrown errors.

## Verify

Open your saved plan, vote twice, and confirm the score returns to its starting value. Change its dashboard status to **Planned** , refresh, and check the roadmap filter. If missing, check the selected filter and that the plan and SDK key belong to the same app.
