SwiftUI+Swift+Xcode Quicklook

Quick reference for how to use various SwiftUI components, common Swift functions, and some Xcode general knowledge. Always updating…

Simulator Documents Directory

The documents directory, which is where you app’s content such as CoreData SQLite files live, can be located below. If you don’t know the Device Simulator ID, usually sorting by last modified shows you the latest simulator you interacted with.

/Users/<USER>/Library/Developer/CoreSimulator/Devices/<Device Simulator ID>/data/Containers/Data

AF Networking Framework Debug Response

.response(completionHandler: { data in
    print(String(data: data.data!, encoding: .utf8))
})

Getting App Version+Build number

private var appVersion: String  = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
private var appBuildNumber: String = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"

Asking for App Store Review

Button(action: {
    if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
        AppStore.requestReview(in: scene)
    }
}, label: {
    Text("Rate on the App Store")
})

Sheet Presentation Height

.sheet(isPresented: $showEditor, content: {
    DetailView(customText: $customizedText)
        .presentationDetents([.medium])
})

TextEditor Placeholder Workaround

ZStack {
    if taskNote.taskBody.isEmpty {
        Text("Note Content...")
        TextEditor(text: .constant("Note content"))
            .padding()
            .disabled(true)
            .foregroundStyle(.gray)
    }
    TextEditor(text: $taskNote.taskBody)
        .padding()
        .focused($editorFocused)
        .opacity(taskNote.taskBody.isEmpty ? 0.25 : 1)
        .toolbar {
            ToolbarItemGroup(placement: .keyboard) {
                Spacer()
                Button("Done"){
                    editorFocused = false
                }
            }
        }
}

iOS Focused Text Done Button

@FocusState var editorFocused: Bool

TextEditor(text: $taskNote.taskBody)
    .padding()
    .focused($editorFocused)
    .toolbar {
        ToolbarItemGroup(placement: .keyboard) {
            Spacer()
            Button("Done"){
                editorFocused = false
            }
        }
    }

Load JSON directly into Swift Class

private func loadCategoryJSON() {
    // Load file from main Bundle
    let url = Bundle.main.url(forResource: "CategoryData", withExtension: "json")!
    let data = try! Data(contentsOf: url)
    let decoder = JSONDecoder()
    // Set the decoding Strategy so we can convert from "category_title" JSON key to "categoryTitle" Swift property
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    do {
        let fileArray = try decoder.decode([ContentCategory].self, from: data)
        if (fileArray.count > 0){
            print(fileArray)
        }else{

        }
    }catch {
        print(error)
    }
}

class ContentCategory: Decodable {
    var categoryTitle: String
    var categoryEmoji: String    
}

Shared Instance

    static let shared: APIManager = {
        let manager = APIManager()
        return manager
    }()

App Storage

    @AppStorage("username") var username: String = "Anonymous"

Popover

@State var showIndvFileSettings = false

Button("Settings") {
    showIndvFileSettings.toggle()
}
.popover(isPresented: $showIndvFileSettings,arrowEdge: .trailing) {
    VStack {
        Text("Popover Content")
    }
    .padding()
}

File Importer/Selector

let allowedSelectionFileTypes: [UTType] = [.commaSeparatedText,.html]
@State var loadFilePresenting = false

Button("Load File") {
    loadFilePresenting.toggle()
}.fileImporter(isPresented: $loadFilePresenting, allowedContentTypes: allowedSelectionFileTypes) { result in
    switch result {
    case .success(let url):
        url.startAccessingSecurityScopedResource()
        // Do something with selected URL
        url.stopAccessingSecurityScopedResource()
    case .failure(let error):
        print(error)
    }
}
.fileDialogDefaultDirectory(URL(filePath: "~/Library/Safari/Bookmarks.plist"))

Confirmation Alert

.confirmationDialog("Delete Confirmation", isPresented: $showDeleteConfirmation, actions: {
    Button("Delete", role: .destructive) {
        print("Action to Delete")
    }
}, message: {
    Text("Are you sure you want to delete this Item?")
})

Iterable Enum

enum StatusClasses: String, Codable, CaseIterable {
    case Active = "Active"
    case Pending = "Pending"
    case Inactive = "Inactive"
    case Archived = "Archived"
    case InitialContact = "Initial Contact"
}

// Showing as List
Picker("Status", selection: $object.status) {
    ForEach(StatusClasses.allCases, id: \.self) { status in
        Text(status.rawValue)
            .tag(status.rawValue)
    }
}

Binding SwiftData Object

// Content View
ForEach(notes) { note in
	NavigationLink {
		EditorView(selectedNote: note)
	}
}

// Editor View
@Bindable var selectedNote: Note

var body: some View {
	VStack {
		TextField("Title", $selectedNote.title)
	}
}

Open URL in Safari

struct ContentView: View {
    @Environment(\.openURL) private var openURL

    var body: some View {
    	VStack {
    		Button("Open URL") {
    			openURL(URL(string:"https://nsmug.com")!)
    		}
    	}
    }
}