Snippets

VisionOS: AccessoryView or Ornament in SwiftUI, depending on the target OS.

public struct EditorView: View {
  public var body: some View {
    EditTextView(text: .constant("Hello, World!"))
    #if os(visionOS)
    .ornament(attachmentAnchor: .scene(.bottom)) {
        Button("Done") { }
    #else
    .safeAreaInset(edge: .bottom) {
        Button("Done") { }
    }
    #endif
  }
}

TabView with custom binding for side effects on tab selection

struct RootView: View {
    enum Tab: Int, CaseIterable {
        case home, notifications, search, profile
    }
 
    @State private var selectedTab: Tab = .home
 
    TabView(selection: .init(get: {
        selectedTab
    }, set: { newTab in
        if newTab == .post {
            appRouterPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility)
            return
        }
 
        HapticManager.shared.fireHaptic(.tabSelection)
        SoundEffectManager.shared.playSound(.tabSelection)
 
        selectedTab = newTab
    })) {
        ForEach(Tab.allCases, id: \.self) { tab in
            tab.makeContentView()
            .tabItem {
                tab.label
            }
            .tag(tab)
        }
    }
}

Async Transferable for ShareLink

struct AsyncImageTransferable: Codable, Transferable {
  let url: URL
 
  func fetchData() async -> Data {
    do {
      return try await URLSession.shared.data(from: url).0
    } catch {
      return Data()
    }
  }
 
  static var transferRepresentation: some TransferRepresentation {
    DataRepresentation(exportedContentType: .jpeg) { transferable in
      await transferable.fetchData()
    }
  }
}
 
struct ShareView: View {
  let imageURL: URL
 
  var body: some View {
    let transferable = AsyncImageTransferable(url: imageURL)
    ShareLink(item: transferable, preview: .init("Share this image", image: transferable))
  }
}

Access AppDelegate from SwiftUI App

@main
struct IceCubesApp: App {
  @UIApplicationDelegateAdaptor private var appDelegate: AppDelegatern
}
 
class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_: UIApplication,
                   didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool
  {
    print("Application did finish lauching")
  }
}

Programmatic navigation with NavigationStack

public enum RouterDestination: Hashable {
  case accountDetail(id: String)
}
 
@MainActor
extension View {
  func withAppRouter() -> some View {
    navigationDestination(for: RouterDestination.self) { destination in
      switch destination {
      case let .accountDetail(id):
        AccountDetailView(accountId: id)
      }
    }
  }
}
 
@MainActor
public class RouterPath: ObservableObject {
  @Published public var path: [RouterDestination] = []
 
  public func navigate(to: RouterDestination) {
    path.append(to)
  }
}
 
 
struct NotificationsTab: View {
  @StateObject private var routerPath = RouterPath()
 
  var body: some View {
    NavigationStack(path: $routerPath.path) {
        Button("Navigate to account") {
          routerPath.navigate(to: .accountDetail(id: accountId))
        }
        .withAppRouter()
    }
  }
}

X Thread (opens in a new tab)


Sheet with visual effect / blurred background and corner radius

public struct AppAccountsSelectorView: View {
  @State private var isPresented: Bool = false
 
  public var body: some View {
    Button {
      isPresented.toggle()
    } label: {
      Text("Present sheet")
    }
    .sheet(isPresented: $isPresented) {
      accountsView
      .presentationDetents([.height(preferredHeight), .large])
      .presentationBackground(.thinMaterial)
      .presentationCornerRadius(16)
    }
  }
}

Save array of Codable in @AppStorage

extension Array: RawRepresentable where Element: Codable {
  public init?(rawValue: String) {
    guard let data = rawValue.data(using: .utf8),
          let result = try? JSONDecoder().decode([Element].self, from: data)
    else {
      return nil
    }
    self = result
  }
 
  public var rawValue: String {
    guard let data = try? JSONEncoder().encode(self),
          let result = String(data: data, encoding: .utf8)
    else {
      return "[]"
    }
    return result
  }
}

Minimal SwiftUI + SwiftData app

@Model
final class Thread {
  @Attribute(.unique) var id: UUID
  var createdOn: Date
  var content: String
  var author: User
  var isLiked: Bool = false
 
  init(content: String, author: User) {
    self.id = UUID()
    self.createdOn = Date()
    self.content = content
    self.author = author
  }
}
 
@Model
final class User {
  @Attribute(.unique) var id: UUID
  var username: String
 
  init(username: String) {
    self.id = UUID()
    self.username = username
  }
}
 

Creating and using custom Environment values in SwiftUI

public extension EnvironmentValues {
  var isSecondaryColumn: Bool {
    get { self[SecondaryColumnKey.self] }
    set { self[SecondaryColumnKey.self] = newValue }
  }
}
 
public struct NotificationsTab: View {
  public var body: some View {
     NotificationsListView()
      .environment(\.isSecondaryColumn, true)
  }
}
 
public struct NotificationsListView: View {
  @Environment(\.isSecondaryColumn) private var isSecondaryColumn: Bool
 
  public var body: some View {
    if isSecondaryColumn {
      Text("I'm secondary column")
    } else {
      Text("I'm not secondary column")
    }
  }
}

2024 © Thomas Ricouard.