Agent Skills: iOS Basics Skill

Build iOS applications - project setup, app lifecycle, Info.plist, capabilities

iosswiftapp-lifecycleproject-setup
mobile-developmentID: pluginagentmarketplace/custom-plugin-swift/swift-ios-basics

Skill Files

Browse the full folder contents for swift-ios-basics.

Download Skill

Loading file tree…

skills/swift-ios-basics/SKILL.md

Skill Metadata

Name
swift-ios-basics
Description
Build iOS applications - project setup, app lifecycle, Info.plist, capabilities

iOS Basics Skill

Foundation knowledge for iOS application development including project setup, app lifecycle, and configuration.

Prerequisites

  • Xcode 15+ installed
  • Apple Developer account (for device testing)
  • macOS Sonoma or later recommended

Parameters

parameters:
  ios_deployment_target:
    type: string
    default: "15.0"
    description: Minimum iOS version supported
  device_family:
    type: array
    items: [iphone, ipad]
    default: [iphone, ipad]
  interface_style:
    type: string
    enum: [storyboard, programmatic, swiftui]
    default: programmatic

Topics Covered

App Lifecycle

| State | Description | Callback | |-------|-------------|----------| | Not Running | App not launched | - | | Inactive | Transitioning | sceneWillResignActive | | Active | Running in foreground | sceneDidBecomeActive | | Background | Running in background | sceneDidEnterBackground | | Suspended | In memory, not executing | - |

Project Structure

MyApp/
├── MyApp.xcodeproj
├── MyApp/
│   ├── App/
│   │   ├── AppDelegate.swift
│   │   ├── SceneDelegate.swift
│   │   └── Info.plist
│   ├── Features/
│   │   └── Home/
│   │       ├── HomeViewController.swift
│   │       └── HomeViewModel.swift
│   ├── Core/
│   │   ├── Extensions/
│   │   ├── Utilities/
│   │   └── Services/
│   └── Resources/
│       ├── Assets.xcassets
│       └── LaunchScreen.storyboard
└── MyAppTests/

Info.plist Keys

| Key | Purpose | Required | |-----|---------|----------| | CFBundleDisplayName | App name shown to user | Yes | | CFBundleIdentifier | Unique app identifier | Yes | | UILaunchStoryboardName | Launch screen | Yes | | NSCameraUsageDescription | Camera permission | If using camera | | NSPhotoLibraryUsageDescription | Photo library permission | If accessing photos | | UIBackgroundModes | Background capabilities | If running in background |

Code Examples

SceneDelegate Setup (iOS 13+)

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    private var appCoordinator: AppCoordinator?

    func scene(_ scene: UIScene,
               willConnectTo session: UISceneSession,
               options connectionOptions: UIScene.ConnectionOptions) {

        guard let windowScene = scene as? UIWindowScene else { return }

        let window = UIWindow(windowScene: windowScene)
        let navigationController = UINavigationController()

        appCoordinator = AppCoordinator(navigationController: navigationController)
        appCoordinator?.start()

        window.rootViewController = navigationController
        window.makeKeyAndVisible()
        self.window = window
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Save state, release resources
        CoreDataStack.shared.saveContext()
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Refresh data if needed
        NotificationCenter.default.post(name: .appWillEnterForeground, object: nil)
    }
}

Permission Request Pattern

import AVFoundation
import Photos

final class PermissionManager {
    static let shared = PermissionManager()

    func requestCameraPermission() async -> Bool {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            return true
        case .notDetermined:
            return await AVCaptureDevice.requestAccess(for: .video)
        case .denied, .restricted:
            return false
        @unknown default:
            return false
        }
    }

    func requestPhotoLibraryPermission() async -> Bool {
        let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)

        switch status {
        case .authorized, .limited:
            return true
        case .notDetermined:
            let newStatus = await PHPhotoLibrary.requestAuthorization(for: .readWrite)
            return newStatus == .authorized || newStatus == .limited
        case .denied, .restricted:
            return false
        @unknown default:
            return false
        }
    }

    func openSettings() {
        guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
        UIApplication.shared.open(url)
    }
}

Background Task Handling

import BackgroundTasks

final class BackgroundTaskManager {
    static let shared = BackgroundTaskManager()

    private let refreshTaskId = "com.app.refresh"
    private let processingTaskId = "com.app.processing"

    func registerTasks() {
        BGTaskScheduler.shared.register(forTaskWithIdentifier: refreshTaskId, using: nil) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }

        BGTaskScheduler.shared.register(forTaskWithIdentifier: processingTaskId, using: nil) { task in
            self.handleProcessing(task: task as! BGProcessingTask)
        }
    }

    func scheduleRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: refreshTaskId)
        request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 min

        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            Logger.background.error("Failed to schedule refresh: \(error)")
        }
    }

    private func handleAppRefresh(task: BGAppRefreshTask) {
        scheduleRefresh() // Reschedule

        let refreshTask = Task {
            do {
                try await DataSyncService.shared.syncAll()
                task.setTaskCompleted(success: true)
            } catch {
                task.setTaskCompleted(success: false)
            }
        }

        task.expirationHandler = {
            refreshTask.cancel()
        }
    }
}

Troubleshooting

Common Issues

| Issue | Cause | Solution | |-------|-------|----------| | Black screen on launch | Missing root VC | Set window.rootViewController | | Permission dialog not showing | Already denied | Check status, guide to Settings | | Background task not running | Not registered | Call register in didFinishLaunching | | Launch image wrong size | Missing assets | Add all required sizes to LaunchScreen |

Debug Tips

# Simulate background fetch
xcrun simctl spawn booted debug-background refresh com.app.bundleid

# Check app lifecycle in console
# Filter: subsystem:com.apple.UIKit category:Lifecycle

# Reset permissions
xcrun simctl privacy booted reset all com.app.bundleid

Validation Rules

validation:
  - rule: info_plist_usage_descriptions
    severity: error
    check: All permission usage descriptions must be non-empty
  - rule: launch_screen_required
    severity: error
    check: LaunchScreen.storyboard or launch screen in Info.plist
  - rule: supported_orientations
    severity: warning
    check: Define supported orientations for all device types

Integration Patterns

// AppDelegate + SceneDelegate coordination
extension Notification.Name {
    static let appWillEnterForeground = Notification.Name("appWillEnterForeground")
    static let appDidBecomeActive = Notification.Name("appDidBecomeActive")
    static let userDidLogin = Notification.Name("userDidLogin")
}

Usage

Skill("swift-ios-basics")

Related Skills

  • swift-uikit - UIKit components
  • swift-swiftui - SwiftUI alternative
  • swift-testing - Testing iOS apps