Profile
Back to NewsBack
GitHub Trending 10 min
Reader Mode
MortenGregersen/Bagbutik: Generated Swift code for the App Store Connect API based on the official OpenAPI spec.

MortenGregersen/Bagbutik: Generated Swift code for the App Store Connect API based on the official OpenAPI spec.

6 hours ago


Bagbutik

Generated Swift code for the App Store Connect API based on the official OpenAPI spec.

Swift versions Platforms
CI Documentation
X (Twitter) Mastodon

What is Bagbutik?

Bag·bu·tik (noun) /ˈb̥a̝w-ˌb̥u-tiːk/ - _(Danish) A room behind a store_

Bagbutik is two things, a command line tool for generating Swift code, and a library with the generated code for the App Store Connect API. The command line tool decodes the official OpenAPI spec for Apple's App Store Connect API.

While generating the code, the command line tool also downloads the official documentation from Apple's Documentation site for the App Store Connect API and applies it to the generated code, so you can get documentation right in Xcode.

The command line tool is only needed, when Apple publishes a new version of the spec. A GitHub Action automatically checks for new versions of the spec and creates a pull request with the changes.

How to use Bagbutik

Bagbutik uses JSON Web Tokens (JWT) for authorization. You obtain the required keys from your organization's App Store Connect account.

See Creating API Keys for App Store Connect API for how to create your keys.

Here is a basic example for fetching all bundle IDs for iOS apps including related profiles sorted by seed ID and the bundle ID itself descending.

import BagbutikCore
import BagbutikProvisioning

let service = BagbutikService(jwt: try .init( keyId: "P9M252746H", issuerId: "82067982-6b3b-4a48-be4f-5b10b373c5f2", privateKey: """ -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2 OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r 1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G -----END PRIVATE KEY----- """ )) let response = try await service.request( .listBundleIdsV1(filters: [.platform([.iOS])], includes: [.profiles], sorts: [.seedIdDescending, .idDescending]) ) print(response)

Project structure

The repository is split into a small manually maintained core and a large generated API surface.

  • BagbutikCore contains the request service, JWT support, shared response protocols, and the common models used across all API areas.
  • BagbutikModelsShared contains the small generated model groups shared by public API products.
  • BagbutikUsersModels and similar model modules are generated from actual schema references for their public API product.
  • BagbutikAppStore, BagbutikGameCenter, BagbutikTestFlight, BagbutikMarketplaces, BagbutikProvisioning, BagbutikReporting, and the other product modules contain generated endpoint builders grouped by App Store Connect domain.
  • Tools/Sources/BagbutikSpecDecoder decodes Apple's OpenAPI document into an intermediate Swift representation.
  • Tools/Sources/BagbutikDocsCollector mirrors Apple documentation as Markdown so generated code gets useful Xcode documentation comments.
  • Tools/Sources/BagbutikGenerator combines the decoded spec and collected docs to render the Swift source in Sources/.
This split matters when navigating the codebase. If you want to understand runtime behavior, start in the Sources/BagbutikCore folder. If you want to understand how generated endpoints and models are produced, start with the nested Tools package.

How to get Bagbutik into a project

Bagbutik is primarily distributed as a source based Swift package using Swift Package Manager.

For advanced use cases, a prebuilt XCFramework is also provided. Using the XCFramework is optional and does not replace the source based package.

Using Bagbutik as a source package

The library of generated code consists of multiple libraries, allowing consumers to depend only on the API areas they need.

Add Bagbutik as a dependency in your Package.swift:

dependencies: [
    .package(url: "https://github.com/MortenGregersen/Bagbutik", from: "24.0.0"),
]

Then add the required products to your target:

.target(
    name: "Awesome",
    dependencies: [
        .product(name: "BagbutikUsers", package: "Bagbutik"),
    ]
)

Select each public product your application uses for the smallest source build. Each product includes the core and generated model targets it requires. The source package also provides an optional Bagbutik umbrella product that imports every API area.

Available modules

Each module corresponds to a specific area of the App Store Connect API:

Endpoint pattern

Generated endpoints follow the same pattern throughout the package:

  1. Import BagbutikCore and the product module for the API area you need.
  2. Build a Request by calling a generated static helper such as .listBundleIdsV1(...).
  3. Send the request through BagbutikService.
  4. If the response type supports pagination, use requestNextPage(for:) or requestAllPages(_:).
This keeps each endpoint builder lightweight while centralizing authentication, decoding, and error handling in BagbutikService.

Using a prebuilt binary package

Bagbutik also provides a prebuilt static XCFramework distribution for projects that prefer not to compile Bagbutik sources. It uses the same public products and imports as the source package. Select the products your app uses, such as BagbutikProvisioning or BagbutikAppStore. The source package only Bagbutik umbrella product is not part of the binary distribution.

Add the binary package to your Package.swift with the explicit package name Bagbutik:

dependencies: [
    .package(
        name: "Bagbutik",
        url: "https://github.com/MortenGregersen/Bagbutik-Binary",
        from: "24.0.0"
    ),
]

Then use the same product declaration as the source package:

.target(
    name: "Awesome",
    dependencies: [
        .product(name: "BagbutikProvisioning", package: "Bagbutik"),
    ]
)

Use either the source package or the binary package in one target. Do not add both because they intentionally expose the same Swift module names. To change distribution, replace the package URL while keeping the explicit package name and selected products. The binary package and its release assets are published separately from the source repository.

For version 24 breaking changes, see the migration guide.

Maintaining generated code

Most files under Sources/ are generated from Apple's OpenAPI document. The generation pipeline is:

  1. Load and patch the OpenAPI spec.
  2. Fetch Apple documentation as a local Markdown mirror in Documentation/.
  3. Render endpoints and models into the package sources.
The manually maintained comments and types in BagbutikCore and the nested Tools package are the best places to look when you need to change generation behavior or understand why a generated file looks the way it does.

Run the maintainer CLI from the repository root with:

swift run --package-path Tools bagbutik-cli

The CLI and generator are not part of the runtime package consumed by applications.

Manual patches applied to OpenAPI Spec

The OpenAPI Spec provided by Apple do not always align with the data received from the API. Whenever such mismatches are identified, feedback is submitted to Apple and a patch is applied. Whenever an issue is resolved, the patch is removed again.

Currently open feedback and applied patches

FB8977648: The "BundleIdPlatform" schema is missing "UNIVERSAL" and "SERVICES"

  • Submitted: January 21st 2021.
  • Updated: October 14th 2022 - the type "SERVICES" is also missing.
  • Partly resolved: November 12th 2024 (Spec version 3.7) - "UNIVERSAL" added.
Title: App Store Connect API is missing the "UNIVERSAL" type for the BundleIdPlatform schema

##### Description

In the OpenAPI spec for the App Store Connect API the "BundleIdPlatform" schema is said to only be "IOS" or "MAC_OS". This is not right as universal apps (iOS and macOS) has a "UNIVERSAL" platform.

FB12292035: ErrorResponse.Errors has required optional "detail" and no "associatedErrors" in "meta"

  • Submitted: June 9th 2023.
In Apple's OpenAPI spec the detail property on ErrorResponse.Errors is marked as required. On 12/1/23 some errors (with status code 409) has been observed, with no detail.

In Apple's OpenAPI spec and documentation the associatedErrors is not mentioned in meta property (last checked 12/1/23). But it is observed when creating a ReviewSubmissionItem with an AppStoreVersion fails.

FB22529824: App Store Connect API Spec is missing the "see" property on ErrorLinks

  • Submitted: April 17th, 2026.
In the OpenAPI spec for the App Store Connect API, the ErrorLinks schema does not include a see property.

However, the live API returns errors[].links.see on some ErrorResponse.Errors payloads. One observed case is a 403 error with code FORBIDDEN.REQUIRED_AGREEMENTS_MISSING_OR_EXPIRED, where links.see is set to /business.

FB16908301: App Store Connect API Spec is missing list of possible "PurchaseRequirement" on AppEvent

  • Submitted: March 17th, 2025.
In the OpenAPI spec (and documentation) for the App Store Connect API the purchaseRequirement property on the “AppEvent” schema is a string. As a consumer of the API, we don’t know the possible values of this could be. After doing some research, it looks like there are two options NO_COST_ASSOCIATED and IN_APP_PURCHASE.

FB25217452: AppAssetLibraryPlacementState is missing "ACTIVE" and "INACTIVE"

  • Submitted: October 16th, 2026.
In the OpenAPI specification for App Store Connect API 4.5.1, the AppAssetLibraryPlacementState schema does not include ACTIVE or INACTIVE. However, the API returns both values in the state attribute of appAssetLibraryPlacements resources.

This causes clients generated from the specification to fail when decoding otherwise successful responses, preventing them from reading existing asset placements. The manual patch adds both values to the schema and placement state filters.

FB25371939: AppAssetLibraryPlacementState is missing "PENDING"

  • Submitted: October 30th, 2026.
Successful placement creation can return PENDING, which the App Store Connect OpenAPI specification 4.5.1 omits. Clients generated from the spec can fail to decode HTTP 201 responses after Apple has already created the placement. The manual patch adds the state to the schema and placement filters. Feedback description.

FB25217789: AppAssetLibraryPlacement relationships has a misplaced discriminator

Submitted on October 16, 2026 for API 4.5.1. Feedback description. The relationships schema discriminates on mediaType, but successful responses supply that field in attributes. Responses contain image and video relationship keys, with null data for the unused relationship. The response schema is patched to expose both optional relationships and preserve their linkage without requiring a discriminator inside relationships.

FB25354273: Bundle ID capabilities relationship rejects limit

  • Observed: October 28th, 2026.
  • Submitted: October 28th, 2026.
Apple's spec declares a limit query parameter for GET /v1/bundleIds/{id}/bundleIdCapabilities. The live API rejects a request with limit=200 with HTTP 400 and code PARAMETER_ERROR.ILLEGAL: "The parameter 'limit' can not be used with this request : This relationship does not support this parameter."

The spec loader removes this parameter from that operation so the generated helper does not expose it.

Closed feedback (removed patches)

  • FB9963088: The xcodeMetrics schema has no properties or attributes in the OpenAPI spec
* Submitted: March 21st 2022. * Resolved: July 12th 2022 (Spec version 2.0).
  • FB10029609: Required 'links' property missing on included BuildBundle
* Submitted: May 27th 2022. * Resolved: At least since May 23rd 2023.
  • FB13071298: App Store Connect API Spec is missing "VISION_OS" type for the Platform schema
* Submitted: August 28th 2023. * Resolved: January 25th 2024 (Spec version 3.2).
  • FB13539766: App Store Connect API Spec is missing "APP_APPLE_VISION_PRO" type for the Screenshot Display Type schema
* Submitted: January 14th 2024. * Resolved: January 25th 2024 (Spec version 3.2).
  • FB13540097: Almost all of the schemas ending in “WithoutIncludesResponse” has wrong "data" type
* Submitted: January 14th 2024. * Resolved: July 11th 2024 (Spec version 3.5).
  • FB13701181: App Store Connect API Spec is missing "DEVELOPER_ID_APPLICATION_G2" type for the Certificate Type schema
* Submitted: March 28th 2024. * Resolved: November 12th 2024 (Spec version 3.7).
  • FB15681740: App Store Connect API Spec is missing "APPLE_VISION_PRO" type for the Device class type schema
* Submitted: November 4th 2024. * Resolved: June 10th 2025 (Spec version 4.0).
  • FB17874677: The "AgeRatingDeclaration.Attributes" properties is missing the values "INFREQUENT_OR_MILD" and "FREQUENT_OR_INTENSE"
* Submitted: June 10th 2025. * Resolved: October 9th 2025 (Spec version 4.1).
  • FB17925890: The "ageRatingOverride" property of "AgeRatingDeclaration.Attributes" is missing
* Submitted: June 11th 2025. * Resolved: October 9th 2025 (Spec version 4.1).
  • FB17932433: The "totalNumberOfCodes" property of "SubscriptionOfferCode.Attributes" is missing
* Submitted: June 11th 2025. * Resolved: October 9th 2025 (Spec version 4.1).
  • FB16699896: App Store Connect API is missing "XKS" (Kosovo) for the TerritoryCode enum
* Submitted: March 3rd, 2025. * Resolved: December 16th, 2025 (Spec version 4.2).
Chat with me