Работа с C API от Swift

Я пытаюсь отслеживать состояние сети. Я прошел через код FXReachability. Конкретно следующий метод.

- (void)setHost:(NSString *)host
{
    if (host != _host)
    {
        if (_reachability)
        {
            SCNetworkReachabilityUnscheduleFromRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
            CFRelease(_reachability);
        }
        _host = [host copy];
        _status = FXReachabilityStatusUnknown;
        _reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [_host UTF8String]);
        SCNetworkReachabilityContext context = { 0, ( __bridge void *)self, NULL, NULL, NULL };
        SCNetworkReachabilitySetCallback(_reachability, ONEReachabilityCallback, &context);
        SCNetworkReachabilityScheduleWithRunLoop(_reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
    }
}

Что он делает, так это продолжает проверять соединение с указанным хостом. Я пытаюсь преобразовать этот метод в Swift, и у меня возникла пара проблем.

1. Преобразование строки хоста в UTF8.

Я должен преобразовать строку хоста в UTF8 и передать ее в метод SCNetworkReachabilityCreateWithName. Я не могу найти точный эквивалент Swift UTF8String недвижимость в Objective-C. Но есть свойство под названием utf8 для String тип. Согласно документации,

Вы можете получить доступ к UTF-8 представлению String, выполнив итерацию по его свойству utf8. Это свойство имеет тип String.UTF8View, который представляет собой набор 8-битных значений без знака (UInt8), по одному на каждый байт в представлении строки UTF-8:

Как получить полное представление строки в UTF8 вместо набора 8-битных значений без знака (UInt8)?


2. Функция обратного вызова для SCNetworkReachabilitySetCallback.

Второй параметр этой функции ожидает что-то типа SCNetworkReachabilityCallBack, Я нырнул в исходный файл и обнаружил, что это на самом деле typealias,

typealias SCNetworkReachabilityCallBack = CFunctionPointer<((SCNetworkReachability!, SCNetworkReachabilityFlags, UnsafeMutablePointer<Void>) -> Void)>

Я определил это так

let reachabilityCallBack: SCNetworkReachabilityCallBack = {

}()

Но я получаю ошибку Пропущенный возврат в замыкании, который, как ожидается, вернет SCNetworkReachabilityCallBack, когда в этом typealias вы можете четко видеть, что он возвращает Void,

Это неправильный способ сделать это?


Это проблемы, с которыми я сталкиваюсь. Я занимаюсь этим часами без удачи, поэтому я очень ценю любую помощь.

Спасибо.

1 ответ

Решение

Ваша первая проблема может быть решена с

let reachability = host.withCString {
    SCNetworkReachabilityCreateWithName(nil, $0).takeRetainedValue()
}

Внутри закрытия, $0 является указателем на представление строки в UTF-8 с завершением NUL

Обновление: как сказал Nate Cook в удаленном ответе, а также здесь, вы можете фактически передать строку Swift функции, принимающей UnsafePointer<UInt8> непосредственно:

let reachability = SCNetworkReachabilityCreateWithName(nil, host).takeRetainedValue()

К сожалению, (насколько я знаю) в настоящее время нет решения вашей второй проблемы. SCNetworkReachabilitySetCallback ожидает указатель на функцию C в качестве второго параметра, и в настоящее время нет способа передать функцию Swift или замыкание. Обратите внимание, что документация для SCNetworkReachabilityCallBack показывает только Objective-C, но не Swift.

См. Также Swift не работает с указателями функций?,


Обновление для Swift 2. Теперь возможно передавать замыкание Swift в функцию C, принимающую параметр указателя функции. Также SCNetworkReachabilityCreateWithName() больше не возвращает неуправляемый объект:

let host = "google.com"
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
let reachability = SCNetworkReachabilityCreateWithName(nil, host)!

SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
    print(flags)
}, &context) 

SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes)

Обновление для Swift 3 (Xcode 8):

let host = "google.com"
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
let reachability = SCNetworkReachabilityCreateWithName(nil, host)!

SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
    print(flags)
    }, &context)

SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(),
                                         CFRunLoopMode.commonModes.rawValue)
Другие вопросы по тегам