Swift, как отправить электронную почту в AppDelegate fetch?

Я совершенно беспомощен прямо сейчас, я пытаюсь отправить электронное письмо для загрузки данных в приложении iOS Swift 4. Я нашел библиотеку Obj-C Mailcore, которая работает в обычном коде, например, в обратном вызове нажатия кнопки. Но если я пытаюсь использовать тот же код в моей функции AppDelegate fetch, он не возвращается из завершенияHandler.

Операция отправки Mailcore является асинхронной, поэтому я использовал оболочку семафора вокруг нее, но обработчик завершения никогда не вызывается.

Изменить:

Видимо не работает код в кнопке с Семафором. Только если я удалю

let _ = semaphore.wait(timeout: DispatchTime.distantFuture)

часть.

Есть ли другой способ дождаться завершения асинхронного завершения блока?


Да, посмотри на мой ответ :-)

Мой код:

let smtpSession = MCOSMTPSession()
[...]
let sendOperation = smtpSession.sendOperation(with: rfc822Data!)
[...]
let semaphore = DispatchSemaphore(value: 0)
if (sendOperation != nil) {
    print("Starting sendOperation...")
    sendOperation?.start { (error) -> Void in
        if (error != nil) {
            sendingError = true
            print("Error sending email: \(error)")
        } else {
            print("Successfully sent email!")
        }
        sendingDone = true
        semaphore.signal()
    }
    let _ = semaphore.wait(timeout: DispatchTime.distantFuture)
} else {
    print("Cant init sendOperation")
    sendingError = true
}

person skilled-solutions    schedule 23.11.2018    source источник
comment
Вы уверены, что sendOperation не равен нулю при вызове start? Нет ничего особенного в объекте, который принимает UIApplicationDelegate   -  person Warren Burton    schedule 23.11.2018
comment
@Warren Burton Я не уверен, но я просто скопировал и вставил его из кода кнопки, где он работал, поэтому меня это не беспокоило. Я проверю это, но не произойдет ли сбой, если я вызову start () для объекта nil?   -  person skilled-solutions    schedule 23.11.2018
comment
sendOperation выглядит Optional из вашего кода. Вы можете видеть это из sendOperation?, используя необязательную цепочку.   -  person Warren Burton    schedule 23.11.2018
comment
@ Уоррен Бертон. Теперь я понимаю, что вы имеете в виду. Я проверю, равно ли оно нулю.   -  person skilled-solutions    schedule 23.11.2018
comment
@WarrenBurton Это не ноль, я добавил проверку и запуск выполняется.   -  person skilled-solutions    schedule 23.11.2018


Ответы (2)


Завершение происходит либо в том же потоке, либо что-то запускает в этом потоке.

Обычно нужно не ждать, а вместо этого делать все, что вам нужно, после завершения самого завершения.

Если вы должны сделать это с семафором, переместите вызов start в другой поток.

person Lou Franco    schedule 23.11.2018

Итак, я нашел решение:

SyncManager.swift

import Foundation

/// A generic Cocoa-Style completion handler
typealias CompletionHandler = (Error?) -> Void

/// Provides syncronous access to results returned by
/// asynchronous processes with completion handlers
class SyncMaker {
    var result: Error? = nil

    /// Generates a synchronous-compliant completion handler
    func completion() -> CompletionHandler{
        return {
            (error: Error?) in

            // Store result, return control
            self.result = error
            CFRunLoopStop(CFRunLoopGetCurrent())
        }
    }

    // Perform task (that must use custom completion handler) and wait
    func run(_ task: @escaping () -> Void) -> Error? {
        task()
        CFRunLoopRun()
        return result
    }
}

AppDelegate.swift (частично)

[...]
let syncMaker = SyncMaker()
let result = syncMaker.run {
    sendOperation?.start(
        syncMaker.completion())
}
if (result != nil) {
    print("Error sending email: \(result)")
} else {
    sendingError = false
    print("Successfully sent email!")
}
[...]
person skilled-solutions    schedule 25.11.2018