Работа с 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. Но для типа String существует свойство, называемое utf8. Согласно documentation,

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

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


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

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

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

Я определил его так.

let reachabilityCallBack: SCNetworkReachabilityCallBack = {

}()

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

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


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

Спасибо.

Ответ 1

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

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

Внутри замыкания $0 является указателем на UUL-8-представление NUL-конца Строка.

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

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

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

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


Обновление для Swift 2: Теперь можно выполнить быстрое закрытие к функции 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)