Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

MGLOfflinePackObserver #15851

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions platform/darwin/src/MGLOfflinePack.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ NS_ASSUME_NONNULL_BEGIN

FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLInvalidOfflinePackException;

@class MGLOfflinePack;

/**
The state an offline pack is currently in.
*/
Expand Down Expand Up @@ -96,6 +98,12 @@ typedef struct __attribute__((objc_boxable)) MGLOfflinePackProgress {
uint64_t maximumResourcesExpected;
} MGLOfflinePackProgress;

@protocol MGLOfflinePackObserver <NSObject>

-(void)didUpdateStateForMGLOfflinePack:(MGLOfflinePack*)offlinePack;

@end

/**
An `MGLOfflinePack` represents a collection of resources necessary for viewing
a region offline to a local database.
Expand Down Expand Up @@ -200,4 +208,13 @@ MGL_EXPORT

@end


@interface MGLOfflinePack (Observers)

-(void)addObserver:(id<MGLOfflinePackObserver>)observer;

-(void)removeObserver:(id<MGLOfflinePackObserver>)observer;

@end

NS_ASSUME_NONNULL_END
30 changes: 29 additions & 1 deletion platform/darwin/src/MGLOfflinePack.mm
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ @interface MGLOfflinePack ()

@property (nonatomic, nullable, readwrite) mbgl::OfflineRegion *mbglOfflineRegion;
@property (nonatomic, readwrite) MGLOfflinePackProgress progress;
@property (nonatomic, strong) NSHashTable<id<MGLOfflinePackObserver>>* observers;

@end

Expand All @@ -76,6 +77,7 @@ - (instancetype)initWithMBGLRegion:(mbgl::OfflineRegion *)region {
_mbglOfflineRegion = region;
_state = MGLOfflinePackStateUnknown;

_observers = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
_mbglFileSource = [[MGLOfflineStorage sharedOfflineStorage] mbglFileSource];
_mbglFileSource->setOfflineRegionObserver(*_mbglOfflineRegion, std::make_unique<MBGLOfflineRegionObserver>(self));
}
Expand Down Expand Up @@ -161,20 +163,34 @@ - (void)setState:(MGLOfflinePackState)state {
if (!_isSuspending || state != MGLOfflinePackStateActive) {
_isSuspending = NO;
_state = state;
for (id<MGLOfflinePackObserver> observer in self.observers) {
if ([observer conformsToProtocol:@protocol(MGLOfflinePackObserver)]) {
[observer didUpdateStateForMGLOfflinePack:self];
} else {
MGLLogError(@"One of observers of MGLOfflinePack was dealoceted and not removed form observers.");
}
}
}
}

- (void)requestProgress {
[self requestProgressWithCompletionHandler:^{}];
}

- (void)requestProgressWithCompletionHandler:(void (^)())completion {
MGLLogInfo(@"Requesting pack progress.");
MGLAssertOfflinePackIsValid();

__weak MGLOfflinePack *weakSelf = self;
_mbglFileSource->getOfflineRegionStatus(*_mbglOfflineRegion, [&, weakSelf](mbgl::expected<mbgl::OfflineRegionStatus, std::exception_ptr> status) {
_mbglFileSource->getOfflineRegionStatus(*_mbglOfflineRegion, [&, weakSelf, completion](mbgl::expected<mbgl::OfflineRegionStatus, std::exception_ptr> status) {
if (status) {
mbgl::OfflineRegionStatus checkedStatus = *status;
dispatch_async(dispatch_get_main_queue(), ^{
MGLOfflinePack *strongSelf = weakSelf;
[strongSelf offlineRegionStatusDidChange:checkedStatus];
if (completion) {
completion();
}
});
}
});
Expand Down Expand Up @@ -280,3 +296,15 @@ - (void)didReceiveMaximumAllowedMapboxTiles:(uint64_t)limit {
[weakPack didReceiveMaximumAllowedMapboxTiles:limit];
});
}

@implementation MGLOfflinePack (Observers)

-(void)addObserver:(id<MGLOfflinePackObserver>)observer {
[self.observers addObject:observer];
}

-(void)removeObserver:(id<MGLOfflinePackObserver>)observer {
[self.observers removeObject:observer];
}

@end
2 changes: 2 additions & 0 deletions platform/darwin/src/MGLOfflinePack_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ NS_ASSUME_NONNULL_BEGIN

- (instancetype)initWithMBGLRegion:(mbgl::OfflineRegion *)region;

- (void)requestProgressWithCompletionHandler:(void (^)())completion;

/**
Invalidates the pack and ensures that no future progress update can ever
revalidate it. This method must be called before the pack is deallocated.
Expand Down
9 changes: 9 additions & 0 deletions platform/darwin/src/MGLOfflineStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,15 @@ MGL_EXPORT
URLForResourceOfKind:(MGLResourceKind)kind
withURL:(NSURL *)url;

@optional
/**
Notifies about package reload

@param storage The storage object processing the reload.

*/
- (void)didReloadPackagesForOfflineStorage:(MGLOfflineStorage *)storage;

@end

NS_ASSUME_NONNULL_END
33 changes: 33 additions & 0 deletions platform/darwin/src/MGLOfflineStorage.mm
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,42 @@ - (void)reloadPacks {
[pack invalidate];
}
self.packs = [packs mutableCopy];
MGLLogInfo(@"Packs reloaded.");
__weak MGLOfflineStorage *weakSelf = self;
[self requestPackProgressWithCompletionHandler:^{
MGLOfflineStorage *strongSelf = weakSelf;
if([strongSelf.delegate respondsToSelector:@selector(didReloadPackagesForOfflineStorage:)]) {
[strongSelf.delegate didReloadPackagesForOfflineStorage:strongSelf];
}
}];
}];
}

-(void)requestPackProgressWithCompletionHandler:(void (^)())completion {
__block NSUInteger count = 0;
__block NSUInteger expectedCount = self.packs.count;
if (self.packs == nil) {
//TODO: completion with error?
return;
}
if( self.packs.count == 0) {
completion();
return;
}
for (MGLOfflinePack *pack in self.packs) {
if(pack.state == MGLOfflinePackStateComplete) {
count++;
return;
}
[pack requestProgressWithCompletionHandler:^{
count++;
if(count == expectedCount) {
completion();
}
}];
}
}

- (void)getPacksWithCompletionHandler:(void (^)(NSArray<MGLOfflinePack *> *packs, NSError * _Nullable error))completion {
_mbglFileSource->listOfflineRegions([&, completion](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> result) {
NSError *error;
Expand Down
87 changes: 55 additions & 32 deletions platform/darwin/test/MGLOfflineStorageTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,34 @@

#pragma clang diagnostic ignored "-Wshadow"

@interface MGLOfflineStorageBlockDelegate : NSObject <MGLOfflineStorageDelegate>
@property (nonatomic, copy, nullable) NSURL* (^URLForResourceOfKind)(MGLOfflineStorage *storage, MGLResourceKind kind, NSURL *url);
@property (nonatomic, copy, nullable) void (^didReloadPackagesForOfflineStorage)(MGLOfflineStorage *storage);

@interface MGLOfflineStorageTests : XCTestCase <MGLOfflineStorageDelegate>
@end


@implementation MGLOfflineStorageBlockDelegate

- (NSURL *)offlineStorage:(MGLOfflineStorage *)storage
URLForResourceOfKind:(MGLResourceKind)kind
withURL:(NSURL *)url {
if(self.URLForResourceOfKind != nil) {
return self.URLForResourceOfKind(storage, kind, url);
} else {
return url;
}
}

- (void)didReloadPackagesForOfflineStorage:(nonnull MGLOfflineStorage *)storage {
if(self.didReloadPackagesForOfflineStorage != nil) {
self.didReloadPackagesForOfflineStorage(storage);
}
}

@end

@interface MGLOfflineStorageTests : XCTestCase
@end

@implementation MGLOfflineStorageTests
Expand All @@ -33,26 +59,6 @@ + (void)tearDown {
XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:cacheURL.path], @"Cache subdirectory should not exist.");
}

- (void)setUp {
[super setUp];

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
XCTestExpectation *expectation = [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]);
return changeKind == NSKeyValueChangeSetting;
}];
if ([MGLOfflineStorage sharedOfflineStorage].packs) {
[expectation fulfill];
[self waitForExpectationsWithTimeout:0 handler:nil];
} else {
[self waitForExpectationsWithTimeout:10 handler:nil];
}

XCTAssertNotNil([MGLOfflineStorage sharedOfflineStorage].packs, @"Shared offline storage object should have a non-nil collection of packs by this point.");
});
}

- (void)testSharedObject {
XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage], [MGLOfflineStorage sharedOfflineStorage], @"There should only be one shared offline storage object.");
}
Expand Down Expand Up @@ -506,19 +512,17 @@ - (void)testCountOfBytesCompleted {
XCTAssertGreaterThan([MGLOfflineStorage sharedOfflineStorage].countOfBytesCompleted, 0UL);
}

- (NSURL *)offlineStorage:(MGLOfflineStorage *)storage
URLForResourceOfKind:(MGLResourceKind)kind
withURL:(NSURL *)url {
if ([url.scheme isEqual: @"test"] && [url.host isEqual: @"api"]) {
return [NSURL URLWithString:@"https://api.mapbox.com"];
} else {
return url;
}
}

- (void)testResourceTransform {
MGLOfflineStorage *os = [MGLOfflineStorage sharedOfflineStorage];
[os setDelegate:self];
MGLOfflineStorageBlockDelegate* delegate = [[MGLOfflineStorageBlockDelegate alloc] init];
delegate.URLForResourceOfKind = ^NSURL *(MGLOfflineStorage *storage, MGLResourceKind kind, NSURL *url) {
if ([url.scheme isEqual: @"test"] && [url.host isEqual: @"api"]) {
return [NSURL URLWithString:@"https://api.mapbox.com"];
} else {
return url;
}
};
[os setDelegate:delegate];

auto fs = os.mbglFileSource;

Expand All @@ -534,8 +538,27 @@ - (void)testResourceTransform {
});

CFRunLoopRun();
[os setDelegate:nil];
}

-(void)testDidCallReloadPackagesForOfflineStorage {
__block XCTestExpectation* didReceiveCallbackExpectation = [self expectationWithDescription:@"didReceiveCallback"];
MGLOfflineStorageBlockDelegate* delegate = [[MGLOfflineStorageBlockDelegate alloc] init];
delegate.URLForResourceOfKind = ^NSURL *(MGLOfflineStorage *storage, MGLResourceKind kind, NSURL *url) {
return [NSURL URLWithString:@"https://api.mapbox.com"];
};
delegate.didReloadPackagesForOfflineStorage = ^void(MGLOfflineStorage *storage) {
[didReceiveCallbackExpectation fulfill];
};

MGLOfflineStorage *os = [MGLOfflineStorage sharedOfflineStorage];
[os setDelegate:delegate];

[self waitForExpectationsWithTimeout:60 handler:nil];

XCTAssertNotNil(os.packs);
[os setDelegate:nil];
NSLog(@"Delegate %@ didReceiveCallback was called for packages %@", delegate, os.packs);
}

- (void)testAddFileContent {
Expand Down