Работа с 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)