Menu
×
   ❮     
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS DSA TYPESCRIPT ANGULAR ANGULARJS GIT POSTGRESQL MONGODB ASP AI R GO KOTLIN SWIFT SASS VUE GEN AI SCIPY CYBERSECURITY DATA SCIENCE INTRO TO PROGRAMMING BASH RUST

Swift Basics

Swift HOME Swift Intro Swift Get Started Swift Syntax Swift Statements Swift Output Swift Comments Swift Variables Swift Data Types Swift Type Casting Swift Operators Swift Strings Swift Arrays Swift Ranges Swift If...Else Swift Switch Swift While Loop Swift For Loop Swift Break/Continue Swift Collections

Swift Types & Functions

Swift Functions Swift Optionals Swift Enums & Patterns Swift Closures Tuples & Type Aliases

Swift Object Model

Swift OOP Swift Inheritance Swift Polymorphism Swift Protocols Swift Generics Swift Extensions Access Control Initializers Deinitializers Value Semantics & COW Equatable & Comparable

Swift Robustness & Async

Swift Error Handling Swift Concurrency Swift Memory

Swift Tooling

Swift Package Manager

SwiftUI Basics

SwiftUI Intro iOS Project Setup SwiftUI Layout SwiftUI Navigation SwiftUI Data Flow SwiftUI Lists & Forms SwiftUI Animations SwiftUI Gestures SwiftUI Modifiers & ViewBuilder SwiftUI Previews SwiftUI Accessibility SwiftUI Styling & Theming

SwiftUI Data & Architecture

Networking Persistence Persistence (Core Data) MVVM Architecture AppStorage & SceneStorage Testing SwiftUI

iOS Capabilities

Privacy & Permissions Push Notifications Widgets & Extensions Background Work Core Location App Clips Keychain Basics CloudKit File System Background URLSession MapKit

iOS Quality & Compliance

Localization Accessibility App Privacy In-App Purchases Analytics & Reporting Testing with XCTest

iOS Release & Distribution

Assets & App Icons Signing & Distribution TestFlight & App Store Ship Your First App

Swift Exercises

Swift Exercises Swift Quiz

Widgets & App Extensions


Widgets & App Extensions

Share small data with App Groups and build Widgets using WidgetKit to show quick glanceable information.


Widget timeline

Build a widget that shows a timeline of time entries.

Syntax:

  • TimelineProvider protocol to define the timeline provider

Example

import WidgetKit
import SwiftUI

struct TimeEntry: TimelineEntry { let date: Date }

struct TimeProvider: TimelineProvider {
  func placeholder(in context: Context) -> TimeEntry { .init(date: Date()) }
  func getSnapshot(in context: Context, completion: @escaping (TimeEntry) -> Void) { completion(.init(date: Date())) }
  func getTimeline(in context: Context, completion: @escaping (Timeline<TimeEntry>) -> Void) {
    let entries = stride(from: 0, through: 60*30, by: 60).map { offset in
      TimeEntry(date: Date().addingTimeInterval(Double(offset)))
    }
    completion(Timeline(entries: entries, policy: .atEnd))
  }
}

struct TimeWidgetView: View {
  var entry: TimeEntry
  var body: some View { Text(entry.date, style: .time).font(.headline) }
}

@main
struct TimeWidget: Widget {
  var body: some WidgetConfiguration {
    StaticConfiguration(kind: "TimeWidget", provider: TimeProvider()) { e in TimeWidgetView(entry: e) }
  }
}
import SwiftUI

struct ContentView: View { var body: some View { Text("Widget timeline demo") } }
import SwiftUI

@main
struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } }

The example above shows a simple widget timeline with a placeholder, snapshot, and timeline provider.


Share extension skeleton

Build a share extension that allows users to share content with your app.

Syntax:

  • ShareExtensionSkeleton struct to define the share extension skeleton

Example

import SwiftUI

struct ShareExtensionSkeleton: View {
  @State private var sharedText = ""
  var body: some View {
    VStack(spacing: 12) {
      Text("Incoming content")
      TextEditor(text: $sharedText).frame(minHeight: 120).border(.secondary)
      HStack { Spacer(); Button("Post") { /* handle share */ } }
    }
    .padding()
  }
}
import SwiftUI

struct ContentView: View { var body: some View { ShareExtensionSkeleton() } }
import SwiftUI

struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } }

The example above shows a simple share extension skeleton with a text editor and a post button.



App Groups (Sharing Data with Widget)

Enable App Groups for both the app and the widget extension, and use a shared suite to exchange small data.

Syntax: UserDefaults(suiteName: "group.id"), write with set(_:forKey:), read with integer(forKey:).

Checklist

  • Add capability App Groups to App target and Widget target
  • Create a group identifier, e.g. group.com.example.notes
import Foundation

func updateSharedNotesCount(_ count: Int) {
  let defaults = UserDefaults(suiteName: "group.com.example.notes")
  defaults?.set(count, forKey: "notesCount")
}
import WidgetKit
import SwiftUI

struct CountEntry: TimelineEntry { let date: Date; let count: Int }

struct NotesProvider: TimelineProvider {
  func placeholder(in context: Context) -> CountEntry { .init(date: Date(), count: 0) }
  func getSnapshot(in context: Context, completion: @escaping (CountEntry) -> Void) {
    let defaults = UserDefaults(suiteName: "group.com.example.notes")
    let count = defaults?.integer(forKey: "notesCount") ?? 0
    completion(.init(date: Date(), count: count))
  }
  func getTimeline(in context: Context, completion: @escaping (Timeline<CountEntry>) -> Void) {
    let defaults = UserDefaults(suiteName: "group.com.example.notes")
    let count = defaults?.integer(forKey: "notesCount") ?? 0
    let entries = [CountEntry(date: Date(), count: count)]
    completion(Timeline(entries: entries, policy: .after(Date().addingTimeInterval(1800))))
  }
}

This example shares a notes count via App Groups: the app writes the value and the widget reads it to display.


Widgets

Create a Widget Extension target in Xcode, then design timelines/views using WidgetKit and SwiftUI.

Syntax: implement TimelineProvider, define a Widget, and return a StaticConfiguration(kind:provider:content:).

Example

import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
  func placeholder(in context: Context) -> SimpleEntry { .init(date: Date()) }
  func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { completion(.init(date: Date())) }
  func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> ()) {
    completion(Timeline(entries: [.init(date: Date())], policy: .atEnd))
  }
}

struct SimpleEntry: TimelineEntry { let date: Date }

struct MyWidgetEntryView: View {
  var entry: SimpleEntry
  var body: some View { Text(entry.date, style: .time) }
}

  @main
  struct MyWidget: Widget {
    var body: some WidgetConfiguration {
      StaticConfiguration(kind: "MyWidget", provider: Provider()) { entry in
        MyWidgetEntryView(entry: entry)
      }
    }
  }
}

The example above shows a simple widget with a timeline provider and a static configuration.


Deep Links from Widgets

Open your app to a specific screen when the widget is tapped by providing a URL.

Syntax: implement Link and onOpenURL.

Example

import SwiftUI

struct NotesCountView: View {
  var body: some View {
    Link(destination: URL(string: "myapp://notes")!) {
      Text("Notes: \(entry.count)")
    }
  }
}
import SwiftUI

@main
struct MyApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
        .onOpenURL { url in
          // Route based on url.host / path (e.g., "notes")
        }
    }
  }
}

The example above shows a simple widget with a link to a specific screen and an app that handles the URL.


Refresh & Timeline Policy

Choose a refresh strategy and trigger reloads when data changes.

Syntax: implement TimelineProvider and WidgetCenter.

Example

import WidgetKit
// Update at a future date
completion(Timeline(entries: entries, policy: .after(Date().addingTimeInterval(1800))))

// Or end when entries are exhausted
// completion(Timeline(entries: entries, policy: .atEnd))
import WidgetKit

// Refresh a specific widget kind after writing new data to App Group
WidgetCenter.shared.reloadTimelines(ofKind: "NotesCount")

// Or refresh all widget kinds
// WidgetCenter.shared.reloadAllTimelines()

The example above shows a simple widget with a timeline provider and a static configuration.


Supported Families

Advertise which sizes your widget supports.

Syntax: implement WidgetConfiguration and supportedFamilies.

Example

import WidgetKit

@main
struct NotesCountWidget: Widget {
  var body: some WidgetConfiguration {
    StaticConfiguration(kind: "NotesCount", provider: NotesProvider()) { entry in
      NotesCountView(entry: entry)
    }
    .supportedFamilies([.systemSmall, .systemMedium])
  }
}

The example above shows a simple widget with a timeline provider and a static configuration.


Testing Widgets

Syntax: implement WidgetPreviewProvider and WidgetCenter.

  • Use Widget preview providers and test on device by adding the widget to the Home/Lock screen.
  • Toggle App Group values and call WidgetCenter.shared.reloadTimelines to validate refresh.
  • Check different families (small/medium/large) and dark/light mode.


×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
sales@w3schools.com

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
help@w3schools.com

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 1999-2025 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.