home / skills / pluginagentmarketplace / custom-plugin-swift / swift-ios-basics

swift-ios-basics skill

/skills/swift-ios-basics

npx playbooks add skill pluginagentmarketplace/custom-plugin-swift --skill swift-ios-basics

Review the files below or copy the command above to add this skill to your agents.

Files (8)
SKILL.md
7.6 KB
---
name: swift-ios-basics
description: Build iOS applications - project setup, app lifecycle, Info.plist, capabilities
version: "2.0.0"
sasmp_version: "1.3.0"
bonded_agent: 02-swift-ios
bond_type: PRIMARY_BOND
---

# 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

```yaml
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+)
```swift
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
```swift
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
```swift
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
```bash
# 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

```yaml
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

```swift
// 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