{"id":13461769,"url":"https://github.com/apple/sample-food-truck","last_synced_at":"2025-10-09T16:43:21.336Z","repository":{"id":37745590,"uuid":"498435413","full_name":"apple/sample-food-truck","owner":"apple","description":"SwiftUI sample code from WWDC22","archived":false,"fork":false,"pushed_at":"2023-08-18T13:14:11.000Z","size":25372,"stargazers_count":1775,"open_issues_count":1,"forks_count":155,"subscribers_count":44,"default_branch":"main","last_synced_at":"2025-05-13T12:52:07.580Z","etag":null,"topics":["swift","swiftui"],"latest_commit_sha":null,"homepage":"https://developer.apple.com/documentation/swiftui/food_truck_building_a_swiftui_multiplatform_app","language":"Swift","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/apple.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-05-31T17:34:10.000Z","updated_at":"2025-05-13T01:39:43.000Z","dependencies_parsed_at":"2024-01-06T17:46:00.294Z","dependency_job_id":"d32c6631-ee0c-49a0-b636-c5c43d51b816","html_url":"https://github.com/apple/sample-food-truck","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apple%2Fsample-food-truck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apple%2Fsample-food-truck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apple%2Fsample-food-truck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apple%2Fsample-food-truck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/apple","download_url":"https://codeload.github.com/apple/sample-food-truck/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254448579,"owners_count":22072764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["swift","swiftui"],"created_at":"2024-07-31T11:00:57.163Z","updated_at":"2025-10-09T16:43:16.305Z","avatar_url":"https://github.com/apple.png","language":"Swift","funding_links":[],"categories":["Swift","HarmonyOS","Misc"],"sub_categories":["Windows Manager","SwiftUI","macOS"],"readme":"# Food Truck: Building a SwiftUI multiplatform app\n\nCreate a single codebase and app target for Mac, iPad, and iPhone.\n\n## Overview\n\nUsing the Food Truck app, someone who operates a food truck can keep track of orders, discover the most-popular menu items, and check the weather at their destination. The sample implements the new [`NavigationSplitView`](https://developer.apple.com/documentation/swiftui/navigationsplitview) to manage the app's views, [`Layout`](https://developer.apple.com/documentation/swiftui/layout) to show the main interface and pending orders, [`Charts`](https://developer.apple.com/documentation/charts) to show trends, and [`WeatherService`](https://developer.apple.com/documentation/weatherkit/weatherservice) to get weather data. Food Truck also implements Live Activities to show the remaining order preparation time with [ActivityKit](https://developer.apple.com/documentation/activitykit) on the lock screen, and with [`DynamicIsland`](https://developer.apple.com/documentation/widgetkit/dynamicisland) on the home screen.\n\nYou can access the source code for this sample on [GitHub](https://github.com/apple/sample-food-truck).\n\n- Note: This sample code project is associated with WWDC22 session [110492: State of the Union](https://developer.apple.com/wwdc22/110492/).\n\nThe Food Truck sample project contains two types of app targets:\n\n- Simple app target you can build using [personal team](https://help.apple.com/xcode/mac/11.4/#/dev17411c009) signing. This app runs in Simulator, and only requires a standard Apple ID to install on a device. It includes in-app purchase, and a widget extension that enable users to add a widget to their iOS Home Screen or the macOS Notification Center.\n\n- Full-featured Food Truck All app target. The full app runs in Simulator, and on devices with an Apple Developer membership.  It also allows you to create and sign in with passkeys.\n\n## Configure the sample code project\n\nTo configure the Food Truck app without an Apple Developer account, follow these steps:\n\n1. In the Food Truck target's Signing \u0026 Capabilities panes click Add Account, and log in with your Apple ID.\n2. Chose Your Name (Personal Team) from the team menu for the Food Truck and Widgets targets.\n3. Build and run your app.\n3. On iOS and iPadOS devices navigate to Settings \u003e General \u003e VPN \u0026 Device Management and trust your developer certificate.\n\nTo configure the Food Truck All app to run on your devices, follow these steps:\n\n1. Open the sample with Xcode 14.3 or later.\n2. Select the top-level Food Truck project.\n3. For all targets, choose your team from the Team menu in the Signing \u0026 Capabilities pane, so Xcode can automatically manage your provisioning profile.\n4. Add the Associated Domains capability, and specify your domain with the `webcredentials` service. For more information about the `webcredentials` service, see [Associated Domains Entitlement](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_associated-domains).\n5. Ensure an `apple-app-site-association` (AASA) file is present on your domain, in the `.well-known` directory, and it contains an entry for this app’s App ID for the `webcredentials` service. For more information about the `apple-app-site-association` file, see [Supporting Associated Domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains).\n6. In the `AccountManager.swift` file, replace all occurrences of `example.com` with the name of your domain.\n\n- Note: To use the weather forecast feature in the sample, you need to perform additional steps to configure WeatherKit, as described in the Configure the project for WeatherKit section below, or the sample will detect an error and use static data included in the project.\n\n## Create a multiplatform app\n\nFood Truck is a multiplatform app, and there are no separate targets to run on macOS or iOS. Instead, there is only one app target that builds for macOS, iPadOS, and iOS.\n\n## Define a default navigation destination\n\nThe sample's navigation interface consists of a [`NavigationSplitView`](https://developer.apple.com/documentation/swiftui/navigationsplitview) with a `Sidebar` view, and a [`NavigationStack`](https://developer.apple.com/documentation/swiftui/navigationstack):\n\n``` swift\nNavigationSplitView {\n    Sidebar(selection: $selection)\n} detail: {\n    NavigationStack(path: $path) {\n        DetailColumn(selection: $selection, model: model)\n    }\n}\n```\n\nAt app launch, the sample presents the `TruckView` as the default view. The `Panel` enum encodes the views the user can select in the sidebar, and hence appear in the detail view. The value corresponding to `TruckView` is `.truck`, and the app sets this to be the default selection.\n\n``` swift\n@State private var selection: Panel? = Panel.truck\n```\n\n## Construct a dynamic layout\n\nIn the Truck view, the New Orders panel shows the five most-recent orders, and each order shows a `DonutStackView`, which is a diagonal stack of donut thumbnails. The [`Layout`](https://developer.apple.com/documentation/swiftui/layout) protocol allows the app to define a `DiagonalDonutStackLayout` that arranges the donut thumbnails into the diagonal layout. The layout's [`placeSubviews(in:proposal:subviews:cache:)`](https://developer.apple.com/documentation/swiftui/layout/placesubviews(in:proposal:subviews:cache:)) implementation calculates the donuts' positions.\n\n``` swift\nfor index in subviews.indices {\n    switch (index, subviews.count) {\n    case (_, 1):\n        subviews[index].place(\n            at: center,\n            anchor: .center,\n            proposal: ProposedViewSize(size)\n        )\n        \n    case (_, 2):\n        let direction = index == 0 ? -1.0 : 1.0\n        let offsetX = minBound * direction * 0.15\n        let offsetY = minBound * direction * 0.20\n        subviews[index].place(\n            at: CGPoint(x: center.x + offsetX, y: center.y + offsetY),\n            anchor: .center,\n            proposal: ProposedViewSize(CGSize(width: size.width * 0.7, height: size.height * 0.7))\n        )\n    case (1, 3):\n        subviews[index].place(\n            at: center,\n            anchor: .center,\n            proposal: ProposedViewSize(CGSize(width: size.width * 0.65, height: size.height * 0.65))\n        )\n        \n    case (_, 3):\n        let direction = index == 0 ? -1.0 : 1.0\n        let offsetX = minBound * direction * 0.15\n        let offsetY = minBound * direction * 0.23\n        subviews[index].place(\n            at: CGPoint(x: center.x + offsetX, y: center.y + offsetY),\n            anchor: .center,\n            proposal: ProposedViewSize(CGSize(width: size.width * 0.7, height: size.height * 0.65))\n        )\n```\n\n## Display a chart of popular items\n\nThe sample contains several charts. The most popular items are shown on the `TopFiveDonutsView`. This chart is implemented in `TopDonutSalesChart`, which uses a [`BarMark`](https://developer.apple.com/documentation/charts/barmark) to construct a bar chart.\n\n``` swift\nChart {\n    ForEach(sortedSales) { sale in\n        BarMark(\n            x: .value(\"Donut\", sale.donut.name),\n            y: .value(\"Sales\", sale.sales)\n        )\n        .cornerRadius(6, style: .continuous)\n        .foregroundStyle(.linearGradient(colors: [Color(\"BarBottomColor\"), .accentColor], startPoint: .bottom, endPoint: .top))\n        .annotation(position: .top, alignment: .top) {\n            Text(sale.sales.formatted())\n                .padding(.vertical, 4)\n                .padding(.horizontal, 8)\n                .background(.quaternary.opacity(0.5), in: Capsule())\n                .background(in: Capsule())\n                .font(.caption)\n        }\n    }\n}\n```\n\nThe *x* axis of the chart shows labels with the names and thumbnails of the items that correspond to each data point.\n\n``` swift\n.chartXAxis {\n    AxisMarks { value in\n        AxisValueLabel {\n            let donut = donutFromAxisValue(for: value)\n            VStack {\n                DonutView(donut: donut)\n                    .frame(height: 35)\n                    \n                Text(donut.name)\n                    .lineLimit(2, reservesSpace: true)\n                    .multilineTextAlignment(.center)\n            }\n            .frame(idealWidth: 80)\n            .padding(.horizontal, 4)\n            \n        }\n    }\n}\n```\n\n## Obtain a weather forecast\n\nThe app shows a forecasted temperature graph in the Forecast panel in the Truck view. The app obtains this data from the [`WeatherKit`](https://developer.apple.com/documentation/weatherkit) framework.\n\n``` swift\n.task(id: city.id) {\n    for parkingSpot in city.parkingSpots {\n        do {\n            let weather = try await WeatherService.shared.weather(for: parkingSpot.location)\n            condition = weather.currentWeather.condition\n            willRainSoon = weather.minuteForecast?.contains(where: { $0.precipitationChance \u003e= 0.3 })\n            cloudCover = weather.currentWeather.cloudCover\n            temperature = weather.currentWeather.temperature\n            symbolName = weather.currentWeather.symbolName\n            \n            let attribution = try await WeatherService.shared.attribution\n            attributionLink = attribution.legalPageURL\n            attributionLogo = colorScheme == .light ? attribution.combinedMarkLightURL : attribution.combinedMarkDarkURL\n            \n            if willRainSoon == false {\n                spot = parkingSpot\n                break\n            }\n        } catch {\n            print(\"Could not gather weather information...\", error.localizedDescription)\n            condition = .clear\n            willRainSoon = false\n            cloudCover = 0.15\n        }\n    }\n}\n```\n\n## Configure the project for WeatherKit\n\nThe data from the [`WeatherService`](https://developer.apple.com/documentation/weatherkit/weatherservice) instance in WeatherKit requires additional configuration for the Food Truck All target. If you don't configure WeatherKit, the sample will detect an error and use static data in the project instead.\n\n1. Create a unique App ID on the [Provisioning Portal](https://developer.apple.com/account/resources/certificates/list), and select the WeatherKit service on the App Services tab.\n2. In Xcode, for the Food Truck All target on the Signing \u0026 Capabilities tab, change the bundle ID to be the same as the App ID from step 1, and add the WeatherKit capability.\n3. For the Widgets target on the Signing \u0026 Capabilities tab, change the bundle ID to make the part before `.Widgets` the same as the bundle ID for the Food Truck All target.\n3. Wait 30 minutes while the service registers your app's bundle ID.\n4. Build and run the Food Truck All target.\n\n## Track preparation time with Live Activity\nThe app allows the food truck operator to keep track of order preparation time, which is guaranteed to be 60 seconds or less. To facilitate this, the app implements a toolbar button on the order details screen for orders with `placed` status. Tapping this button changes the order status to `preparing`, and creates an [`Activity`](https://developer.apple.com/documentation/activitykit/activity) instance to start a Live Activity, which shows the countdown timer and order details on an iPhone lock screen.\n\n``` swift\nlet timerSeconds = 60\nlet activityAttributes = TruckActivityAttributes(\n    orderID: String(order.id.dropFirst(6)),\n    order: order.donuts.map(\\.id),\n    sales: order.sales,\n    activityName: \"Order preparation activity.\"\n)\n\nlet future = Date(timeIntervalSinceNow: Double(timerSeconds))\n\nlet initialContentState = TruckActivityAttributes.ContentState(timerRange: Date.now...future)\n\nlet activityContent = ActivityContent(state: initialContentState, staleDate: Calendar.current.date(byAdding: .minute, value: 2, to: Date())!)\n\ndo {\n    let myActivity = try Activity\u003cTruckActivityAttributes\u003e.request(attributes: activityAttributes, content: activityContent,\n        pushType: nil)\n    print(\" Requested MyActivity live activity. ID: \\(myActivity.id)\")\n    postNotification()\n} catch let error {\n    print(\"Error requesting live activity: \\(error.localizedDescription)\")\n}\n```\n\nThe app also implements [`DynamicIsland`](https://developer.apple.com/documentation/widgetkit/dynamicisland) to show the same information as on the lock screen in the Dynamic Island on iPhone 14 Pro devices.\n\n``` swift\nDynamicIsland {\n    DynamicIslandExpandedRegion(.leading) {\n        ExpandedLeadingView()\n    }\n\n    DynamicIslandExpandedRegion(.trailing, priority: 1) {\n        ExpandedTrailingView(orderNumber: context.attributes.orderID, timerInterval: context.state.timerRange)\n            .dynamicIsland(verticalPlacement: .belowIfTooWide)\n    }\n} compactLeading: {\n    Image(\"IslandCompactIcon\")\n        .padding(4)\n        .background(.indigo.gradient, in: ContainerRelativeShape())\n       \n} compactTrailing: {\n    Text(timerInterval: context.state.timerRange, countsDown: true)\n        .monospacedDigit()\n        .foregroundColor(Color(\"LightIndigo\"))\n        .frame(width: 40)\n} minimal: {\n    Image(\"IslandCompactIcon\")\n        .padding(4)\n        .background(.indigo.gradient, in: ContainerRelativeShape())\n}\n.contentMargins(.trailing, 32, for: .expanded)\n.contentMargins([.leading, .top, .bottom], 6, for: .compactLeading)\n.contentMargins(.all, 6, for: .minimal)\n.widgetURL(URL(string: \"foodtruck://order/\\(context.attributes.orderID)\"))\n```\n\nTapping the same button again changes the status to `complete`, and ends the Live Activity. This removes the Live Activity from the lock screen and from the Dynamic Island.\n\n``` swift\nTask {\n    for activity in Activity\u003cTruckActivityAttributes\u003e.activities {\n        // Check if this is the activity associated with this order.\n        if activity.attributes.orderID == String(order.id.dropFirst(6)) {\n            await activity.end(nil, dismissalPolicy: .immediate)\n        }\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapple%2Fsample-food-truck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapple%2Fsample-food-truck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapple%2Fsample-food-truck/lists"}