RORK LABJP
MAX — Rork Max generates native Swift apps for iPhone, iPad, Watch, TV, Vision Pro, and iMessageNATIVE — It reaches AR/LiDAR, Metal 3D, Dynamic Island, Live Activities, HealthKit, and Core MLPUBLISH — Two-click App Store submission sharply cuts the overhead of shipping an appPRICING — Rork Max is 00/month, while the original Rork starts free with paid plans from 5/monthFUNDING — Rork raised .8M from a16z, with over 743k monthly visits and 85% growthTOOL — The original Rork builds native iOS and Android apps from plain English using React Native (Expo)MAX — Rork Max generates native Swift apps for iPhone, iPad, Watch, TV, Vision Pro, and iMessageNATIVE — It reaches AR/LiDAR, Metal 3D, Dynamic Island, Live Activities, HealthKit, and Core MLPUBLISH — Two-click App Store submission sharply cuts the overhead of shipping an appPRICING — Rork Max is 00/month, while the original Rork starts free with paid plans from 5/monthFUNDING — Rork raised .8M from a16z, with over 743k monthly visits and 85% growthTOOL — The original Rork builds native iOS and Android apps from plain English using React Native (Expo)
Articles/App Dev
App Dev/2026-07-01Advanced

Making the Block Screen Feel Like Your Own App — ShieldConfiguration and Button Handling in Rork Max

Replace the plain gray shield that screen-time apps show with something that speaks in your app's voice. This walks through swapping the UI with a ShieldConfiguration extension and controlling the buttons with ShieldAction, wired into a Rork Max native setup.

Rork Max200Family Controls2ManagedSettings2Screen TimeiOS94

Premium Article

As an indie developer building a focus-time app in Rork Max, I kept hitting the same wall: the block would fire on schedule, but what appeared was Apple's default gray screen — nothing that told the user it was still my app. After a carefully made onboarding flow, users were suddenly dropped onto a cold system screen.

That "shield" screen can be replaced. But you can't touch it from your main app code. The screen shown during a block is drawn by a separate process, so reshaping it means adding two dedicated App Extensions: a ShieldConfiguration extension that swaps the appearance, and a ShieldAction extension that decides what happens when its buttons are pressed.

I'll assume you already have the Family Controls entitlement and are applying shields through ManagedSettingsStore. This article focuses on the step after that: rebuilding the screen itself.

Why you can't draw the shield from the main app

When you hand tokens to store.shield.applications, the system slides a shield screen in the moment the target app is opened. That screen isn't drawn by your app — it's a separate process running inside the ManagedSettingsUI framework. Neither React Native nor your main Swift target can touch it directly.

The one entry point is ShieldConfigurationDataSource. Right before the system shows the shield, it asks this class, and builds the screen from the ShieldConfiguration you return. So what you control is "which parts, in which colors" — not an arbitrary SwiftUI view. Miss this and you'll get stuck trying to render an elaborate layout that the API simply won't accept.

What you wantPossible?How
Change background color / blurYesbackgroundColor / backgroundBlurStyle
Use your own iconYes (only images bundled in the extension)icon as a UIImage
Title / subtitle text and colorYestitle / subtitle (Label)
Two buttonsYes (max two — label and color only)primaryButtonLabel / secondaryButtonLabel
Render an arbitrary SwiftUI viewNo
Fetch a remote image on the spotEffectively noBundle it, or pass via App Group

Step 1: Add the ShieldConfiguration extension

In Xcode, add a new "Shield Configuration" extension target. When you build through Expo like Rork Max does, re-adding the target by hand on every eas build isn't realistic, so inject the extension target, its Info.plist, and entitlements through a config plugin. I keep this in the same config plugin as my main native module so the extension never gets forgotten.

In the extension's Info.plist, set NSExtensionPointIdentifier to com.apple.ManagedSettingsUI.shield-configuration-service and NSExtensionPrincipalClass to your class name. Get this wrong and the build still succeeds, but the system never calls you — you stay on the default screen, with no error to explain why.

import ManagedSettings
import ManagedSettingsUI
import UIKit
 
class FocusShieldConfiguration: ShieldConfigurationDataSource {
  override func configuration(shielding application: Application) -> ShieldConfiguration {
    return ShieldConfiguration(
      backgroundBlurStyle: .systemThinMaterialDark,
      backgroundColor: UIColor(red: 0.08, green: 0.10, blue: 0.16, alpha: 1),
      icon: UIImage(named: "ShieldLeaf"),
      title: ShieldConfiguration.Label(
        text: "This is your focus time",
        color: .white
      ),
      subtitle: ShieldConfiguration.Label(
        text: "You chose to close this app during this window. Give it a few more minutes.",
        color: UIColor(white: 0.75, alpha: 1)
      ),
      primaryButtonLabel: ShieldConfiguration.Label(
        text: "Back to focus",
        color: .white
      ),
      primaryButtonBackgroundColor: UIColor(red: 0.20, green: 0.55, blue: 0.90, alpha: 1),
      secondaryButtonLabel: ShieldConfiguration.Label(
        text: "Open for 5 minutes",
        color: UIColor(white: 0.65, alpha: 1)
      )
    )
  }
 
  // Return this too when you block by category
  override func configuration(shielding application: Application,
                              in category: ActivityCategory) -> ShieldConfiguration {
    return configuration(shielding: application)
  }
}

Set secondaryButtonLabel to nil and you get a single button. Specific wording pays off. Rather than announcing "blocked," a line that reminds users "this was your own promise" changes how the screen lands. Just swapping "Access is blocked" for the line above visibly warmed up my review sentiment.

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
A full ShieldConfiguration extension that replaces the default gray shield — background, icon, title, and up to two buttons — in your app's tone
The ShieldAction code that decides what the block-screen buttons do: close, grant a timed pass, or ignore, with a clean state-sharing design
Practical notes on the out-of-process, low-memory constraints of these extensions: bundling images, passing state via App Group, and what review looks at
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

App Dev2026-07-01
Building a Song-Recognition App with ShazamKit in Rork Max's Native Swift
Implementation notes on building a song-recognition app with SHManagedSession in Rork Max's native Swift. Covers the difference from hand-rolling AVAudioEngine, designing the idle / prerecording / matching states, using prerecording to improve initial accuracy, and the boundary design for bridging from Expo — with the pitfalls I actually hit.
App Dev2026-07-01
Building an AlarmKit Timer in Rork Max's Native Swift — Alerts That Cut Through Silent Mode and Focus
Implementation notes on solving the 'local notifications stay silent in Silent Mode and Focus' problem with iOS 26's AlarmKit. Covers authorization, scheduling a countdown, the Live Activity for the Lock Screen and Dynamic Island, and listing active timers in Swift — plus the boundary design for Rork Max native code and bridging from Expo, with the pitfalls I actually hit.
App Dev2026-06-30
Screening Images On Device Before They Appear — Notes on SensitiveContentAnalysis
Implementation notes on blocking inappropriate images before they render, right on the device, for apps that handle AI-generated or user-submitted photos. Covers calling Apple's SensitiveContentAnalysis framework from Swift and wiring it into Rork Max native code or an Expo native module, with the pitfalls I actually hit.
📚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 →