I'm making my first attempts with Swift's async/await features and Task.
My first goal is working with NSItemProvider
. I have a custom class that extends NSObject
and conforms to NSSecureCoding
. I have working code that converts my class to and from NSItemProvider
.
What I'm now trying to do is convert an array of NSItemProvider
objects back into an array of MyClass
objects. Each conversion requires a call to an async
function.
I'm able to do this using a plain for in
loop. The trouble begins when I try to use compactMap
or forEach
to create the new array from the array of NSItemProvider
.
Here is some code that can be copied into a Swift Playground or project that demonstrates the issue.
import Foundation
class MyClass: NSObject, NSSecureCoding {
var name: String
override var description: String {
return name
}
init(name: String) {
self.name = name
}
static var supportsSecureCoding = true
func encode(with coder: NSCoder) {
coder.encode(name, forKey: "name")
}
required init?(coder: NSCoder) {
if let name = coder.decodeObject(of: NSString.self, forKey: "name") as? String {
self.name = name
} else {
return nil
}
}
}
extension MyClass {
static let identifier = "mySpecialId"
func asItemProvider() -> NSItemProvider {
return NSItemProvider(item: self, typeIdentifier: Self.identifier)
}
static func create(withItemProvider itemProvider: NSItemProvider) async throws -> Self? {
let res = try await itemProvider.loadItem(forTypeIdentifier: identifier)
return res as? Self
}
}
func example() {
Task {
let objs = [ MyClass(name: "A"), MyClass(name: "B"), MyClass(name: "C"), MyClass(name: "D") ]
let providers: [NSItemProvider] = objs.map { $0.asItemProvider() }
do {
// This basic `for in` loop works
var newObjs = [MyClass]()
for provider in providers {
if let obj = try await MyClass.create(withItemProvider: provider) {
newObjs.append(obj)
}
}
print("newObjs: \(newObjs)")
// Attempt to use `forEach`
// Error: Cannot pass function of type '(NSItemProvider) async -> MyClass?' to parameter expecting synchronous function type
/*
providers.forEach { provider in
if let obj = try await MyClass.create(withItemProvider: provider) {
newObjs.append(obj)
}
}
*/
// Attempt to use `compactMap`
// Error: Cannot pass function of type '(NSItemProvider) async -> MyClass?' to parameter expecting synchronous function type
//let newObjs2 = providers.compactMap { await MyClass.create(withItemProvider: $0) }
} catch {
}
}
}
I was looking at another question but the solutions are far more complicated than just using a simple for in
loop.
Is there a simpler way to use compactMap
, for example, to directly convert the array of NSItemProvider
into an array of MyClass
?