Back to skills
SKILL.md
Announcement Banner
ASecurityGenerates an in-app announcement banner system with remote configuration, scheduling, deep link actions, and dismiss tracking. Use when user wants in-app banners, promotional notices, maintenance alerts, or contextual announcements.
- 693 stars
- 0 votes
- 0 copies
- 2 views
- Added September 6, 2026
Works with
Security analysis
100/100Pro scans all 2 files and shows the line behind each finding
npx -y skills add rshankras/claude-code-apple-skills --skill announcement-banner --agent claude-codeAre you the author of Announcement Banner?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/rshankras-announcement-banner)---
name: announcement-banner
description: Generates an in-app announcement banner system with remote configuration, scheduling, deep link actions, and dismiss tracking. Use when user wants in-app banners, promotional notices, maintenance alerts, or contextual announcements.
allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion]
last_verified: 2026-07-16
review_by: 2027-06-22
os_version: iOS 27 / macOS 27
---
# Announcement Banner Generator
Generate a configurable in-app announcement/banner system that displays contextual messages (maintenance alerts, new feature highlights, promotions) from remote config or local definitions. Supports dismissal, deep link actions, and scheduling.
## When This Skill Activates
Use this skill when the user:
- Asks to "add announcement banner" or "in-app banner"
- Wants "app announcement" or "notification banner"
- Mentions "promotional banner" or "promo banner"
- Asks about "maintenance notice" or "maintenance alert"
- Wants "in-app messaging" or "contextual banners"
- Asks to "show announcements" or "display alerts to users"
- Mentions "feature highlight banner" or "what's new banner"
## Pre-Generation Checks
### 1. Project Context Detection
- [ ] Check Swift version (requires Swift 5.9+)
- [ ] Check deployment target (iOS 16+ / macOS 13+)
- [ ] Check for @Observable support (iOS 17+ / macOS 14+)
- [ ] Identify source file locations
### 2. Conflict Detection
Search for existing banner/toast code:
```
Glob: **/*Banner*.swift, **/*Announcement*.swift, **/*Toast*.swift, **/*InAppMessage*.swift
Grep: "AnnouncementBanner" or "InAppBanner" or "ToastView" or "BannerView"
```
If third-party library found (Firebase In-App Messaging, Braze, Intercom):
- Ask if user wants to replace or keep it
- If keeping, don't generate — advise on integration patterns instead
### 3. Deep Linking Detection
Check for existing deep linking setup:
```
Grep: "deepLink" or "DeepLink" or "universalLink" or "openURL" or "onOpenURL"
```
If deep linking exists, integrate with it. If not, generate standalone action handling.
## Configuration Questions
Ask user via AskUserQuestion:
1. **Banner position?**
- Top (slides in from top) — recommended for alerts
- Bottom (slides in from bottom)
- Floating (centered overlay with dimmed background)
2. **Content source?**
- Local only (hardcoded announcements in code)
- Remote JSON (fetch from server endpoint)
- Both (remote with local fallbacks) — recommended
3. **Action type?**
- Deep link (navigate to in-app screen)
- URL (open in Safari/SFSafariViewController)
- Dismiss only (informational, no action button)
- All of the above — recommended
4. **Include scheduling (start/end dates)?**
- Yes (time-limited announcements with date ranges) — recommended
- No (always-active until dismissed)
## Generation Process
### Step 1: Read Templates
Read `templates.md` for production Swift code.
### Step 2: Create Core Files
Generate these files:
1. `Announcement.swift` — Model with style, action, priority, scheduling
2. `AnnouncementManager.swift` — @Observable manager: loads, filters, tracks dismissals
3. `AnnouncementProvider.swift` — Protocol + local/remote implementations with caching
### Step 3: Create UI Files
4. `AnnouncementBannerView.swift` — SwiftUI banner with style-aware colors and animations
5. `AnnouncementBannerModifier.swift` — ViewModifier for overlay placement and action routing
### Step 4: Create Optional Files
Based on configuration:
- `AnnouncementScheduler.swift` — If scheduling selected (date range filtering, timezone handling)
### Step 5: Determine File Location
Check project structure:
- If `Sources/` exists -> `Sources/AnnouncementBanner/`
- If `App/` exists -> `App/AnnouncementBanner/`
- Otherwise -> `AnnouncementBanner/`
## Output Format
After generation, provide:
### Files Created
```
AnnouncementBanner/
├── Announcement.swift # Model with style, action, priority
├── AnnouncementManager.swift # Observable manager with dismiss tracking
├── AnnouncementProvider.swift # Protocol + local/remote providers
├── AnnouncementBannerView.swift # SwiftUI banner view
├── AnnouncementBannerModifier.swift# ViewModifier for overlay + actions
└── AnnouncementScheduler.swift # Date range filtering (optional)
```
### Integration Steps
**Add banner overlay to root view:**
```swift
// In your root ContentView or NavigationStack
ContentView()
.announcementBanner()
```
**With custom position:**
```swift
ContentView()
.announcementBanner(position: .bottom)
```
**With deep link action handler:**
```swift
ContentView()
.announcementBanner { action in
switch action {
case .deepLink(let destination):
router.navigate(to: destination)
case .url(let url):
openURL(url)
case .dismiss:
break
}
}
```
**Define local announcements:**
```swift
let provider = LocalAnnouncementProvider(announcements: [
Announcement(
id: "maintenance-2024",
title: "Scheduled Maintenance",
message: "We'll be performing maintenance on Saturday 9-11 AM EST.",
style: .warning,
action: .dismiss,
priority: 10,
startDate: maintenanceStart,
endDate: maintenanceEnd
),
Announcement(
id: "new-feature-photos",
title: "New: Photo Library",
message: "Check out our new photo library with AI-powered search!",
style: .promotion,
action: .deepLink("app://features/photos"),
priority: 5,
isDismissible: true
)
])
```
**Fetch from remote config:**
```swift
let provider = RemoteAnnouncementProvider(
url: URL(string: "https://api.example.com/announcements")!,
cacheDuration: 3600 // 1 hour
)
```
### Testing
```swift
@Test
func dismissedAnnouncementsAreHidden() async throws {
let manager = AnnouncementManager(
provider: MockAnnouncementProvider(announcements: [testAnnouncement]),
dismissalStore: InMemoryDismissalStore()
)
await manager.loadAnnouncements()
#expect(manager.activeAnnouncement != nil)
manager.dismiss(testAnnouncement)
#expect(manager.activeAnnouncement == nil)
}
@Test
func highestPriorityAnnouncementShownFirst() async throws {
let lowPriority = Announcement(id: "low", title: "Low", message: "...", style: .info, priority: 1)
let highPriority = Announcement(id: "high", title: "High", message: "...", style: .warning, priority: 10)
let manager = AnnouncementManager(
provider: MockAnnouncementProvider(announcements: [lowPriority, highPriority])
)
await manager.loadAnnouncements()
#expect(manager.activeAnnouncement?.id == "high")
}
@Test
func expiredAnnouncementsAreFiltered() async throws {
let expired = Announcement(
id: "expired",
title: "Old",
message: "...",
style: .info,
endDate: Date.distantPast
)
let manager = AnnouncementManager(
provider: MockAnnouncementProvider(announcements: [expired])
)
await manager.loadAnnouncements()
#expect(manager.activeAnnouncement == nil)
}
```
## Common Patterns
### Show Maintenance Banner
```swift
Announcement(
id: "maintenance-\(date)",
title: "Scheduled Maintenance",
message: "Service will be unavailable Saturday 2-4 AM EST.",
style: .warning,
action: .url(URL(string: "https://status.example.com")!),
priority: 100, // High priority overrides other banners
startDate: Calendar.current.date(byAdding: .day, value: -1, to: maintenanceDate)!,
endDate: maintenanceEndDate,
isDismissible: false // Can't dismiss maintenance warnings
)
```
### Promote New Feature
```swift
Announcement(
id: "feature-v2.5-darkmode",
title: "Dark Mode is Here!",
message: "Try our new dark mode in Settings.",
style: .promotion,
action: .deepLink("app://settings/appearance"),
priority: 5,
isDismissible: true
)
```
### Time-Limited Promotion
```swift
Announcement(
id: "promo-summer-2024",
title: "Summer Sale - 40% Off!",
message: "Upgrade to Pro at our lowest price ever.",
style: .promotion,
action: .deepLink("app://subscription/upgrade"),
priority: 8,
startDate: promoStart,
endDate: promoEnd,
isDismissible: true,
targetAudience: .freeUsers
)
```
## Gotchas
1. **Banner stacking** — Never show multiple banners simultaneously. Use priority to determine which single banner to display. Queue others and show the next one after dismissal.
2. **Don't cover critical UI** — Ensure the banner doesn't overlap navigation bars, tab bars, or interactive elements. Use safe area insets.
3. **Respect user dismissal** — Persist dismissed announcement IDs so they don't reappear after app relaunch. Use UserDefaults or a lightweight store.
4. **Remote config caching** — Cache fetched announcements to avoid network requests on every app launch. Honor cache duration but allow force-refresh.
5. **Date/timezone handling** — Always use UTC for start/end dates in remote config. Convert to local timezone only for display purposes.
6. **Accessibility** — Announce banner appearance with `AccessibilityNotification.Announcement`. Ensure dismiss button has proper label.
7. **Animation interruption** — If a user navigates away mid-animation, ensure the banner state resets properly. Use `.task` tied to view lifecycle.
## References
- **templates.md** — All production Swift templates
- Related: `generators/deep-linking` — Deep link routing integration
- Related: `generators/whats-new` — What's new screen for feature highlights
Files in this skill
- SKILL.md
- templates.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.