I’m constructing an app in Swift UI and Swift 6 for iOS 26. I’m utilizing SwiftData to retailer content material. I’ve an edit sheet which I’m combating.
When the sheet hundreds, the person can’t faucet the TextField to edit instantly; it takes a number of faucets for the keyboard to look, and due to that, the textual content within the discipline is highlighted for lower, copy, and paste. I’ve been at this for days, even utilizing AI to see if it will possibly assist, and I’m no additional ahead.
My EditPolicyView.swift code:
//
// EditPolicyView.swift
// Coverage Pal
//
// Created by Justin Erswell on 09/01/2026.
//
import SwiftUI
import SwiftData
import PhotosUI
// Light-weight attachment abstract - no binary information, simply metadata for show
struct AttachmentSummary: Identifiable, Sendable {
let id: UUID
let filename: String
let mimeType: String
let isExisting: Bool // true = already saved in SwiftData, false = newly added
var isPDF: Bool { mimeType == "utility/pdf" }
// Init for present attachments (extracted values, not the mannequin itself)
init(id: UUID, filename: String, mimeType: String, isExisting: Bool) {
self.id = id
self.filename = filename
self.mimeType = mimeType
self.isExisting = isExisting
}
// Comfort init for brand spanking new attachments
init(id: UUID = UUID(), filename: String, mimeType: String) {
self.id = id
self.filename = filename
self.mimeType = mimeType
self.isExisting = false
}
}
// Easy worth struct to move information with out SwiftData statement
// NOTE: Attachments are NOT copied right here to keep away from blocking primary thread with massive binary information
struct EditPolicyData: Identifiable {
let id: PersistentIdentifier
var identify: String
var class: PolicyCategory
var supplier: String
var policyNumber: String
var price: Decimal
var costFrequency: CostFrequency
var renewalDate: Date
var notes: String
var reminderThirtyDays: Bool
var reminderFourteenDays: Bool
var reminderThreeDays: Bool
var reminderRenewalDay: Bool
init(from coverage: PolicyItem) {
let begin = CFAbsoluteTimeGetCurrent()
self.id = coverage.persistentModelID
print("⏱️ EditPolicyData: persistentModelID took (CFAbsoluteTimeGetCurrent() - begin)s")
let t1 = CFAbsoluteTimeGetCurrent()
self.identify = coverage.identify
self.class = coverage.class
self.supplier = coverage.supplier
self.policyNumber = coverage.policyNumber
self.price = coverage.price
self.costFrequency = coverage.costFrequency
self.renewalDate = coverage.renewalDate
self.notes = coverage.notes
print("⏱️ EditPolicyData: primary props took (CFAbsoluteTimeGetCurrent() - t1)s")
let t2 = CFAbsoluteTimeGetCurrent()
let schedule = coverage.reminderSchedule
self.reminderThirtyDays = schedule.thirtyDays
self.reminderFourteenDays = schedule.fourteenDays
self.reminderThreeDays = schedule.threeDays
self.reminderRenewalDay = schedule.renewalDay
print("⏱️ EditPolicyData: reminderSchedule took (CFAbsoluteTimeGetCurrent() - t2)s")
print("⏱️ EditPolicyData: TOTAL took (CFAbsoluteTimeGetCurrent() - begin)s")
}
}
// Wrapper view that passes information to the precise kind
struct EditPolicyView: View {
let information: EditPolicyData
var physique: some View {
EditPolicyFormView(
policyID: information.id,
initialName: information.identify,
initialCategory: information.class,
initialProvider: information.supplier,
initialPolicyNumber: information.policyNumber,
initialCost: information.price,
initialCostFrequency: information.costFrequency,
initialRenewalDate: information.renewalDate,
initialNotes: information.notes,
initialReminderThirtyDays: information.reminderThirtyDays,
initialReminderFourteenDays: information.reminderFourteenDays,
initialReminderThreeDays: information.reminderThreeDays,
initialReminderRenewalDay: information.reminderRenewalDay
)
}
// Comfort init
init(information: EditPolicyData) {
self.information = information
}
init(coverage: PolicyItem) {
self.information = EditPolicyData(from: coverage)
}
}
// Precise kind view with inline @State initialization (like AddPolicyView)
struct EditPolicyFormView: View {
@Setting(.dismiss) non-public var dismiss
@Setting(.modelContext) non-public var modelContext
@EnvironmentObject non-public var appSettings: AppSettings
// Retailer the coverage ID for saving
let policyID: PersistentIdentifier
// Preliminary values handed in
let initialName: String
let initialCategory: PolicyCategory
let initialProvider: String
let initialPolicyNumber: String
let initialCost: Decimal
let initialCostFrequency: CostFrequency
let initialRenewalDate: Date
let initialNotes: String
let initialReminderThirtyDays: Bool
let initialReminderFourteenDays: Bool
let initialReminderThreeDays: Bool
let initialReminderRenewalDay: Bool
// Kind state - utilizing inline initialization like AddPolicyView
@State non-public var identify = ""
@State non-public var class: PolicyCategory = .insurance coverage
@State non-public var supplier = ""
@State non-public var policyNumber = ""
@State non-public var price: Decimal = 0
@State non-public var costString = ""
@State non-public var costFrequency: CostFrequency = .yearly
@State non-public var renewalDate = Date()
@State non-public var notes = ""
// Reminder schedule
@State non-public var reminderThirtyDays = true
@State non-public var reminderFourteenDays = true
@State non-public var reminderThreeDays = true
@State non-public var reminderRenewalDay = true
// Monitor if we have loaded preliminary values
@State non-public var hasLoadedInitialValues = false
// Attachments - use light-weight summaries for show, monitor adjustments individually
@State non-public var attachmentSummaries: [AttachmentSummary] = []
@State non-public var newAttachments: [Attachment] = [] // Newly added attachments (with information)
@State non-public var deletedAttachmentIDs: Set<UUID> = [] // IDs of present attachments to delete
@State non-public var attachmentsLoaded = false
@State non-public var selectedPhotoItems: [PhotosPickerItem] = []
@State non-public var showingDocumentScanner = false
@State non-public var showingFilePicker = false
@State non-public var showingValidationError = false
@State non-public var validationErrorMessage = ""
// MARK: - Subscription-specific Labels
non-public var isSubscription: Bool {
class == .subscription
}
non-public var nameFieldLabel: String {
isSubscription ? "Subscription Identify" : "Identify"
}
non-public var providerFieldLabel: String {
isSubscription ? "Service" : "Supplier"
}
non-public var referenceFieldLabel: String {
isSubscription ? "Account ID (optionally available)" : "Reference Quantity"
}
non-public var dateFieldLabel: String {
isSubscription ? "Subsequent Billing Date" : "Renewal Date"
}
non-public var basicInfoSectionHeader: String {
isSubscription ? "Subscription Particulars" : "Primary Info"
}
non-public var dateSectionHeader: String {
isSubscription ? "Billing" : "Renewal"
}
non-public var reminderFooterText: String {
isSubscription
? "You may obtain notifications at 9:00 AM earlier than your billing date."
: "You may obtain notifications at 9:00 AM on nowadays."
}
var physique: some View {
// Match AddPolicyView construction precisely
NavigationStack {
Kind {
// Primary Information Part - minimal take a look at
Part {
TextField(nameFieldLabel, textual content: $identify)
} header: {
Textual content(basicInfoSectionHeader)
}
}
.navigationTitle("Edit Report")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Save") {
saveChanges()
}
.disabled(identify.isEmpty)
}
}
.alert("Validation Error", isPresented: $showingValidationError) {
Button("OK") { }
} message: {
Textual content(validationErrorMessage)
}
.onAppear {
// Load preliminary values solely as soon as
if !hasLoadedInitialValues {
identify = initialName
class = initialCategory
supplier = initialProvider
policyNumber = initialPolicyNumber
price = initialCost
costString = "(initialCost)"
costFrequency = initialCostFrequency
renewalDate = initialRenewalDate
notes = initialNotes
reminderThirtyDays = initialReminderThirtyDays
reminderFourteenDays = initialReminderFourteenDays
reminderThreeDays = initialReminderThreeDays
reminderRenewalDay = initialReminderRenewalDay
hasLoadedInitialValues = true
}
}
}
/* TEMPORARILY DISABLED - restore after keyboard take a look at
.sheet(isPresented: $showingDocumentScanner) {
DocumentScannerView { pictures in
processScannedImages(pictures)
}
}
.sheet(isPresented: $showingFilePicker) {
DocumentPickerView { urls in
processSelectedFiles(urls)
}
}
.onChange(of: selectedPhotoItems) { _, newItems in
processSelectedPhotos(newItems)
}
.activity {
// Load attachments in background to keep away from blocking UI
await loadAttachments()
}
*/
}
// Load attachment METADATA solely (not binary information) to keep away from blocking primary thread
non-public func loadAttachments() async {
guard !attachmentsLoaded else { return }
let begin = CFAbsoluteTimeGetCurrent()
print("⏱️ loadAttachments: beginning...")
// Use a background context to keep away from blocking primary thread
let container = modelContext.container
let policyIDCopy = policyID
// Fetch uncooked metadata as tuples (Sendable) from background
let metadata: [(UUID, String, String)] = await Job.indifferent {
let bgStart = CFAbsoluteTimeGetCurrent()
let backgroundContext = ModelContext(container)
guard let coverage = backgroundContext.mannequin(for: policyIDCopy) as? PolicyItem else {
return []
}
// Solely entry metadata properties, NOT the information property
let end result = coverage.safeAttachments.map { ($0.id, $0.filename, $0.mimeType) }
print("⏱️ loadAttachments background activity took (CFAbsoluteTimeGetCurrent() - bgStart)s")
return end result
}.worth
// Create summaries on primary actor
attachmentSummaries = metadata.map {
AttachmentSummary(id: $0.0, filename: $0.1, mimeType: $0.2, isExisting: true)
}
attachmentsLoaded = true
print("⏱️ loadAttachments: TOTAL took (CFAbsoluteTimeGetCurrent() - begin)s")
}
// MARK: - Save Modifications
non-public func saveChanges() {
guard !identify.trimmingCharacters(in: .whitespaces).isEmpty else {
validationErrorMessage = "Please enter a reputation."
showingValidationError = true
return
}
// Fetch the coverage by ID
guard let coverage = modelContext.mannequin(for: policyID) as? PolicyItem else {
validationErrorMessage = "Couldn't discover report to replace."
showingValidationError = true
return
}
coverage.identify = identify.trimmingCharacters(in: .whitespaces)
coverage.class = class
coverage.supplier = supplier.trimmingCharacters(in: .whitespaces)
coverage.policyNumber = policyNumber.trimmingCharacters(in: .whitespaces)
coverage.price = price
coverage.costFrequency = costFrequency
coverage.renewalDate = renewalDate
coverage.notes = notes.trimmingCharacters(in: .whitespaces)
coverage.updatedAt = Date()
coverage.reminderSchedule = ReminderSchedule(
thirtyDays: reminderThirtyDays,
fourteenDays: reminderFourteenDays,
threeDays: reminderThreeDays,
renewalDay: reminderRenewalDay
)
// Solely modify attachments that modified (not rewriting every part)
// 1. Take away deleted attachments
if !deletedAttachmentIDs.isEmpty {
coverage.safeAttachments.removeAll { deletedAttachmentIDs.incorporates($0.id) }
}
// 2. Add new attachments
for attachment in newAttachments {
coverage.safeAttachments.append(attachment)
}
// Reschedule notifications
Job {
await NotificationManager.shared.scheduleNotifications(for: coverage)
}
dismiss()
}
// MARK: - Attachment Dealing with
non-public func removeAttachment(_ abstract: AttachmentSummary) {
attachmentSummaries.removeAll { $0.id == abstract.id }
if abstract.isExisting {
// Mark present attachment for deletion on save
deletedAttachmentIDs.insert(abstract.id)
} else {
// Take away newly added attachment
newAttachments.removeAll { $0.id == abstract.id }
}
}
non-public func processScannedImages(_ pictures: [UIImage]) {
for (index, picture) in pictures.enumerated() {
if let information = picture.jpegData(compressionQuality: 0.8) {
let id = UUID()
let filename = "scan_(attachmentSummaries.rely + index + 1).jpg"
let mimeType = "picture/jpeg"
// Add to newAttachments (with information) for saving
let attachment = Attachment(filename: filename, information: information, mimeType: mimeType)
attachment.id = id
newAttachments.append(attachment)
// Add abstract for show
attachmentSummaries.append(AttachmentSummary(id: id, filename: filename, mimeType: mimeType))
}
}
}
non-public func processSelectedPhotos(_ objects: [PhotosPickerItem]) {
for merchandise in objects {
Job {
if let information = strive? await merchandise.loadTransferable(sort: Knowledge.self) {
await MainActor.run {
let id = UUID()
let filename = "photo_(attachmentSummaries.rely + 1).jpg"
let mimeType = "picture/jpeg"
// Add to newAttachments (with information) for saving
let attachment = Attachment(filename: filename, information: information, mimeType: mimeType)
attachment.id = id
newAttachments.append(attachment)
// Add abstract for show
attachmentSummaries.append(AttachmentSummary(id: id, filename: filename, mimeType: mimeType))
}
}
}
}
selectedPhotoItems = []
}
non-public func processSelectedFiles(_ urls: [URL]) {
for url in urls {
guard url.startAccessingSecurityScopedResource() else { proceed }
defer { url.stopAccessingSecurityScopedResource() }
if let information = strive? Knowledge(contentsOf: url) {
let id = UUID()
let filename = url.lastPathComponent
let mimeType = url.pathExtension.lowercased() == "pdf" ? "utility/pdf" : "picture/jpeg"
// Add to newAttachments (with information) for saving
let attachment = Attachment(filename: filename, information: information, mimeType: mimeType)
attachment.id = id
newAttachments.append(attachment)
// Add abstract for show
attachmentSummaries.append(AttachmentSummary(id: id, filename: filename, mimeType: mimeType))
}
}
}
}
#Preview {
EditPolicyView(coverage: PolicyItem(
identify: "Check Coverage",
class: .insurance coverage,
supplier: "Check Supplier",
renewalDate: Date()
))
.modelContainer(for: PolicyItem.self, inMemory: true)
.environmentObject(AppSettings.shared)
}
Additionally A screenshot of the view working on an iPhone 17 Professional Max: 
I’m certain I’m doing one thing intensely silly and would be glad about assist from the neighborhood on this.

