NetworkExtension5-App和Extension通
2020-08-31 本文已影响0人
梦即是幻
环境
- Xcode 11.6
- iOS 13
- MacOS 10.15
导航
完整代码在此,熟悉的小伙伴可以直接试试。
共享数据方案
通过之前文章可以看到,发送和接收的数据都是在Tunnel进程的,而我们需要在主App进程展示发送和接收的数据,怎么拿到呢?
通过一番Google,推荐使用AppGroup,有下面两种方式:
-
通过NSUserDefaults
-
通过一个扩展与App都可以访问的共享容器,来存放文件,数据
其实就是一种。。
也可以使用类似MMWormhole等框架,使用起来很方便。
这里我们使用NSUserDefaults方式,也非常简单;但还需要一种垮进程的通知机制,最后选定Darwin Notification。
封装了一下,放到YYVPNLib中,功能如下:
#import <Foundation/Foundation.h>
typedef void(^YYDarwinNotificationManagerHandler)(void);
/// 如果是Darwin notification center, 无法传递参数
@interface YYDarwinNotificationManager : NSObject
+ (_Nonnull instancetype)sharedInstance;
- (void)registerNotificationForName:(NSString *_Nonnull)name callback:(YYDarwinNotificationManagerHandler _Nullable)callback;
- (void)postNotificationForName:(NSString *_Nonnull)name;
@end
这里有个坑,最开始想用这个通知直接搞定参数传递的,但是发现怎么也不行,最后看到Api有对应说明,对于object,userInfo,deliverImmediately这些参数:
If center is a Darwin notification center, this value is ignored.
实现代码
确定了方案,可以开始撸了,先添加一些扩展:
public extension UserDefaults {
var readPackets: String? {
get {
string(forKey: #function)
}
set {
set(newValue, forKey: #function)
}
}
var receivePackets: String? {
get {
string(forKey: #function)
}
set {
set(newValue, forKey: #function)
}
}
}
public extension YYVPNManager {
static let didChangeStatusNotification = "YYVPNManager.didChangeStatusNotification"
static let didReadPacketsNotification = "YYVPNManager.didReadPacketsNotification"
static let didReceivePacketsNotification = "YYVPNManager.didReceivePacketsNotification"
}
扩展中的实现
PacketTunnelProvider中添加一个属性:
private lazy var dataStorage = UserDefaults(suiteName: YYVPNManager.groupID)!
然后在读取到流量和收到服务器响应的地方设置数据,发送通知:
func remotePacketsToLocal() {
udpSession.setReadHandler({ [weak self] packets, _ in
if let packets = packets {
packets.forEach {
self?.dataStorage.receivePackets = ($0 as NSData).description
YYDarwinNotificationManager.sharedInstance().postNotification(forName: YYVPNManager.didReceivePacketsNotification)
self?.packetFlow.writePackets([$0], withProtocols: [AF_INET as NSNumber])
}
}
}, maxDatagrams: .max)
}
func localPacketsToServer() {
os_log(.default, log: .default, "LocalPacketsToServer")
packetFlow.readPackets { [weak self] packets, _ in
os_log(.default, log: .default, "readPackets")
packets.forEach {
self?.dataStorage.readPackets = ($0 as NSData).description
YYDarwinNotificationManager.sharedInstance().postNotification(forName: YYVPNManager.didReadPacketsNotification)
self?.udpSession.writeDatagram($0) { error in
if let error = error {
os_log(.default, log: .default, "udpSession.writeDatagram error: %{public}@", "\(error)")
}
}
}
self?.localPacketsToServer()
}
}
主App中的实现
包含2个List,监听didReadPacketsNotification和didReceivePacketsNotification,添加数据并展示。
界面如下:
image直接上代码:
import SwiftUI
import YYVPNLib
struct PacketView: View {
@State var sendPackets: [YYListView.Model] = []
@State var receviedPackets: [YYListView.Model] = []
private let dataStorage = UserDefaults(suiteName: YYVPNManager.groupID)!
var body: some View {
VStack {
Group {
HStack {
Text("发送的包")
Button(action: cleanSendPackets) {
Text("clean")
}
}.fixedSize()
YYListView(items: $sendPackets)
}
Group {
HStack {
Text("接收的包")
Button(action: cleanReceviedPackets) {
Text("clean")
}
}.fixedSize()
YYListView(items: $receviedPackets)
}
}.onAppear(perform: starListening)
}
private func cleanSendPackets() {
sendPackets.removeAll()
}
private func cleanReceviedPackets() {
receviedPackets.removeAll()
}
private func starListening() {
YYDarwinNotificationManager.sharedInstance().registerNotification(forName: YYVPNManager.didReadPacketsNotification) {
if let data = self.dataStorage.readPackets {
DispatchQueue.main.async {
self.sendPackets.append(.init(text: data))
}
}
}
YYDarwinNotificationManager.sharedInstance().registerNotification(forName: YYVPNManager.didReceivePacketsNotification) {
if let data = self.dataStorage.receivePackets {
DispatchQueue.main.async {
self.receviedPackets.append(.init(text: data))
}
}
}
}
}
最近再添加一个入口,可以在ConfigView里面添加一个判断,如果VPN连上了,就展示跳转入口:
Form {
...
if viewModel.status == .on {
Section {
NavigationLink(destination: PacketView()) {
Text("Show packets View")
}
}
}
...
}
搞定~ ~