iOS app extensions - widgets and share

Sofia Martinez Jan 2026
2 tabs
import WidgetKit
import SwiftUI

struct SimpleEntry: TimelineEntry {
    let date: Date
    let count: Int
    let message: String
}

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), count: 0, message: "Placeholder")
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        let entry = SimpleEntry(date: Date(), count: 42, message: "Snapshot")
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
        var entries: [SimpleEntry] = []
        let currentDate = Date()

        // Generate timeline entries for next 5 hours
        for hourOffset in 0..<5 {
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(
                date: entryDate,
                count: hourOffset * 10,
                message: "Update \(hourOffset)"
            )
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleWidgetEntryView: View {
    var entry: Provider.Entry
    @Environment(\.widgetFamily) var family

    var body: some View {
        switch family {
        case .systemSmall:
            SmallWidgetView(entry: entry)
        case .systemMedium:
            MediumWidgetView(entry: entry)
        case .systemLarge:
            LargeWidgetView(entry: entry)
        default:
            SmallWidgetView(entry: entry)
        }
    }
}

struct SmallWidgetView: View {
    let entry: SimpleEntry

    var body: some View {
        VStack {
            Text(entry.message)
                .font(.headline)
            Text("\(entry.count)")
                .font(.largeTitle)
                .fontWeight(.bold)
        }
        .padding()
    }
}

struct MediumWidgetView: View {
    let entry: SimpleEntry

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text(entry.message)
                    .font(.headline)
                Text(entry.date, style: .time)
                    .font(.caption)
            }
            Spacer()
            Text("\(entry.count)")
                .font(.system(size: 40, weight: .bold))
        }
        .padding()
    }
}

struct LargeWidgetView: View {
    let entry: SimpleEntry

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text(entry.message)
                .font(.title)
                .fontWeight(.bold)

            Text("Count: \(entry.count)")
                .font(.title2)

            Text(entry.date, style: .time)
                .font(.caption)
                .foregroundColor(.secondary)

            Spacer()
        }
        .padding()
    }
}

@main
struct SimpleWidget: Widget {
    let kind: String = "SimpleWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            SimpleWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This displays useful information.")
        .supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
    }
}
2 files · swift Explain with highlit

App extensions expand app functionality into other parts of iOS. Today widgets display glanceable information using WidgetKit with SwiftUI views and timeline providers. Share extensions let users share content to your app from other apps—I process shared items with NSExtensionContext and NSItemProvider. Widget timelines define when to update content with TimelineEntry and TimelineProvider. For widgets, I use @main with Widget protocol and provide configurations. App groups enable data sharing between the main app and extensions via shared containers. Notification content extensions customize notification UI. Action extensions process selected content. Each extension runs in its own process with memory constraints.