I am working thru the issues of turning on Strict Concurrency Checking. I have a SwiftData application, and I am compressing images before saving them as data. My save function is pretty simple
private func save() {
ImageCompressor.compress(image: (frontImageSelected?.asUIImage())!, maxByte: 1_048_576) { image in
guard image != nil else {
print("Error compressing image")
return
}
if let greetingCard {
greetingCard.cardName = cardName
greetingCard.cardFront = image?.pngData()
greetingCard.cardManufacturer = cardManufacturer
greetingCard.cardURL = cardURL
greetingCard.eventType = eventType
} else {
let newGreetingCard = GreetingCard(cardName: cardName, cardFront: image?.pngData(), eventType: eventType, cardManufacturer: cardManufacturer, cardURL: cardURL)
modelContext.insert(newGreetingCard)
}
}
}
I compress the selected image, I had to change my ImageCompressor.compress closure to Sendable, but now every assignment above is flagging with the above warning. I define the greetingCard as var greetingCard: GreetingCard?
in my view, since I can have it passed in for edit, or generated if new.
I also get the same warning on modelContext, which is defined as @Environment(\.modelContext) private var modelContext
.
It's not clear to me how to address this warning. Any pointers would be helpful.
Figured it out..
struct ImageCompressor {
/// This is a static method that takes three parameters:
/// image: The input UIImage that needs to be compressed.
/// maxByte: The maximum allowable size for the compressed image in bytes.
/// completion: A closure that will be called when the compression is complete, passing the resulting compressed UIImage or nil if an error occurs.
static func compress(image: UIImage, maxByte: Int,
completion: @Sendable @MainActor @escaping (UIImage?) -> Void) {
MainActor.assumeIsolated {
guard let currentImageSize = image.jpegData(compressionQuality: 1.0)?.count else {
return completion(nil)
}
var iterationImage: UIImage? = image
var iterationImageSize = currentImageSize
var iterationCompression: CGFloat = 1.0
while iterationImageSize > maxByte && iterationCompression > 0.01 {
let percentageDecrease: CGFloat
switch iterationImageSize {
case 0..<3000000: percentageDecrease = 0.05
case 3000000..<10000000: percentageDecrease = 0.1
default: percentageDecrease = 0.2
}
let canvasSize = CGSize(width: image.size.width * iterationCompression,
height: image.size.height * iterationCompression)
UIGraphicsBeginImageContextWithOptions(canvasSize, false, image.scale)
defer { UIGraphicsEndImageContext() }
image.draw(in: CGRect(origin: .zero, size: canvasSize))
iterationImage = UIGraphicsGetImageFromCurrentImageContext()
guard let newImageSize = iterationImage?.jpegData(compressionQuality: 1.0)?.count else {
return completion(nil)
}
iterationImageSize = newImageSize
iterationCompression -= percentageDecrease
}
completion(iterationImage)
}
}
}
Once I changed my ImageCompressor the save function looks like this -
private func save() {
ImageCompressor.compress(image: (frontImageSelected?.asUIImage())!, maxByte: 1_048_576) { image in
guard image != nil else {
print("Error compressing image")
return
}
if let greetingCard {
greetingCard.cardName = cardName
greetingCard.cardFront = image?.pngData()
greetingCard.cardManufacturer = cardManufacturer
greetingCard.cardURL = cardURL
greetingCard.eventType = eventType
} else {
let newGreetingCard = GreetingCard(cardName: cardName, cardFront: image?.pngData(), eventType: eventType, cardManufacturer: cardManufacturer, cardURL: cardURL)
modelContext.insert(newGreetingCard)
}
}
}