Skip to content

Commit 401ab79

Browse files
committed
Harden paired wireless inspection
1 parent 3e10f7b commit 401ab79

13 files changed

Lines changed: 201 additions & 120 deletions

LookinClient/Connection/LKAppsManager.m

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ - (instancetype)init {
4343
_didAutoReconnectSucc = [RACSubject subject];
4444

4545
@weakify(self);
46-
[[[[LKConnectionManager sharedInstance].channelWillEnd filter:^BOOL(Lookin_PTChannel *channel) {
46+
[[[[LKConnectionManager sharedInstance].channelWillEnd filter:^BOOL(id<LookinChannelProtocol> channel) {
4747
@strongify(self);
4848
return channel == self.inspectingApp.channel;
4949

50-
}] flattenMap:^__kindof RACSignal * _Nullable(Lookin_PTChannel *channel) {
50+
}] flattenMap:^__kindof RACSignal * _Nullable(id<LookinChannelProtocol> channel) {
5151
@strongify(self);
5252

5353
NSLog(@"current connection end");
@@ -120,13 +120,13 @@ - (RACSignal *)fetchAppInfosWithImage:(BOOL)needImages localInfos:(NSArray<Looki
120120
}] ? : @[];
121121
NSDictionary *params = @{@"needImages":@(needImages), @"local":localInfoIdentifiers};
122122

123-
return [[[[LKConnectionManager sharedInstance] tryToConnectAllPorts] flattenMap:^__kindof RACSignal * _Nullable(NSArray<Lookin_PTChannel *> *connectedChannels) {
123+
return [[[[LKConnectionManager sharedInstance] tryToConnectAllPorts] flattenMap:^__kindof RACSignal * _Nullable(NSArray<id<LookinChannelProtocol>> *connectedChannels) {
124124
if (!connectedChannels.count) {
125125
// 没有任何 channel
126126
return [RACSignal return:nil];
127127
}
128128

129-
NSArray<RACSignal *> *signals = [connectedChannels lookin_map:^id(NSUInteger idx, Lookin_PTChannel *channel) {
129+
NSArray<RACSignal *> *signals = [connectedChannels lookin_map:^id(NSUInteger idx, id<LookinChannelProtocol> channel) {
130130
return [[[LKConnectionManager sharedInstance] requestWithType:LookinRequestTypeApp data:params channel:channel] catch:^RACSignal * _Nonnull(NSError * _Nonnull error) {
131131
if (error.code == LookinErrCode_ServerVersionTooHigh || error.code == LookinErrCode_ServerVersionTooLow) {
132132
// 这些 Lookin 版本不匹配的错误应该被保留,因为业务需要显示这些错误
@@ -154,7 +154,7 @@ - (RACSignal *)fetchAppInfosWithImage:(BOOL)needImages localInfos:(NSArray<Looki
154154
}
155155

156156
if ([value isKindOfClass:[RACTuple class]]) {
157-
RACTupleUnpack(LookinConnectionResponseAttachment *response, Lookin_PTChannel *relatedChannel) = value;
157+
RACTupleUnpack(LookinConnectionResponseAttachment *response, id<LookinChannelProtocol> relatedChannel) = value;
158158
if (response.error) {
159159
NSAssert(NO, @"");
160160
return nil;

LookinClient/Connection/LKConnectionManager.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838
/// 该方法不会 sendError
3939
- (RACSignal *)tryToConnectAllPorts;
4040

41-
/// 返回的 data 为 RACTuple<LookinConnectionResponseAttachment *, Lookin_PTChannel *>
41+
/// 返回的 data 为 RACTuple<LookinConnectionResponseAttachment *, id<LookinChannelProtocol>>
4242
/// 在调用该方法发请求时,如果已有相同 type 的旧 request 尚未返回结果,则之前的旧 request 会被报告 Error,然后被丢弃
43-
- (RACSignal *)requestWithType:(unsigned int)requestType data:(NSObject *)requestData channel:(Lookin_PTChannel *)channel;
43+
- (RACSignal *)requestWithType:(unsigned int)requestType data:(NSObject *)requestData channel:(id<LookinChannelProtocol>)channel;
4444

4545
- (RACSignal<ECOChannelDeviceInfo *> *)connectToWireless:(ECOChannelDeviceInfo *)device;
4646

@@ -51,11 +51,11 @@
5151
- (void)setWhiteListDevice:(ECOChannelDeviceInfo *)device white:(BOOL)white;
5252

5353
/// 取消先前使用 requestWithType:data:channel: 方法发送的尚未完成的 request,这个 request 会被报告为 completion
54-
- (void)cancelRequestWithType:(unsigned int)requestType channel:(Lookin_PTChannel *)channel;
54+
- (void)cancelRequestWithType:(unsigned int)requestType channel:(id<LookinChannelProtocol>)channel;
5555

5656
/// 如果发送的消息不需要 server 端回复,则请使用该方法而非 requestWithType:
5757
/// 如果此时 server 端不在前台或处于断点等模式,则 server 端可能无法收到该消息
58-
- (void)pushWithType:(unsigned int)pushType data:(NSObject *)requestData channel:(Lookin_PTChannel *)channel;
58+
- (void)pushWithType:(unsigned int)pushType data:(NSObject *)requestData channel:(id<LookinChannelProtocol>)channel;
5959

6060
/// 即将关闭某个 channel,一般是因为 server 端断开(比如 iOS app 被 kill 掉或 USB 被拔掉)
6161
@property(nonatomic, strong, readonly) RACSubject *channelWillEnd;

LookinClient/Connection/LKConnectionManager.m

Lines changed: 131 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#import "LKServerVersionRequestor.h"
1717
#import "ECOChannelManager.h"
1818

19-
static NSIndexSet * PushFrameTypeList() {
19+
static NSIndexSet *PushFrameTypeList(void) {
2020
static NSIndexSet *list;
2121
static dispatch_once_t onceToken;
2222
dispatch_once(&onceToken, ^{
@@ -27,6 +27,14 @@
2727
}
2828

2929
static NSString *const LKWhiteListDevicesKey = @"LKWhiteListDevicesKey";
30+
static NSTimeInterval const LKWirelessAuthorizationTimeout = 30;
31+
32+
static NSString *LKWirelessDeviceIdentifier(ECOChannelDeviceInfo *device) {
33+
if (!device.uuid.length || !device.appInfo.appId.length) {
34+
return nil;
35+
}
36+
return [NSString stringWithFormat:@"%@_%@", device.uuid, device.appInfo.appId];
37+
}
3038

3139
@implementation Lookin_PTChannel (LKConnection)
3240

@@ -93,7 +101,7 @@ @interface LKConnectionManager () <Lookin_PTChannelDelegate>
93101
@property(nonatomic, strong) NSMutableArray<ECOChannelDeviceInfo *> *connectWirelessDevices;
94102
@property(nonatomic, strong) NSMutableArray<ECOChannelDeviceInfo *> *notConnectWirelessDevices;
95103
@property(nonatomic, strong) ECOChannelManager *wirelessChannel;
96-
@property(nonatomic, strong) NSMutableDictionary *authStateChangedBlocks;
104+
@property(nonatomic, strong) NSMutableDictionary<NSString *, ECOChannelAuthStateChangedBlock> *authStateChangedBlocks;
97105

98106
@property(nonatomic, strong) NSMutableArray<ECOChannelDeviceInfo *> *whitelistDevices;
99107

@@ -173,9 +181,12 @@ - (RACSignal *)tryToConnectAllPorts {
173181
return [[RACSignal zip:@[[self _tryToConnectAllSimulatorPorts],
174182
[self _tryToConnectAllUSBDevices],
175183
[self _tryToConnectToAllWirelessDevice]]] map:^id _Nullable(RACTuple * _Nullable value) {
176-
RACTupleUnpack(NSArray<Lookin_PTChannel *> *simulatorChannels, NSArray<Lookin_PTChannel *> *usbChannels, NSArray<ECOChannelDeviceInfo *> *wirelessDevices) = value;
177-
NSArray *connectedChannels = [[simulatorChannels arrayByAddingObjectsFromArray:usbChannels] arrayByAddingObjectsFromArray:wirelessDevices];
178-
return connectedChannels;
184+
RACTupleUnpack(NSArray<Lookin_PTChannel *> *simulatorChannels, NSArray<Lookin_PTChannel *> *usbChannels, NSArray<ECOChannelDeviceInfo *> *wirelessDevices) = value;
185+
NSMutableArray<id<LookinChannelProtocol>> *connectedChannels = [NSMutableArray array];
186+
[connectedChannels addObjectsFromArray:simulatorChannels];
187+
[connectedChannels addObjectsFromArray:usbChannels];
188+
[connectedChannels addObjectsFromArray:wirelessDevices];
189+
return connectedChannels.copy;
179190
}];
180191
}
181192

@@ -286,20 +297,25 @@ - (RACSignal *)_tryToConnectToAllWirelessDevice {
286297

287298
#pragma mark - Request
288299

289-
- (void)pushWithType:(unsigned int)pushType data:(NSObject *)data channel:(Lookin_PTChannel *)channel {
300+
- (void)pushWithType:(unsigned int)pushType data:(NSObject *)data channel:(id<LookinChannelProtocol>)channel {
290301
if (!channel || !channel.isConnected) {
291302
return;
292303
}
293304
NSError *archiveError = nil;
294-
dispatch_data_t payload = [[NSKeyedArchiver archivedDataWithRootObject:data requiringSecureCoding:YES error:&archiveError] createReferencingDispatchData];
305+
NSData *sendData = [NSKeyedArchiver archivedDataWithRootObject:data requiringSecureCoding:YES error:&archiveError];
295306
if (archiveError) {
296307
NSAssert(NO, @"");
297308
}
298309
NSLog(@"LookinClient - pushData, type:%@", @(pushType));
299-
[channel sendFrameOfType:pushType tag:0 withPayload:payload callback:nil];
310+
if ([channel isKindOfClass:ECOChannelDeviceInfo.class]) {
311+
[self.wirelessChannel sendPacket:sendData extraInfo:@{@"tag": @0, @"type": @(pushType)} toDevice:(ECOChannelDeviceInfo *)channel];
312+
} else if ([channel isKindOfClass:Lookin_PTChannel.class]) {
313+
dispatch_data_t payload = [sendData createReferencingDispatchData];
314+
[(Lookin_PTChannel *)channel sendFrameOfType:pushType tag:0 withPayload:payload callback:nil];
315+
}
300316
}
301317

302-
- (RACSignal *)requestWithType:(unsigned int)requestType data:(NSObject *)requestData channel:(Lookin_PTChannel *)channel {
318+
- (RACSignal *)requestWithType:(unsigned int)requestType data:(NSObject *)requestData channel:(id<LookinChannelProtocol>)channel {
303319
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
304320
// NSLog(@"LookinClient, level1 - will ping for request:%@, port:%@", @(type), @(channel.portNumber));
305321
NSTimeInterval timeoutInterval;
@@ -457,7 +473,7 @@ - (void)_requestWithType:(unsigned int)requestType channel:(id<LookinChannelProt
457473
}
458474
}
459475

460-
- (void)cancelRequestWithType:(unsigned int)requestType channel:(Lookin_PTChannel *)channel {
476+
- (void)cancelRequestWithType:(unsigned int)requestType channel:(id<LookinChannelProtocol>)channel {
461477
LKConnectionRequest *activeRequest = [channel.activeRequests lookin_firstFiltered:^BOOL(LKConnectionRequest *obj) {
462478
return obj.type == requestType;
463479
}];
@@ -506,91 +522,133 @@ - (void)_startListeningForWirelessDevices {
506522
@weakify(self);
507523
// 接收到数据回调
508524
self.wirelessChannel.receivedBlock = ^(ECOChannelDeviceInfo *device, NSData *data, NSDictionary *extraInfo) {
509-
NSLog(@"🚀 Lookin receivedBlock device:%@", device);
510-
NSNumber *tag = extraInfo[@"tag"];
511-
NSNumber *type = extraInfo[@"type"];
512-
LKConnectionRequest *activeRequest = [device.activeRequests lookin_firstFiltered:^BOOL(LKConnectionRequest *obj) {
513-
return [@(obj.type) isEqualToNumber:type] && [@(obj.tag) isEqualToNumber:tag];
514-
}];
515-
if (!activeRequest) {
516-
// 也许在 shouldAcceptFrameOfType 和 didReceiveFrame 两个时机之间,该 request 因为超时而被销毁了?有点玄学但确实偶尔会走到这里。
517-
return;
518-
}
519-
[self_weak_ _didReceiveDataWithChannel:device data:data activeRequest:activeRequest];
525+
dispatch_async(dispatch_get_main_queue(), ^{
526+
@strongify(self);
527+
NSNumber *tag = [extraInfo[@"tag"] isKindOfClass:NSNumber.class] ? extraInfo[@"tag"] : nil;
528+
NSNumber *type = [extraInfo[@"type"] isKindOfClass:NSNumber.class] ? extraInfo[@"type"] : nil;
529+
if (!tag || !type) {
530+
return;
531+
}
532+
LKConnectionRequest *activeRequest = [device.activeRequests lookin_firstFiltered:^BOOL(LKConnectionRequest *obj) {
533+
return [@(obj.type) isEqualToNumber:type] && [@(obj.tag) isEqualToNumber:tag];
534+
}];
535+
if (!activeRequest) {
536+
// 请求可能在数据到达前已超时。
537+
return;
538+
}
539+
[self _didReceiveDataWithChannel:device data:data activeRequest:activeRequest];
540+
});
520541
};
521542
// 设备连接变更
522543
self.wirelessChannel.deviceBlock = ^(ECOChannelDeviceInfo *device, BOOL isConnected) {
523-
NSLog(@"🚀 Lookin deviceBlock device:%@", device);
524-
if (isConnected && ![self_weak_.connectWirelessDevices containsObject:device]) {
525-
if (!device.authorizedType && ![self_weak_ isWhiteListDevice:device]) {
526-
if (![self_weak_.notConnectWirelessDevices containsObject:device]) {
527-
[self_weak_.notConnectWirelessDevices addObject:device];
544+
dispatch_async(dispatch_get_main_queue(), ^{
545+
@strongify(self);
546+
if (isConnected && ![self.connectWirelessDevices containsObject:device]) {
547+
if (!device.authorizedType && ![self isWhiteListDevice:device]) {
548+
if (![self.notConnectWirelessDevices containsObject:device]) {
549+
[self.notConnectWirelessDevices addObject:device];
550+
}
551+
} else {
552+
[[self _connectToWirelessDevice:device] subscribeNext:^(__unused ECOChannelDeviceInfo *connectedDevice) {
553+
} error:^(__unused NSError *error) {
554+
}];
528555
}
529-
} else {
530-
RACSignal *signal = [self_weak_ _connectToWirelessDevice:device];
531-
[signal subscribeNext:^(ECOChannelDeviceInfo * _Nullable x) {
532-
NSLog(@"%@", x);
533-
}];
556+
} else if (!isConnected) {
557+
[self.notConnectWirelessDevices removeObject:device];
558+
[self.connectWirelessDevices removeObject:device];
559+
[self.channelWillEnd sendNext:device];
534560
}
535-
} else if (!isConnected) {
536-
[self_weak_.notConnectWirelessDevices removeObject:device];
537-
[self_weak_.connectWirelessDevices removeObject:device];
538-
[self_weak_.channelWillEnd sendNext:device];
539-
}
561+
});
540562
};
541563
// 授权状态变更回调
542564
self.wirelessChannel.authStateChangedBlock = ^(ECOChannelDeviceInfo *device, ECOAuthorizeResponseType authState) {
543-
NSLog(@"🚀 Lookin authStateChangedBlock device:%@", device);
544-
void(^block)(ECOChannelDeviceInfo *device, ECOAuthorizeResponseType authState) = self_weak_.authStateChangedBlocks[@(device.hash)];
545-
if (block) {
546-
block(device, authState);
547-
} else if (authState && [self_weak_ isWhiteListDevice:device] && ![self_weak_.connectWirelessDevices containsObject:device]) {
548-
[[self_weak_ _tryToConnectToWirelessDevice:device] subscribeNext:^(ECOChannelDeviceInfo * _Nullable x) {
549-
NSLog(@"🚀 Lookin auto connect white list device success. device:%@", x);
550-
}];
551-
}
552-
if (!authState && [self_weak_.connectWirelessDevices containsObject:device]) {
553-
[self_weak_.connectWirelessDevices removeObject:device];
554-
[self_weak_.channelWillEnd sendNext:device];
555-
}
556-
};
557-
// 请求授权状态认证回调
558-
self.wirelessChannel.requestAuthBlock = ^(ECOChannelDeviceInfo *device, ECOAuthorizeResponseType authState) {
559-
NSLog(@"🚀 Lookin requestAuthBlock device:%@ authState:%ld", device, authState);
565+
dispatch_async(dispatch_get_main_queue(), ^{
566+
@strongify(self);
567+
NSString *identifier = LKWirelessDeviceIdentifier(device);
568+
ECOChannelAuthStateChangedBlock block = identifier.length ? self.authStateChangedBlocks[identifier] : nil;
569+
if (block) {
570+
block(device, authState);
571+
} else if (authState && [self isWhiteListDevice:device] && ![self.connectWirelessDevices containsObject:device]) {
572+
[[self _tryToConnectToWirelessDevice:device] subscribeNext:^(__unused ECOChannelDeviceInfo *connectedDevice) {
573+
} error:^(__unused NSError *error) {
574+
}];
575+
}
576+
if (!authState && [self.connectWirelessDevices containsObject:device]) {
577+
[self.connectWirelessDevices removeObject:device];
578+
[self.channelWillEnd sendNext:device];
579+
}
580+
});
560581
};
561582
}
562583

563584
- (RACSignal<ECOChannelDeviceInfo *> *)_tryToConnectToWirelessDevice:(ECOChannelDeviceInfo *)device {
564-
if (!device.isConnected)
565-
return [RACSignal error:LookinErr_Inner];
585+
if (!device.isConnected) {
586+
NSError *error = [NSError errorWithDomain:LookinErrorDomain code:LookinErrCode_Inner userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Wireless device disconnected.", nil)}];
587+
return [RACSignal error:error];
588+
}
589+
590+
NSString *identifier = LKWirelessDeviceIdentifier(device);
591+
if (!identifier.length) {
592+
NSError *error = [NSError errorWithDomain:LookinErrorDomain code:LookinErrCode_Inner userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Wireless device identity is invalid.", nil)}];
593+
return [RACSignal error:error];
594+
}
566595

567596
if (!device.authorizedType) {
568597
// 未信任
569598
@weakify(self);
570-
RACSignal *signal = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
571-
self_weak_.authStateChangedBlocks[@(device.hash)] = ^(ECOChannelDeviceInfo *device, ECOAuthorizeResponseType authState) {
599+
RACSignal *authorizationSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
600+
@strongify(self);
601+
if (self.authStateChangedBlocks[identifier]) {
602+
NSError *error = [NSError errorWithDomain:LookinErrorDomain code:LookinErrCode_Inner userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"A wireless connection request is already in progress.", nil)}];
603+
[subscriber sendError:error];
604+
return nil;
605+
}
606+
607+
__weak ECOChannelAuthStateChangedBlock weakCallback = nil;
608+
ECOChannelAuthStateChangedBlock callback = nil;
609+
callback = [^(ECOChannelDeviceInfo *authorizedDevice, ECOAuthorizeResponseType authState) {
610+
ECOChannelAuthStateChangedBlock currentCallback = self.authStateChangedBlocks[identifier];
611+
if (currentCallback != weakCallback) {
612+
return;
613+
}
614+
[self.authStateChangedBlocks removeObjectForKey:identifier];
572615
if (authState) {
573-
[subscriber sendNext:device];
616+
[subscriber sendNext:authorizedDevice];
574617
[subscriber sendCompleted];
575-
NSLog(@"🚀 Lookin connect device success. device:%@", device);
576618
} else {
577-
NSError *error = [NSError errorWithDomain:LookinErrorDomain code:LookinErrCode_Inner userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Wireless Connection rejected", nil)}];
578-
NSLog(@"🚀 Lookin connect reject. device:%@", device);
579-
dispatch_async(dispatch_get_main_queue(), ^{
580-
AlertErrorText(NSLocalizedString(@"Wireless Connections", nil), NSLocalizedString(@"Wireless Connection rejected", nil), CurrentKeyWindow);
581-
});
619+
NSError *error = [NSError errorWithDomain:LookinErrorDomain code:LookinErrCode_Inner userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Wireless Connection rejected", nil)}];
582620
[subscriber sendError:error];
583621
}
584-
};
585-
return nil;
586-
}] flattenMap:^__kindof RACSignal * _Nullable(ECOChannelDeviceInfo *device) {
587-
return [self_weak_ _connectToWirelessDevice:device];
622+
} copy];
623+
weakCallback = callback;
624+
self.authStateChangedBlocks[identifier] = callback;
625+
626+
BOOL showAuthorizationAlert = ![self.wirelessChannel.whitelistDevices containsObject:identifier];
627+
[self.wirelessChannel sendAuthorizationMessageToDevice:device
628+
state:ECOAuthorizeResponseType_AllowAlways
629+
showAuthAlert:showAuthorizationAlert];
630+
631+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(LKWirelessAuthorizationTimeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
632+
ECOChannelAuthStateChangedBlock currentCallback = self.authStateChangedBlocks[identifier];
633+
if (currentCallback != callback) {
634+
return;
635+
}
636+
[self.authStateChangedBlocks removeObjectForKey:identifier];
637+
NSError *error = [NSError errorWithDomain:LookinErrorDomain code:LookinErrCode_Inner userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Wireless connection timed out.", nil)}];
638+
[subscriber sendError:error];
639+
});
640+
641+
return [RACDisposable disposableWithBlock:^{
642+
dispatch_async(dispatch_get_main_queue(), ^{
643+
if (self.authStateChangedBlocks[identifier] == callback) {
644+
[self.authStateChangedBlocks removeObjectForKey:identifier];
645+
}
646+
});
647+
}];
648+
}];
649+
return [authorizationSignal flattenMap:^__kindof RACSignal * _Nullable(ECOChannelDeviceInfo *authorizedDevice) {
650+
return [self_weak_ _connectToWirelessDevice:authorizedDevice];
588651
}];
589-
NSString *uniId = [NSString stringWithFormat:@"%@_%@",device.uuid, device.appInfo.appId];
590-
[self.wirelessChannel sendAuthorizationMessageToDevice:device
591-
state:ECOAuthorizeResponseType_AllowAlways
592-
showAuthAlert:![self.wirelessChannel.whitelistDevices containsObject:uniId]];
593-
return signal;
594652
} else {
595653
return [self _connectToWirelessDevice:device];
596654
}

LookinClient/Connection/LKInspectableApp.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@
1212
#import "LookinCustomAttrModification.h"
1313
#import "LookinAttributesGroup.h"
1414

15-
@class Lookin_PTChannel, LookinDisplayItemTrace, LookinInvocationRequest, LookinHierarchyInfo, LookinStaticAsyncUpdateTasksPackage, LookinStaticAsyncUpdateTask;
15+
@class LookinDisplayItemTrace, LookinInvocationRequest, LookinHierarchyInfo, LookinStaticAsyncUpdateTasksPackage, LookinStaticAsyncUpdateTask;
16+
@protocol LookinChannelProtocol;
1617

1718
@interface LKInspectableApp : NSObject
1819

1920
@property(nonatomic, strong) NSError *serverVersionError;
2021

2122
@property(nonatomic, strong) LookinAppInfo *appInfo;
2223

23-
@property(nonatomic, weak) Lookin_PTChannel *channel;
24+
@property(nonatomic, weak) id<LookinChannelProtocol> channel;
2425

2526
- (RACSignal *)fetchHierarchyData;
2627

0 commit comments

Comments
 (0)