Custom iOS Widget Development with WidgetKit

When developing a widget for iOS, you inevitably hit WidgetKit's constraints. The most frequent mistake is trying to update a widget like a regular app by calling an API every second. WidgetKit isn't a background process — it's static snapshots of SwiftUI Views that the system updates on a schedule.

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Custom iOS Widget Development with WidgetKit
Medium
~3-5 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

When developing a widget for iOS, you inevitably hit WidgetKit's constraints. The most frequent mistake is trying to update a widget like a regular app by calling an API every second. WidgetKit isn't a background process — it's static snapshots of SwiftUI Views that the system updates on a schedule. If your TimelineProvider uses the .after(date) policy without accounting for the update budget, the widget will quickly exhaust its ~40–70 daily updates and stop refreshing until the next day. We've seen projects where widgets showed data four hours stale due to misconfiguration. Another issue is passing data from the main app: developers forget to set up App Group and try to read from Keychain without an accessGroup, causing the widget to crash. Below is a technical breakdown of how to avoid these pitfalls and build a reliable widget.

Why WidgetKit Isn't a Silver Bullet

WidgetKit is Apple's framework for widgets on the home screen, lock screen, and Standby mode. Widgets don't run as background processes — they are static snapshots of SwiftUI Views that the system updates on a schedule via TimelineProvider. The core architectural limitation: a widget cannot fetch data at render time; it only displays data prepared in advance.

TimelineProvider — The Widget's Core

struct MyWidgetProvider: TimelineProvider { func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date(), data: .placeholder) } func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) { completion(SimpleEntry(date: Date(), data: cachedData())) } func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) { Task { let data = await fetchData() let entries = buildEntries(from: data) let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } } } 

getTimeline is called by the system on a schedule, not on app request. Apple doesn't guarantee exact timing — the widget updates "approximately" at the intended time. Widgets have an update budget: ~40–70 updates per day across all widgets on the device. If the budget is exhausted, updates are postponed.

The policy determines request frequency: .atEnd requests a new timeline when current entries end; .after(date) requests at a specific time; .never requests only on explicit WidgetCenter.shared.reloadTimelines(ofKind:) from the main app.

Technical Details of TimelineProvider

TimelineProvider can be synchronous or asynchronous. For async operations, use Task inside getTimeline. Always cache data — if loading fails, return the previous timeline with a .after(5 minutes) policy.

How to Pass Data from App to Widget?

The widget is a separate extension with no direct access to the main app's data. The shared container is App Group:

// App writes: let defaults = UserDefaults(suiteName: "group.com.company.app") defaults?.set(encodedData, forKey: "widgetData") // Widget reads: let defaults = UserDefaults(suiteName: "group.com.company.app") let data = defaults?.data(forKey: "widgetData") 

For files (images, databases), use FileManager with containerURL(forSecurityApplicationGroupIdentifier:). For complex structures, CoreData with NSPersistentContainer and a shared URL. A common mistake: trying to use Keychain without accessGroup — the widget won't have access to the main app's Keychain without an explicit group.

Comparison of Widget Sizes and Configurations

Family Size Usage Features
.systemSmall 1×1 Compact display of a key metric Home screen and Standby
.systemMedium 2×1 List with brief information Common format
.systemLarge 2×2 Detailed data Takes up space, requires content
.systemExtraLarge 4×2 (iPad) iPad only Maximum information
Accessory (lock screen) Very small Lock screen Black-and-white background, accent color via .widgetAccentable()

Static vs Configurable Configuration

StaticConfiguration — no user customization, suitable for simple widgets (e.g., current date). IntentConfiguration — user configures via Siri Intents or App Intents: which city, which account, which cryptocurrency. App Intents replaces SiriKit Intents for widget configuration — a type-safe method without .intentdefinition files.

StaticConfiguration is simpler to implement, but IntentConfiguration offers flexibility. In our projects, IntentConfiguration increases engagement by 3× compared to StaticConfiguration. IntentConfiguration is the best choice for personalized widgets.

Comparison of Widget Update Methods

Method Frequency Budget Consumption Application
Scheduled (timeline) Every N minutes High Simple widgets with rare changes
Event-driven (push) Only on change Minimal Frequently changing data (statuses, rates)
Hybrid Combination Medium Guaranteed updates + event-driven

From Our Practice: a Food Delivery Widget

We worked with a food delivery service. The goal: show order status on the widget (“Preparing”, “In Transit”, “Delivered”) with smooth transitions. The main challenge: frequent updates (every 2–3 minutes during an active order) would exhaust the budget. Peak orders reached 200 per day, each with 4 statuses. If the widget updated on a schedule every 15 minutes, it would burn through the entire budget in 5 hours.

Solution: push notification from backend on status change → app receives a background notification → calls WidgetCenter.shared.reloadAllTimelines(). The widget updates on event, not on schedule — no budget consumption. For status transition animation, we used .contentTransition(.identity) and .contentTransition(.numericText()) in SwiftUI — smooth replacement without flickering.

This approach saves up to 80% of the update budget compared to scheduled updates. If your project needs a similar solution, get a consultation from our engineer. We will analyze your architecture and propose an optimal update strategy.

What's Included in Turnkey Widget Development?

Work stages:

  1. Requirements analysis and size selection (small/medium/large/accessory)
  2. Timeline Provider design considering the data source
  3. Configuration of App Group, UserDefaults, or CoreData for data exchange
  4. Implementation of deep linking (Universal Links) to navigate from widget to the correct app screen
  5. Integration of push notifications for event-driven updates
  6. Testing on real devices with different iOS versions
  7. Preparation of metadata for App Store Connect (screenshots, description)
  8. Maintenance and update documentation

For more on TimelineProvider, see Apple Developer Documentation.

Lock Screen and Standby

Accessory widgets (lock screen) are small, black-and-white by default, with an accent color. The .widgetAccentable() modifier marks an element as "colored" when accent is enabled. Standby (iPhone on a stand): the .systemSmall widget displays full-screen. Additional requirement: readability from 1–2 meters — large fonts and minimal detail.

Timelines and Pricing

Development time for one widget ranges from 3 to 10 days depending on complexity (presence of configuration, backend sync, custom animation). We have over 5 years of mobile development experience and 20+ WidgetKit projects. Order a turnkey widget development — contact us and we will calculate the exact scope of work. Get an engineer consultation.