Swift 5.5 Concurrency: creating a task with custom error type
Asked Answered
H

1

11

I need to use my own custom error enum in tasks that I create:

enum MyError: Error {
 case someError
}

var myTask: Task<MyModel, MyError> = Task { () throws -> MyModel in
            // in case of an error: 
            // throw .someError
            // ... perform some work
            return MyModel()
        }

but I got the following error at the beginning of Task initializer: Referencing initializer 'init(priority:operation:)' on 'Task' requires the types 'MyError' and 'Error' be equivalent.

How can I restrict the Task to only throw errors that are of my custom error type MyError ?

Hypoacidity answered 17/8, 2021 at 12:24 Comment(5)
Why declare the type as Task<MyModel, MyError> in the first place?Baptiste
trying to tell the compiler that I need to use MyErrorHypoacidity
You're not quite getting what I'm asking. Nothing stops you from declaring Task<MyModel, Error> or event omitting the declaration altogether. You can throw whatever you want to throw.Baptiste
I need to declare Task<MyModel, MyError> not Task<MyModel, Error>Hypoacidity
No, you don't "need" to. You want to. There's a big difference.Baptiste
B
14

Omit the unnecessary type declaration:

var myTask = Task { () throws -> MyModel in
    // in case of an error:
    throw MyError.someError
    // ... perform some work
    return MyModel()
}

The compiler implicitly (and rightly) types myTask as a Task<MyModel, Error>. The compiler doesn't care that what you throw is a MyError, because that's a form of Error.


Okay, I see now that the issue is merely that you asked the wrong question. You want to know why you can't declare this Task as a Task<MyModel,MyError>.

The reason is because of how this initializer is declared:

extension Task where Failure == Error {
    public init(priority: TaskPriority? = nil, operation: @escaping @Sendable () async throws -> Success)
}

Do you see? This initializer is available only where Failure == Error. That's ==, not :. Using this initializer doesn't require that the Failure type be an Error, it requires that it be Error.

Baptiste answered 17/8, 2021 at 13:36 Comment(8)
I know that I can use it this way, but the error enums in my app are numerous as well the created tasks, I need a way to statically tell the compiler that a specific task should only throw MyError but not any other type of Error.Hypoacidity
If you know the answer, why are you asking the question?Baptiste
OK, wait, I see the problem: you've asked the wrong question. I'll answer the question you should have asked.Baptiste
thanks for the answer, I've edited the question text a little bit to be clearerHypoacidity
Well, as for "How can I restrict the Task to only throw errors that are of my custom error type MyError", you can't. But that's no big surprise. An ordinary throws function declaration cannot specify what type of thing it throws.Baptiste
Unfortunately this is so true at least until up this moment of time. Well, this is where Combine shines when I declare something like: func getData() -> AnyPublisher<SomeDataType, MyError> then my code is forced to only emit MyError errors.Hypoacidity
You can certainly write code that forces itself to emit only MyError errors! If your question is how to do that, ask a new question asking that. But the point is, you cannot use the Task generic Failure type as your way of doing it. If you think this is wrong, file a bug with Apple.Baptiste
I'm (also) surprised that Task, unlike Result, does not support a Failure that is some Error rather than just any Error or Never. This seems like it would fit well with the existing API and could be provided with a Task.init(_ priority: TaskPriority = nil, task: () -> Result<Success, Failure>) initialiser, I think? It would support typed Failure in the way Result does.Inure

© 2022 - 2024 — McMap. All rights reserved.