Is there an @escaping analog for "in-closure closures"?

:wave:

I have a closure that accepts another closure:

var onSend: ((String, (Int) -> Void) -> Void)?

which is called like:

onSend?(messageText) { [weak self] messageID in
  self?.markAsSent(messageID)
}

and assigned as:

vc.onSend = { [unowned messageService] message, callback in
  messageService.send(message) { result in
    switch result {
    case let .success(messageID): callback(messageID)
    case let .failure(error): fatalError("failed to send a message with error: \(error)")
    }
  }
}

But the snippet above fails to compile with the error:

Closure use of non-escaping parameter 'callback' may allow it to escape

I wonder if there is a way to mark the callback in the previous snippet as @escaping?


In case it matters, messageService.send/2 is defined as follows:

func send(_ message: String,
          then callback: @escaping (Result<Int, MessageServiceError>) -> Void)

Or should I maybe try assigning vc.onSend to a function?

vc.onSend = self.onSend

where self.onSend is

private func onSend(message: String, @escaping callback: (Int) -> Void) {
  messageService.send(message) { result in
    switch result {
    case let .success(messageID): callback(messageID)
    case let .failure(error): fatalError("failed to send a message with error: \(error)")
    }
  }
}

But it forces me to assign messageService somewhere so that it could be accessed from func onSend ...

Yet another way is to remove the "inner closure" and just call a vc's function from within the "outer closure" ...

var onSend: ((String) -> Void)?
func onSent(_ messageID: Int)

// ...

onSend?(messageText)
vc.onSend = { [unowned messageService] message in
  messageService.send(message) { [weak vc] result in
    guard let vc = vc else { return }
    switch result {
    case let .success(messageID): vc.onSent(messageID)
    case let .failure(error): fatalError("failed to send a message with error: \(error)")
    }
  }
}

This works for me in an Xcode 10 playground:

import Foundation

var onSend: ((String, @escaping (Int) -> Void) -> Void)?
var dummy: ((Int) -> Void)?
let x = NSObject()

onSend = { [unowned x] string, fn in dummy = fn; print(x) }

onSend?("hello") { print($0) }
// Output: <NSObject: 0x7fccafe31760>

dummy?(7)
// Output: 7
1 Like