Author: saqibkhan

  • Privacy & Permissions

    Privacy & Permissions

    Declare usage descriptions in Info.plist and request authorization before accessing protected resources.


    Request Camera Access

    Request camera access in your app.

    Syntax:

    • AVCaptureDevice.authorizationStatus(for:)
    • AVCaptureDevice.requestAccess(for:)

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    import AVFoundation
    
    struct CameraPermissionDemo: View {
      @State private var status = AVCaptureDevice.authorizationStatus(for: .video)
      var body: some View {
    
    VStack(spacing: 12) {
      Text("Status: \(label(for: status))")
      Button("Request Access") {
        AVCaptureDevice.requestAccess(for: .video) { _ in
          DispatchQueue.main.async {
            status = AVCaptureDevice.authorizationStatus(for: .video)
          }
        }
      }
    }
    .padding()
    } private func label(for s: AVAuthorizationStatus) -> String {
    switch s { case .notDetermined: return "notDetermined"
    case .denied: return "denied"
    case .restricted: return "restricted"
    case .authorized: return "authorized"
    @unknown default: return "unknown" }
    } }

    The example above shows a simple camera permission request with a button to request access and a label to display the current status.


    Open Settings

    Open the app’s settings page from within the app.

    Syntax:

    • UIApplication.openSettingsURLString
    • UIApplication.shared.canOpenURL(url)
    • UIApplication.shared.open(url)

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    import UIKit
    
    struct OpenSettingsDemo: View {
      var body: some View {
    
    VStack(spacing: 12) {
      Text("Permission denied? Open Settings.")
      Button("Open Settings") {
        if let url = URL(string: UIApplication.openSettingsURLString),
           UIApplication.shared.canOpenURL(url) {
          UIApplication.shared.open(url)
        }
      }
    }
    .padding()
    } }

    The example above shows a simple settings page with a button to open the app’s settings page from within the app.


    Info.plist Usage Descriptions

    When accessing protected resources (camera, photos, location, microphone, etc.) you must include a usage description in Info.plist (e.g., NSCameraUsageDescription).

    Syntax:

    • NSCameraUsageDescriptionNSMicrophoneUsageDescription
    • NSPhotoLibraryUsageDescriptionNSPhotoLibraryAddUsageDescription
    • NSLocationWhenInUseUsageDescriptionNSLocationAlwaysAndWhenInUseUsageDescription
    • NSContactsUsageDescriptionNSCalendarsUsageDescriptionNSRemindersUsageDescription
    • NSBluetoothAlwaysUsageDescriptionNSMotionUsageDescriptionNSFaceIDUsageDescription

    Example

    Info.plist (snippets)

    NSCameraUsageDescription = "This app uses the camera to scan QR codes"
    NSPhotoLibraryUsageDescription = "Select a profile picture"
    NSLocationWhenInUseUsageDescription = "Find nearby stores"

    These usage descriptions will be displayed in the app’s Settings page.


    Requesting Authorization

    Use the respective frameworks to request runtime permission before accessing data.

    Syntax:

    • AVCaptureDevice.authorizationStatus(for:)
    • AVCaptureDevice.requestAccess(for:)

    Example

    Camera.swift

    import AVFoundation
    
    let status = AVCaptureDevice.authorizationStatus(for: .video)
    switch status {
    case .notDetermined:
      AVCaptureDevice.requestAccess(for: .video) { granted in /* handle */ }
    case .denied, .restricted:
      // Show instructions to enable in Settings
      break
    case .authorized:
      // Proceed to use camera
      break
    @unknown default: break
    }

    This example checks current camera authorization, requests access if needed, and guides handling for denied or restricted states.

    Tip: Always explain why you need access, and gracefully handle denial by offering alternative flows.

    Handle Denied: Link to Settings

    Example

    Settings.swift

    import UIKit
    
    if let url = URL(string: UIApplication.openSettingsURLString),
       UIApplication.shared.canOpenURL(url) {
      UIApplication.shared.open(url)
    }

    Other Common Permissions

    Example

    Photos.swift

    Location.swift

    Microphone.swift

    Contacts.swift

    import Photos
    
    PHPhotoLibrary.requestAuthorization { status in /* handle */ }
  • Testing SwiftUI

    Unit Test a ViewModel

    Place business logic in a ViewModel and verify it using XCTestCase.

    Syntax:

    • final class MyTests: XCTestCase { ... }
    • assertions like XCTAssertEqualXCTAssertTrue

    Example

    Demo.swift

    ContentView.swift

    App.swift

    CounterViewModelTests.swift

    import SwiftUI
    import Combine
    
    final class CounterViewModel: ObservableObject {
      @Published private(set) var count = 0
      func increment() { count += 1 }
    }
    
    struct CounterView: View {
      @StateObject private var vm = CounterViewModel()
      var body: some View {
    
    VStack(spacing: 12) {
      Text("Count: \(vm.count)")
      Button("Increment") { vm.increment() }
    }
    } }

    This example verifies that calling increment() updates the published count.


    Prerequisites (UI Testing Target)

    • Add a UI Testing Bundle target (File → New → Target → UI Testing Bundle).
    • Prefer locating elements by accessibility identifiers rather than visible text.
    • Pass launch arguments and environment to control app state for tests (e.g., reset data, use fakes).

    UI Test a SwiftUI View

    Drive the app with XCUIApplication and locate elements by accessibility.

    Syntax:

    • let app = XCUIApplication(); app.launch()
    • app.buttons["Label"].tap()

    Example

    Demo.swift

    ContentView.swift

    App.swift

    MyUITests.swift

    import SwiftUI
    
    struct UITestDemo: View {
      @State private var count = 0
      var body: some View {
    
    VStack(spacing: 12) {
      Text("Count: \(count)").accessibilityIdentifier("countLabel")
      Button("Increment") { count += 1 }
        .accessibilityIdentifier("incrementButton")
    }
    } }
  • SwiftUI AppStorage & SceneStorage

    SwiftUI AppStorage & SceneStorage

    Persist small values with @AppStorage and view-state per scene with @SceneStorage.


    @AppStorage (User Defaults)

    Bind a value directly to UserDefaults so changes persist across app launches.

    Syntax: @AppStorage("key") var value: T = default

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct AppStorageDemo: View {
      @AppStorage("username") private var username = ""
      var body: some View {
    
    VStack(spacing: 12) {
      Text("Hello, \(username.isEmpty ? "Guest" : username)")
      TextField("Username", text: $username)
        .textFieldStyle(.roundedBorder)
    }
    .padding()
    } }

    This example stores the username in UserDefaults and keeps the UI in sync.


    @SceneStorage (Per-Scene UI State)

    Preserve transient UI data (like a draft) per window/scene; state is restored when the scene returns.

    Syntax: @SceneStorage("key") var value: T

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct SceneStorageDemo: View {
      @SceneStorage("draft_note") private var draft = ""
      var body: some View {
    
    VStack(alignment: .leading, spacing: 8) {
      Text("Draft:")
      TextEditor(text: $draft)
        .frame(minHeight: 120)
        .overlay(RoundedRectangle(cornerRadius: 6).stroke(.secondary))
    }
    .padding()
    } }
  • SwiftUI MVVM

    Model-View-ViewModel

    MVVM separates UI (View) from business logic (ViewModel) and data (Model).

    The ViewModel is an ObservableObject the View observes.

    Syntax:

    • class VM: ObservableObject { @Published var state }
    • @StateObject private var vm = VM()
    • List(vm.items) { ... }
    • ObservableObject: A class that notifies its observers when its state changes.
    • @Published: A property wrapper that publishes changes to a value, notifying observers.
    • @StateObject: A property wrapper that creates a new instance of a class, making it a source of truth for a view.

    Example

    Demo.swift

    ViewModel.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct MVVMBasicView: View {
      @StateObject private var vm = TodoViewModel()
      var body: some View {
    
    List(vm.todos) { t in Text(t.title) }
      .onAppear { vm.load() }
    } }

    This example shows the MVVM flow: a ViewModel exposes state, the View observes it, and triggers loading on appear.


    REMOVE ADS


    Sample App: Notes (MVVM)

    Example

    Demo.swift

    ViewModel.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct NotesMVVMView: View {
      @StateObject private var vm = NotesViewModel()
      var body: some View {
    
    List(vm.notes) { n in
      VStack(alignment: .leading) {
        Text(n.title).font(.headline)
        Text(n.body).font(.subheadline)
      }
    }
    .onAppear { vm.loadMock() }
    } }

    This example creates a simple Notes model and ViewModel, then renders a list and loads mock data when the view appears.

    Next, connect data to a server in Networking or persist locally in Core Data.


    App Group

    Use an App Group to share small values (like counts, flags, timestamps) between your app and its Widget.

    The hook below writes notesCount whenever the ViewModel’s notes change.

    How to use it:

    • Enable the same App Group on both the app target and the widget target (e.g., group.com.example.notes).
    • Keep updateSharedNotesCount writing to that group ID; the didSet observer writes the new count on every change.
    • Read the value from the widget using the same suiteName (see snippet below).

    App Group hook (Widget Count)

    Example

    NotesViewModel.swift

    import Combine
    
    func updateSharedNotesCount(_ count: Int) {
      let defaults = UserDefaults(suiteName: "group.com.example.notes")
      defaults?.set(count, forKey: "notesCount")
    }
    
    class NotesViewModel: ObservableObject {
      @Published var notes: [Note] = [] {
    
    didSet { updateSharedNotesCount(notes.count) }
    } func add(title: String, body: String) {
    notes.append(.init(id: UUID(), title: title, body: body))
    } func delete(at offsets: IndexSet) {
    notes.remove(atOffsets: offsets)
    } }

    Example

    WidgetRead.swift

    import SwiftUI
    
    // Option A: Direct read via UserDefaults (e.g., from a TimelineProvider)
    let defaults = UserDefaults(suiteName: "group.com.example.notes")
    let count = defaults?.integer(forKey: "notesCount") ?? 0
    
    // Option B: @AppStorage with a custom store (usable in a Widget view)
    struct WidgetCountView: View {
      @AppStorage("notesCount", store: UserDefaults(suiteName: "group.com.example.notes"))
      private var notesCount: Int = 0
    
      var body: some View {
    
    Text("Notes: \(notesCount)")
    } }
  • Persistence

    What is Core Data?

    Core Data is Apple’s object graph and persistence framework.

    It manages models, relationships, and change tracking, and can persist to SQLite under the hood.


    Basic Setup

    Add Core Data when creating the project, or create a NSPersistentContainer manually and pass its viewContext down to your SwiftUI views via .environment(\_.managedObjectContext).

    This example demonstrates a basic setup with a single entity and a list view.

    Syntax:

    • let container = NSPersistentContainer(name: "Model")
    • container.loadPersistentStores

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    import CoreData
    
    @objc(Note)
    class Note: NSManagedObject {
      @NSManaged var title: String?
    }
    
    extension Note {
      @nonobjc class func fetchRequest() -> NSFetchRequest<Note> { NSFetchRequest<Note>(entityName: "Note") }
    }
    
    struct PersistenceController {
      static let shared = PersistenceController()
      let container: NSPersistentContainer
      init() {
    
    // Programmatic model so the sample runs without an .xcdatamodeld
    let model = NSManagedObjectModel()
    let entity = NSEntityDescription()
    entity.name = "Note"
    entity.managedObjectClassName = NSStringFromClass(Note.self)
    let title = NSAttributeDescription()
    title.name = "title"
    title.attributeType = .stringAttributeType
    title.isOptional = true
    entity.properties = &#91;title]
    model.entities = &#91;entity]
    container = NSPersistentContainer(name: "MyModel", managedObjectModel: model)
    let description = NSPersistentStoreDescription()
    description.type = NSInMemoryStoreType
    container.persistentStoreDescriptions = &#91;description]
    container.loadPersistentStores { _, error in
      if let error = error { fatalError("Unresolved error: \(error)") }
    }
    } } struct BasicCoreDataView: View { @Environment(\.managedObjectContext) private var context @FetchRequest(sortDescriptors: []) private var items: FetchedResults<Note> var body: some View {
    List(items, id: \.objectID) { note in Text(note.title ?? "") }
      .toolbar { Button("Add") { add() } }
    } private func add() {
    let i = Note(context: context)
    i.title = "New"
    try? context.save()
    } }

    This example initializes a persistent container and uses @Environment(\.managedObjectContext) with @FetchRequest to list and add items.

    The note about .xcdatamodeld is for running this in Xcode. It is not relevant for this example. But if you want to use Xcode, you can add a .xcdatamodeld file to your project.


    Sample App: Notes (Core Data)

    This example demonstrates a more complex setup with multiple entities and relationships.

    Syntax:

    • @Environment(\.managedObjectContext) for the context
    • @FetchRequest(sortDescriptors: [NSSortDescriptor(...)]) to query
    • context.save() to persist

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    import CoreData
    
    class NoteEntity: NSManagedObject {
      @NSManaged public var id: UUID?
      @NSManaged public var title: String?
      @NSManaged public var body: String?
    }
    
    extension NoteEntity: Identifiable {}
    
    struct PersistenceController {
      static let shared = PersistenceController()
      let container: NSPersistentContainer
      init() {
    
    container = NSPersistentContainer(name: "MyModel")
    container.loadPersistentStores { _, error in
      if let error = error { fatalError("Unresolved error: \(error)") }
    }
    } } struct NotesCoreDataView: View { @Environment(\.managedObjectContext) private var context @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \NoteEntity.title, ascending: true)]) private var notes: FetchedResults<NoteEntity> var body: some View {
    List(notes) { n in
      VStack(alignment: .leading) {
        Text(n.title ?? "Untitled").font(.headline)
        Text(n.body ?? "").font(.subheadline)
      }
    }
    .toolbar { Button("Add") { add() } }
    } private func add() {
    let n = NoteEntity(context: context)
    n.id = UUID(); n.title = "New Note"; n.body = ""
    try? context.save()
    } }

    App Group Shared Store (Widget Access)

    Store your Core Data SQLite file in an App Group so your Widget can read the same data.

    This example demonstrates how to set up an App Group shared store.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    import CoreData
    
    struct PersistenceController {
      static let shared = PersistenceController()
      let container: NSPersistentContainer
      init() {
    
    container = NSPersistentContainer(name: "MyModel")
    if let groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.example.notes") {
      let storeURL = groupURL.appendingPathComponent("MyModel.sqlite")
      let description = NSPersistentStoreDescription(url: storeURL)
      container.persistentStoreDescriptions = &#91;description]
    }
    container.loadPersistentStores { _, error in
      if let error = error { fatalError("Unresolved error: \(error)") }
    }
    } } struct AppGroupListView: View { @Environment(\.managedObjectContext) private var context @FetchRequest(sortDescriptors: []) private var items: FetchedResults<Item> var body: some View {
    List(items) { item in Text(item.title ?? "") }
      .toolbar { Button("Add") { add() } }
    } private func add() {
    let i = Item(context: context)
    i.title = "Shared Item"
    try? context.save()
    } }

    Enable the same App Group on the Widget target, then query via a Core Data stack that points to the same container URL.

  • Persistence

    UserDefaults API

    Store small preferences like flags or last-used values.

    Syntax:

    • UserDefaults.standard.set(_:forKey:)
    • .bool(forKey:)
    • SwiftUI @AppStorage("key")

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct UserDefaultsDemo: View {
      @AppStorage("username") private var username = ""
      var body: some View {
    
    VStack(spacing: 12) {
      Text(username.isEmpty ? "Hello, Guest" : "Hello, " + username)
      TextField("Username", text: $username)
        .textFieldStyle(.roundedBorder)
    }
    .padding()
    } }

    The UserDefaults API provides a simple way to store small preferences and settings, such as flags or last-used values.

    Tip: For larger data or sync, use Core Data or CloudKit.

    Avoid storing secrets in UserDefaults.

    App Group Shared Defaults (Widget Access)

    Share small values between your app and a Widget (or extensions) using an App Group suite.

    Syntax:

    • UserDefaults(suiteName: "group.id")
    • shared.set(_:forKey:)
    • shared.integer(forKey:)

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    import Foundation
    
    struct AppGroupDemo: View {
      private let suite = UserDefaults(suiteName: "group.com.example.notes")!
      @State private var count: Int = 0
      var body: some View {
    
    VStack(spacing: 12) {
      Text("Notes count: \(count)")
      HStack {
        Button("Increment") { count += 1; suite.set(count, forKey: "notesCount") }
        Button("Load") { count = suite.integer(forKey: "notesCount") }
        Button("Clear") { suite.removeObject(forKey: "notesCount"); count = 0 }
      }
    }
    .task { count = suite.integer(forKey: "notesCount") }
    .padding()
    } }
  • Networking

    GET Request with async/await

    Use URLSession.shared.data(from:) with async/await to fetch data.

    Syntax:

    • let (data, response) = try await URLSession.shared.data(from: url)
    • decode with JSONDecoder().decode

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    import Foundation
    
    struct Todo: Decodable, Identifiable { let id: Int; let title: String }
    
    func fetchTodos() async throws -> [Todo] {
      let url = URL(string: "https://jsonplaceholder.typicode.com/todos?_limit=2")!
      let (data, _) = try await URLSession.shared.data(from: url)
      return try JSONDecoder().decode([Todo].self, from: data)
    }
    
    struct NetworkingGetDemo: View {
      @State private var todos: [Todo] = []
      var body: some View {
    
    List(todos) { t in Text(t.title) }
      .task {
        do { todos = try await fetchTodos() } catch { print(error) }
      }
    } }

    This example fetches a small JSON list with URLSession using async/await and displays it in a SwiftUI List.


    Sample App: Notes (Networking)

    Use @MainActor class VM: ObservableObject { @Published var items: [T] = []; func loadFromAPI() async { ... } } to separate concerns and load data in the background.

    Syntax:

    • @MainActor class VM: ObservableObject { @Published var items: [T] = []; func loadFromAPI() async { ... } }
    • View: .task { await vm.loadFromAPI() } to kick off the async load
    • API: let (data, _) = try await URLSession.shared.data(from: url)JSONDecoder().decode

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    import Foundation
    
    struct NoteDTO: Decodable, Identifiable { let id: Int; let title: String; let body: String }
    
    enum API {
      static func fetchNotes() async throws -> [NoteDTO] {
    
    let url = URL(string: "https://jsonplaceholder.typicode.com/posts?_limit=3")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(&#91;NoteDTO].self, from: data)
    } } @MainActor final class NotesViewModel: ObservableObject { @Published var notes: [NoteDTO] = [] func loadFromAPI() async {
    do { notes = try await API.fetchNotes() }
    catch { print(error) }
    } } struct NotesView: View { @StateObject private var vm = NotesViewModel() var body: some View {
    List(vm.notes) { n in Text(n.title) }
      .task { await vm.loadFromAPI() }
    } }

    This example separates concerns with an API helper and a ViewModel, then loads notes into a SwiftUI List on task.


    POST JSON

    Send JSON by encoding your payload and setting the request method and headers.

    Syntax:

    • var req = URLRequest(url:)
    • req.httpMethod = "POST"
    • req.setValue("application/json", forHTTPHeaderField: "Content-Type")
    • req.httpBody = try JSONEncoder().encode(payload)
    • let (_, resp) = try await URLSession.shared.data(for: req)

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    import Foundation
    
    struct NewTodo: Encodable { let title: String; let completed: Bool }
    
    func createTodo(_ todo: NewTodo) async throws -> Int {
      let url = URL(string: "https://jsonplaceholder.typicode.com/todos")!
      var req = URLRequest(url: url)
      req.httpMethod = "POST"
      req.setValue("application/json", forHTTPHeaderField: "Content-Type")
      req.httpBody = try JSONEncoder().encode(todo)
      let (_, resp) = try await URLSession.shared.data(for: req)
      return (resp as? HTTPURLResponse)?.statusCode ?? 0
    }
    
    struct NetworkingPostDemo: View {
      @State private var status: Int? = nil
      var body: some View {
    
    VStack(spacing: 12) {
      Button("Create Todo") {
        Task {
          do { status = try await createTodo(NewTodo(title: "Demo", completed: false)) }
          catch { print(error); status = nil }
        }
      }
      if let status { Text("Status: \(status)") }
    }
    .padding()
    } }

    This example sends a JSON payload with a POST request and returns the HTTP status code from the response.

  • SwiftUI Styling Theming

    SwiftUI Styling Theming

    Centralize colors and styles with a theme to keep your app consistent and easy to update.


    Centralize styles with a Theme

    Define a Theme type to hold colors and spacing, then apply them throughout your views for consistency.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct Theme {
      static let primary = Color.blue
      static let cardBG = Color.blue.opacity(0.1)
    }
    
    struct ThemedCard: View {
      var body: some View {
    
    Text("Hello")
      .padding()
      .background(Theme.cardBG)
      .cornerRadius(8)
      .foregroundStyle(Theme.primary)
    } }
  • SwiftUI Styling Materials

    SwiftUI Styling Materials

    Use built-in blur materials like .regularMaterial to create depth and visual hierarchy.


    Use materials to add depth

    Materials are frosted blur effects that add depth and visual hierarchy to your app.

    The difference between materials is their opacity and blur amount.

    The most common materials are:

    • .regularMaterial – Frosted blur with a medium amount of blur and opacity.
    • .thickMaterial – Frosted blur with a thick amount of blur and opacity.
    • .thinMaterial – Frosted blur with a thin amount of blur and opacity.

    Apply a material fill (for example .regularMaterial) to create a frosted backdrop behind content.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct MaterialCard: View {
      var body: some View {
    
    ZStack {
      Image(systemName: "photo.fill").resizable().scaledToFill().ignoresSafeArea()
      RoundedRectangle(cornerRadius: 12).fill(.regularMaterial)
        .frame(height: 120)
        .overlay(Text("Regular Material").font(.headline))
        .padding()
    }
    } }
  • SwiftUI Styling Color Schemes

    SwiftUI Styling Color Schemes

    Use light/dark modes and the system color palette to keep your UI readable and consistent.


    Read the current color scheme

    Access the environment’s color scheme to adapt your UI for light or dark mode.

    Example

    Demo.swift

    ContentView.swift

    App.swift

    import SwiftUI
    
    struct ColorSchemesDemo: View {
      @Environment(\.colorScheme) var scheme
      var body: some View {
    
    Text(scheme == .dark ? "Scheme: Dark" : "Scheme: Light")
      .padding()
    } }