Skip to content

Commit

Permalink
DiscoveryManager: pause and resume discovery providers on app state c…
Browse files Browse the repository at this point in the history
…hange
  • Loading branch information
Eugene Nikolskyi committed Jun 22, 2015
1 parent c7ca5b8 commit 9732a8e
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 35 deletions.
129 changes: 129 additions & 0 deletions ConnectSDKTests/Discovery/DiscoveryManagerTests.m
@@ -0,0 +1,129 @@
//
// DiscoveryManagerTests.m
// ConnectSDK
//
// Created by Eugene Nikolskyi on 2015-06-19.
// Copyright (c) 2015 LG Electronics. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import "DiscoveryManager_Private.h"

#import "AppStateChangeNotifier.h"
#import "DiscoveryProvider.h"

#import "OCMArg+ArgumentCaptor.h"

@interface MockDeviceService : DeviceService @end
@implementation MockDeviceService

+ (NSDictionary *)discoveryParameters {
return @{@"serviceId": @"mockService"};
}

@end



@interface DiscoveryManagerTests : XCTestCase

@property (nonatomic, strong) DiscoveryManager *discoveryManager;
@property (nonatomic, strong) id /*AppStateChangeNotifier **/ stateNotifierMock;
@property (nonatomic, strong) id /*DiscoveryProvider **/ discoveryProviderMock;

@end

@implementation DiscoveryManagerTests

#pragma mark - Setup

- (void)setUp {
[super setUp];

self.stateNotifierMock = OCMClassMock([AppStateChangeNotifier class]);
self.discoveryProviderMock = OCMClassMock([DiscoveryProvider class]);
self.discoveryManager = [self createDiscoveryManager];
}

- (DiscoveryManager *)createDiscoveryManager {
DiscoveryManager *discoveryManager = [[DiscoveryManager alloc]
initWithAppStateChangeNotifier:self.stateNotifierMock];
[discoveryManager registerDeviceService:[MockDeviceService class]
withDiscoveryProviderFactory:^{
return self.discoveryProviderMock;
}];

return discoveryManager;
}

- (void)tearDown {
[self.discoveryManager stopDiscovery];
self.discoveryManager = nil;
self.stateNotifierMock = nil;

[super tearDown];
}

#pragma mark - UIApplication State Change Tests

- (void)testDefaultStateNotifierShouldBeCreated {
DiscoveryManager *discoveryManager = [DiscoveryManager new];
XCTAssertNotNil(discoveryManager.appStateChangeNotifier,
@"a real AppStateChangeNotifier should be created");
}

- (void)testStartDiscoveryShouldStartListeningStateNotifier {
OCMExpect([self.stateNotifierMock startListening]);
[self.discoveryManager startDiscovery];
OCMVerifyAll(self.stateNotifierMock);
}

- (void)testStopDiscoveryShouldStopListeningStateNotifier {
OCMExpect([self.stateNotifierMock stopListening]);
[self.discoveryManager startDiscovery];
[self.discoveryManager stopDiscovery];
OCMVerifyAll(self.stateNotifierMock);
}

- (void)testBackgroundingShouldPauseDiscoveryProviders {
AppStateChangeBlock backgroundStateBlock;
OCMExpect([self.stateNotifierMock setDidBackgroundBlock:
[OCMArg captureBlockTo:&backgroundStateBlock]]);

DiscoveryManager *discoveryManager = [self createDiscoveryManager];
[discoveryManager startDiscovery];

OCMExpect([self.discoveryProviderMock pauseDiscovery]);
backgroundStateBlock();
OCMVerifyAll(self.discoveryProviderMock);
}

- (void)testForegroundingShouldResumeDiscoveryProviders {
AppStateChangeBlock backgroundStateBlock;
AppStateChangeBlock foregroundStateBlock;
OCMExpect([self.stateNotifierMock setDidBackgroundBlock:
[OCMArg captureBlockTo:&backgroundStateBlock]]);
OCMExpect([self.stateNotifierMock setDidForegroundBlock:
[OCMArg captureBlockTo:&foregroundStateBlock]]);

DiscoveryManager *discoveryManager = [self createDiscoveryManager];
[discoveryManager startDiscovery];

backgroundStateBlock();
OCMExpect([self.discoveryProviderMock resumeDiscovery]);
foregroundStateBlock();
OCMVerifyAll(self.discoveryProviderMock);
}

@end
85 changes: 50 additions & 35 deletions Discovery/DiscoveryManager.m
Expand Up @@ -35,6 +35,8 @@
#import "ServiceConfigDelegate.h"
#import "CapabilityFilter.h"

#import "AppStateChangeNotifier.h"

#import <SystemConfiguration/CaptiveNetwork.h>

@interface DiscoveryManager() <DiscoveryProviderDelegate, ServiceConfigDelegate>
Expand Down Expand Up @@ -96,7 +98,13 @@ - (void) setDeviceStore:(id <ConnectableDeviceStore>)deviceStore
_useDeviceStore = (_deviceStore != nil);
}

- (instancetype) init
- (instancetype)init {
return [self initWithAppStateChangeNotifier:nil];
}

#pragma mark - Private Init

- (instancetype) initWithAppStateChangeNotifier:(nullable AppStateChangeNotifier *)stateNotifier
{
self = [super init];

Expand All @@ -112,6 +120,17 @@ - (instancetype) init
_allDevices = [[NSMutableDictionary alloc] init];
_compatibleDevices = [[NSMutableDictionary alloc] init];

_appStateChangeNotifier = stateNotifier ?: [AppStateChangeNotifier new];
__weak typeof(self) wself = self;
_appStateChangeNotifier.didBackgroundBlock = ^{
typeof(self) sself = wself;
[sself pauseDiscovery];
};
_appStateChangeNotifier.didForegroundBlock = ^{
typeof(self) sself = wself;
[sself resumeDiscovery];
};

[self startSSIDTimer];
}

Expand Down Expand Up @@ -445,8 +464,7 @@ - (void) startDiscovery
[service startDiscovery];
}];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hAppDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hAppDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
[self.appStateChangeNotifier startListening];
}

- (void) stopDiscovery
Expand All @@ -462,8 +480,35 @@ - (void) stopDiscovery

if (!_shouldResumeSearch)
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
[self.appStateChangeNotifier stopListening];
}
}

/// Pauses all discovery providers and the SSID change timer.
- (void)pauseDiscovery {
// moved from -hAppDidEnterBackground:
[self stopSSIDTimer];

if (_searching)
{
_searching = NO;
_shouldResumeSearch = YES;

[self.discoveryProviders makeObjectsPerformSelector:@selector(pauseDiscovery)];
}
}

/// Resumes all discovery providers and the SSID change timer.
- (void)resumeDiscovery {
// moved from -hAppDidBecomeActive:
[self startSSIDTimer];

if (_shouldResumeSearch)
{
_searching = YES;
_shouldResumeSearch = NO;

[self.discoveryProviders makeObjectsPerformSelector:@selector(resumeDiscovery)];
}
}

Expand Down Expand Up @@ -654,36 +699,6 @@ - (ConnectableDevice *) lookupMatchingDeviceForDeviceStore:(ServiceConfig *)serv
return foundDevice;
}

#pragma mark - Handle background state

- (void) hAppDidEnterBackground:(NSNotification *)notification
{
[self stopSSIDTimer];

if (_searching)
{
_shouldResumeSearch = YES;
[self stopDiscovery];
}
}

- (void) hAppDidBecomeActive:(NSNotification *)notification
{
[self startSSIDTimer];

if (_shouldResumeSearch)
{
_shouldResumeSearch = NO;
[self startDiscovery];
}
}

- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
}

#pragma mark - Device Picker creation

- (DevicePicker *) devicePicker
Expand Down
11 changes: 11 additions & 0 deletions Discovery/DiscoveryManager_Private.h
Expand Up @@ -20,10 +20,20 @@

#import "DiscoveryManager.h"

@class AppStateChangeNotifier;
@class DiscoveryProvider;

NS_ASSUME_NONNULL_BEGIN
@interface DiscoveryManager ()

/// An @c AppStateChangeNotifier that allows to track app state changes.
@property (nonatomic, readonly) AppStateChangeNotifier *appStateChangeNotifier;


/// Initializes the instance with the given @c AppStateChangeNotifier. Using
/// @c nil parameter will create real object.
- (instancetype)initWithAppStateChangeNotifier:(nullable AppStateChangeNotifier *)stateNotifier;

/**
* Registers a service with the given @c deviceClass and a @c DiscoveryProvider
* created by the @c providerFactory.
Expand All @@ -32,3 +42,4 @@
withDiscoveryProviderFactory:(DiscoveryProvider *(^)(void))providerFactory;

@end
NS_ASSUME_NONNULL_END
12 changes: 12 additions & 0 deletions Discovery/DiscoveryProvider.h
Expand Up @@ -70,4 +70,16 @@
*/
- (void) stopDiscovery;

/**
* Pauses the discovery while the app is in background. Calls @c -stopDiscovery
* by default.
*/
- (void)pauseDiscovery;

/**
* Resumes the discovery when the app has foregrounded. Calls @c -startDiscovery
* be default.
*/
- (void)resumeDiscovery;

@end
8 changes: 8 additions & 0 deletions Discovery/DiscoveryProvider.m
Expand Up @@ -29,4 +29,12 @@ - (BOOL)isEmpty { return YES; }
- (void)startDiscovery { }
- (void)stopDiscovery { }

- (void)pauseDiscovery {
[self stopDiscovery];
}

- (void)resumeDiscovery {
[self startDiscovery];
}

@end

0 comments on commit 9732a8e

Please sign in to comment.