Snippets

Async Transferable for ShareLink

struct AsyncImageTransferable: Codable, Transferable {
  let url: URL
 
  func fetchAsImage() async -> Image {
    let data = try? await URLSession.shared.data(from: url).0
    guard let data, let uiimage = UIImage(data: data) else {
      return Image(systemName: "photo")
    }
    return Image(uiImage: uiimage)
  }
 
  static var transferRepresentation: some TransferRepresentation {
    ProxyRepresentation { media in
      await media.fetchAsImage()
    }
  }
}
 
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")
    }
  }
}

2023 © Thomas Ricouard.