i am trying to use a publisher, but i got this error: "Escaping closure captures non-escaping parameter 'promise'" in the Timer. line.
How can i solve this?
extension OperationQueue {
func publisher<Output, Failure: Error>(_ block: #escaping (Future<Output, Failure>.Promise) -> Void) -> AnyPublisher<Output, Failure> {
Future<Output, Failure> { promise in
self.addOperation {
block(promise)
}
}.eraseToAnyPublisher()
}
}
struct ContentView: View {
func getDataViaPublisher()->AnyPublisher<String,Error> {
OperationQueue.main.publisher { promise in
Timer.scheduledTimer(withTimeInterval: Double.random(in: 1...3), repeats: false) { (timer) in. // <<<<< error here
promise(.success("Chris"))
}
}
}
Promise is also closure, so you need to make it #escaping in arguments as well.
Here is fixed extension
extension OperationQueue {
func publisher<Output, Failure: Error>(_ block: #escaping (#escaping Future<Output, Failure>.Promise) -> Void) -> AnyPublisher<Output, Failure> {
Future<Output, Failure> { promise in
self.addOperation {
block(promise)
}
}.eraseToAnyPublisher()
}
}
Related
I am stuck with this situation where I have a custom JSONDecoder struct which contains a private function to decode data, and another function which is exposed, and should return a specific, Decodable type. I would like these functions to throw successively so I only have to write my do/catch block inside the calling component, but I'm stuck with this error on the exposedFunc() function:
Invalid conversion from throwing function of type '(Completion) throws -> ()' (aka '(Result<Data, any Error>) throws -> ()') to non-throwing function type '(Completion) -> ()' (aka '(Result<Data, any Error>) -> ()')
Here is the code:
import Foundation
import UIKit
typealias Completion = Result<Data, Error>
let apiProvider = ApiProvider()
struct DecodableTest: Decodable {
}
struct CustomJSONDecoder {
private static func decodingFunc<T: Decodable>(
_ response: Completion,
_ completion: #escaping (T) -> Void
) throws {
switch response {
case .success(let success):
try completion(
JSONDecoder().decode(
T.self,
from: success
)
)
case .failure(let error):
throw error
}
}
static func exposedFunc(
value: String,
_ completion: #escaping (DecodableTest) -> Void
) throws {
apiProvider.request {
try decodingFunc($0, completion)
}
}
}
class CustomViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
do {
try CustomJSONDecoder.exposedFunc(value: "test_value") { result in
// Do something with result
}
} catch {
print(error)
}
}
}
class ApiProvider: NSObject {
func request(_ completion: #escaping (Completion) -> ()) {
}
}
Thank you for your help
This defines method that takes a non-throwing function:
class ApiProvider: NSObject {
func request(_ completion: #escaping (Completion) -> ()) {
}
}
So in all cases, this function must take a Completion and return Void without throwing. However, you pass the following:
apiProvider.request {
try decodingFunc($0, completion)
}
This method does throw (note the uncaught try), so that's not allowed. You need to do something if this fails:
apiProvider.request {
do {
try decodingFunc($0, completion)
} catch {
// Here you must deal with the error without throwing.
}
}
}
Not using concurrency features is making your code hard to understand. Switch!
final class CustomViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Task {
let result = DecodableTest()
// Do something with result
}
}
}
extension DecodableTest {
init() async throws {
self = try JSONDecoder().decode(Self.self, from: await APIProvider.data)
}
}
enum APIProvider {
static var data: Data {
get async throws { .init() }
}
}
I'm trying to change my function from using a completion handler to a function that uses combine, but I'm getting an error message.
with completionHandler:
typealias AuthorizationCompletion = (UNAuthorizationStatus) -> ()
func reloadAuthorizationStatus(completion: #escaping AuthorizationCompletion) {
UNUserNotificationCenter.current().getNotificationSettings { settings in
DispatchQueue.main.async {
let notificationStatus = settings.authorizationStatus
completion(notificationStatus)
}
}
Using Combine:
func reloadAuthorizationStatus() -> AnyPublisher<UNAuthorizationStatus, Error> {
UNUserNotificationCenter.current().getNotificationSettings { settings in
Just(settings)
.tryMap({$0.authorizationStatus})
.eraseToAnyPublisher()
}
But getting the error Cannot convert return expression of type 'Void' to return type 'AnyPublisher<UNAuthorizationStatus, Error>'
From the moment I add the return type to the function, the settings let will change from type UNNotificationSettings to error type
What am I missing here ?
Future is the publisher you need
func reloadAuthorizationStatus() -> AnyPublisher<UNAuthorizationStatus, Never> {
Future<UNAuthorizationStatus, Never> { promise in
UNUserNotificationCenter.current()
.getNotificationSettings { settings in
promise(.success(settings.authorizationStatus))
}
}
.eraseToAnyPublisher()
}
I'm trying to make a DB mock to test some UI implementations, but compiler keeps giving me the following error:
Type 'DBClientMock<T>' does not conform to protocol 'DBClient'
This is my code...
protocol DBClient {
associatedtype Model
func get(id: UUID, completion: #escaping ((Model?, Error?)) -> Void)
func insert(_ model: Model, completion: #escaping (Error?) -> Void)
func delete(_ model: Model, completion: #escaping (Error?) -> Void)
}
final class DBClientMock<T>: DBClient where T: Identifiable, T: Equatable {
typealias Model = T
private let queue: DispatchQueue
private var modelDB = [T]()
enum Error: Swift.Error {
case notFound
}
init(queue: DispatchQueue = DispatchQueue.global()) {
self.queue = queue
}
private func getModel(id: UUID) -> T? {
let results = modelDB.filter({ $0.id as? UUID == id })
guard results.count > 0 else { return nil }
return results[0]
}
// Extension
func get(id: UUID, completion: #escaping ((T?, Error?)) -> Void) {
let record = getModel(id: id)
queue.asyncAfter(deadline: .now() + .milliseconds(1500), execute: {
if let model = record {
completion((model, nil))
} else {
completion((nil, Error.notFound))
}
})
}
func insert(_ model: T, completion: #escaping (Error?) -> Void) {
modelDB.append(model)
queue.asyncAfter(deadline: .now() + .milliseconds(1000), execute: {
completion(nil)
})
}
func delete(_ model: T, completion: #escaping (Error?) -> Void) {
modelDB.removeAll(where: { $0 == model })
queue.asyncAfter(deadline: .now() + .milliseconds(800), execute: {
completion(nil)
})
}
}
XCode: Version 12.4 (12D4e)
Swift: 5.0
What am I doing wrong? Do I have to be more explicit with the generic type in some way? I tried replacing T with Model but had the same result.
Thanks for your help!
It doesn't conform because you declared another Error type inside the class, so everywhere where you use Error in the required methods, it uses DBClientMock.Error instead of the protocol-required Swift.Error.
Either rename DBClientMock.Error to something else, or change the Error in methods to Swift.Error, like below:
// Extension
func get(id: UUID, completion: #escaping (T?, Swift.Error?) -> Void) {
//...
}
func insert(_ model: T, completion: #escaping (Swift.Error?) -> Void) {
//...
}
func delete(_ model: T, completion: #escaping (Swift.Error?) -> Void) {
//...
}
If you have a sync function, how would you convert it to an async function?
func syncFunc() -> Int {
//Do something
}
Would this work?
func asyncFunc(_ syncFunc:()->Int, _ completion:(Int)->()) -> Int {
DispatchQueue.background.async{
completion( syncFunc() )
}
}
No, functions containing an asynchronous task cannot return any value from the closure body and both closures must be marked as #escaping
func asyncFunc(_ syncFunc: #escaping ()->Int, completion: #escaping (Int)->()) {
DispatchQueue.global().async {
completion( syncFunc() )
}
}
I would like to call an #autoclosure parameter inside dispatch_async block.
func myFunc(#autoclosure condition: () -> Bool) {
dispatch_async(dispatch_get_main_queue()) {
if condition() {
println("Condition is true")
}
}
}
I get the following error.
Closure use of #noescape parameter may allow it to escape.
Is it possible to call #autoclosure parameter asynchronously?
Tested in Xcode 6.4 (6E23).
Yes, so long as you declare them #autoclosure(escaping):
Declarations with the autoclosure attribute imply noescape as well, except when passed the optional attribute escaping.
So this should do it:
func myFunc(#autoclosure(escaping) condition: () -> Bool) {
dispatch_async(dispatch_get_main_queue()) {
if condition() {
println("Condition is true")
}
}
}
Updating the answer from Airspeed Velocity, you can pass weak self directly into escaping autoclosure.
var boolValue: Bool = true
func myFunc(condition: #autoclosure #escaping () -> Bool?) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if let condition = condition() {
print("Condition is \(condition ? "true" : "false")")
}
}
}
func main() {
weak var weakSelf = self
myFunc(condition: weakSelf?.boolValue)
}