RORK LABJP
MAX — Rork Max is built on Claude Code and Claude Opus 4.6, generating native Swift apps directly instead of React NativeAPPLE — Rork Max targets the whole Apple ecosystem: iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessageWORKFLOW — In practice, users settle into letting the AI scaffold while they rewrite the state management and data layer themselvesSEED — Rork raised a $15M seed led by Left Lane Capital in April, with Peak XV, True Ventures, and a16z Speedrun joiningPAPERLINE — Rork acquired app builder Paperline and says it will stay acquisitive to bring in engineering talentREVIEW — Three-month revisit reviews are growing, clarifying where the tool shines and where it doesn'tMAX — Rork Max is built on Claude Code and Claude Opus 4.6, generating native Swift apps directly instead of React NativeAPPLE — Rork Max targets the whole Apple ecosystem: iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessageWORKFLOW — In practice, users settle into letting the AI scaffold while they rewrite the state management and data layer themselvesSEED — Rork raised a $15M seed led by Left Lane Capital in April, with Peak XV, True Ventures, and a16z Speedrun joiningPAPERLINE — Rork acquired app builder Paperline and says it will stay acquisitive to bring in engineering talentREVIEW — Three-month revisit reviews are growing, clarifying where the tool shines and where it doesn't
Articles/Dev Tools
Dev Tools/2026-06-14Advanced

Actually Delivering 'It Updates Without Opening' in Expo — A Realistic Background Task Design

Building 'content refreshes every morning' into a Rork-generated Expo app runs into iOS background execution being far less dutiful than you expect. Here is a minimal expo-background-task setup plus a design that doesn't break when the task never runs.

Rork502Expo139Background TaskBGTaskScheduler3iOS109indie developer37

Premium Article

Running a wallpaper app as an indie developer, the single most requested feature was "I want a new wallpaper every morning without opening the app." Naively, you would just run something periodically in the background and swap the image. But build this seriously on iOS and you end up chasing irreproducible bug reports: "it updated yesterday but not today."

The cause is not a bug in your code — iOS background execution is designed around the premise that you cannot know when it will run. Rork generates an Expo app, but underneath it iOS BGTaskScheduler is at work, and if you build without understanding its capriciousness, your UX is left to chance. This article covers building a refresh experience that survives the task not running, on both the wiring and the judgment side.

Why "It Should Update Every Morning" Doesn't

iOS background tasks are not something you can pin to a clock and force to run. The system runs them only when it decides, based on the device's usage patterns, battery, and network, that "now is acceptable."

So they run fairly often for users who open the app daily, and rarely for users who almost never open it. Ironically, the dormant users who most want "updates without opening" are exactly the ones whose background tasks rarely fire. Accept this asymmetry first or you will design wrong.

A Minimal expo-background-task Setup

Current Expo uses expo-background-task (which uses BGTaskScheduler / WorkManager internally), not the deprecated expo-background-fetch. The task itself is defined with expo-task-manager.

import * as TaskManager from "expo-task-manager";
import * as BackgroundTask from "expo-background-task";
 
const REFRESH_TASK = "daily-wallpaper-refresh";
 
TaskManager.defineTask(REFRESH_TASK, async () => {
  try {
    const updated = await fetchAndCacheTodaysWallpaper();
    // Always return success/failure. Not returning makes iOS shrink your future budget
    return updated
      ? BackgroundTask.BackgroundTaskResult.Success
      : BackgroundTask.BackgroundTaskResult.Failed;
  } catch {
    return BackgroundTask.BackgroundTaskResult.Failed;
  }
});
 
export async function registerRefreshTask() {
  const status = await BackgroundTask.getStatusAsync();
  if (status !== BackgroundTask.BackgroundTaskStatus.Available) return;
  await BackgroundTask.registerTaskAsync(REFRESH_TASK, {
    minimumInterval: 60 * 12, // minutes; 12 hours here
  });
}

In app.json, iOS UIBackgroundModes must include processing. In Expo you write it under infoPlist.

{
  "expo": {
    "ios": {
      "infoPlist": {
        "UIBackgroundModes": ["fetch", "processing"],
        "BGTaskSchedulerPermittedIdentifiers": ["daily-wallpaper-refresh"]
      }
    }
  }
}

Forget to list the task ID in BGTaskSchedulerPermittedIdentifiers and registration succeeds but the task never runs on device — a silent failure. I lost half a day to exactly this.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Get a working minimal setup of expo-background-task and expo-task-manager, including the UIBackgroundModes configuration
Understand that minimumInterval is a floor, not a guarantee, expressed through concrete iOS conditions: Low Power Mode, launch frequency, charging state
Implement a two-tier foreground fallback that keeps a wallpaper app's auto-refresh from breaking when background never fires
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-05-28
Tracking Down BGTaskScheduler.submit Error Code=1 (Unavailable) in Rork iOS Apps
A field-tested checklist for diagnosing BGTaskScheduler.submit failing with Error Code=1 (Unavailable) in iOS apps built with Rork, walking through the six causes that account for nearly every case.
Dev Tools2026-07-04
Should You Show a Read More Link? Let the Rendered Text Decide in Rork (Expo)
Clamping a product description to three lines and adding a Read more toggle sounds simple, until the toggle also appears under single-line text. This walks through measuring the real line count with onTextLayout so the toggle only shows when text actually overflows, covering iOS vs Android quirks, expand animation, and font scaling.
Dev Tools2026-06-30
Adding Home-Screen Quick Actions to a Rork App — dynamic items without cold-launch drops
How to implement the long-press quick actions on a Rork (Expo) app icon. Covers static vs dynamic items, the iOS/Android differences, and the cold-launch problem where the action arrives before the router is ready — solved with a hold-and-replay design.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →