Skip to content

Instantly share code, notes, and snippets.

@yannxou
yannxou / ProcessInfo+SwiftUIPreview.swift
Created April 5, 2024 11:07
Swift: Detect code running in SwiftUI preview
extension ProcessInfo {
var isSwiftUIPreview: Bool {
environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
}
// or:
static func isOnPreview() -> Bool {
return processInfo.processName == "XCPreviewAgent"
@yannxou
yannxou / OneShotSubscription.swift
Last active April 2, 2024 09:36
Combine: One-shot subscriptions
// Credit: https://www.apeth.com/UnderstandingCombine/subscribers/subscribersoneshot.html
var cancellable: AnyCancellable?
cancellable = pub.sink(receiveCompletion: {_ in
cancellable?.cancel()
}) { image in
self.imageView.image = image
}
// Or just subscribe to the Subscribers.Sink:
@yannxou
yannxou / ScrollToModifier.swift
Created September 28, 2023 12:58
SwiftUI: ScrollToModifier
import SwiftUI
public struct ScrollToModifier<T: Hashable>: ViewModifier {
@Binding var id: T?
public func body(content: Content) -> some View {
ScrollViewReader { proxy in
content
.onChange(of: id) { newValue in
if let id {
scrollTo(id: id, using: proxy)
@yannxou
yannxou / ActorReentrancy.swift
Last active September 15, 2023 07:11
Swift: Handling actor reentrancy correctly
// Code from: https://alexdremov.me/swift-actors-common-problems-and-tips/
import Foundation
actor ActivitiesStorage {
var cache = [UUID: Task<Data?, Never>]()
func retrieveHeavyData(for id: UUID) async -> Data? {
if let task = cache[id] {
return await task.value
@yannxou
yannxou / onTapGestureAsync.swift
Created July 26, 2023 10:40
SwiftUI onTapGesture modifier async
extension View {
func onTapGesture(perform action: () async -> Void) -> some View {
self.onTapGesture {
Task { await action() }
}
}
}
@yannxou
yannxou / forcaselet.swift
Last active July 13, 2023 12:08
Swift: For case let example
import Foundation
enum State {
case idle
case running(speed: Double)
case paused(duration: TimeInterval)
case stopped(reason: String)
}
let states: [State] = [.idle, .paused(duration: 2), .running(speed: 10), .running(speed: 2)]
@yannxou
yannxou / FileComponents.swift
Created February 22, 2023 15:30
Swift: Split filename and extension
private func fileComponents(_ fileName: String) -> (name: String, extension: String) {
let fileExtension = URL(fileURLWithPath: fileName).pathExtension
let fileNameOnly = fileExtension.isEmpty ? fileName : String(fileName.dropLast(fileExtension.count + 1))
return (fileNameOnly, fileExtension)
}
@yannxou
yannxou / View+BindingSync.swift
Last active February 23, 2023 16:50
SwiftUI: Synchronize bindings
import SwiftUI
extension View {
/// Synchronizes the bindings so changes in any of them are propagated to the other.
///
/// The following example shows how to synchronize a Published property in a view model with a Binding used for presenting a sheet:
/// ```
/// class ViewModel: ObservableObject {
/// @Published var isPresented = false
@yannxou
yannxou / PassDownFocusState.swift
Created January 20, 2023 14:28
Pass FocusState property to child views
// source: https://developer.apple.com/forums/thread/682448
struct Parent: View {
@FocusState var focusedField: UUID?
var body: some View {
VStack {
Child(focusedField: _focusedField)
}
}
@yannxou
yannxou / UIDevice+CatalystOptimized.swift
Created January 14, 2023 14:34
Swift: Check if app running as Catalyst with 'Optimized for mac' setting enabled
// Credit: https://steipete.com/posts/forbidden-controls-in-catalyst-mac-idiom/
extension UIDevice {
/// Checks if we run in Mac Catalyst Optimized For Mac Idiom
var isCatalystMacIdiom: Bool {
if #available(iOS 14, *) {
return UIDevice.current.userInterfaceIdiom == .mac
} else {
return false
}