From 73da07140fbe4ea8c0c96d85379f10f60471437d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Tue, 3 May 2016 23:45:58 +0200 Subject: [PATCH 01/12] Add tests for cases where feedbackComposerPreparedItems is nil --- .../HockeySDKTests/BITFeedbackManagerTests.m | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/Support/HockeySDKTests/BITFeedbackManagerTests.m b/Support/HockeySDKTests/BITFeedbackManagerTests.m index 7a1dc309f3..4f5ac3d302 100644 --- a/Support/HockeySDKTests/BITFeedbackManagerTests.m +++ b/Support/HockeySDKTests/BITFeedbackManagerTests.m @@ -236,24 +236,35 @@ self.sut.feedbackComposeHideImageAttachmentButton = YES; XCTAssertTrue(self.sut.feedbackComposeHideImageAttachmentButton); - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" - self.sut.feedbackComposerPreparedItems = @[sampleImage1, sampleData1]; -#pragma clang diagnostic pop id mockDelegate = mockProtocol(@protocol(BITFeedbackManagerDelegate)); [given([mockDelegate preparedItemsForFeedbackManager:self.sut]) willReturn:@[sampleImage2, sampleData2]]; self.sut.delegate = mockDelegate; + + // Test when feedbackComposerPreparedItems is also set +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + self.sut.feedbackComposerPreparedItems = @[sampleImage1, sampleData1]; +#pragma clang diagnostic pop + BITFeedbackComposeViewController *composeViewController = [self.sut feedbackComposeViewController]; - NSArray *attachments = [composeViewController performSelector:@selector(attachments)]; - XCTAssertEqual(attachments.count, 4); + + + // Test when feedbackComposerPreparedItems is nil +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + self.sut.feedbackComposerPreparedItems = nil; +#pragma clang diagnostic pop + + composeViewController = [self.sut feedbackComposeViewController]; + attachments = [composeViewController performSelector:@selector(attachments)]; + XCTAssertEqual(attachments.count, 2); + XCTAssertTrue(composeViewController.hideImageAttachmentButton); - XCTAssertEqual(composeViewController.delegate, mockDelegate); } From 55c78b27b892b44342d06c62c7827cdf965c7866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Tue, 3 May 2016 23:46:13 +0200 Subject: [PATCH 02/12] Make sure preparedItems array is always initialized --- Classes/BITFeedbackManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/BITFeedbackManager.m b/Classes/BITFeedbackManager.m index 3135f31847..e9f322d34a 100644 --- a/Classes/BITFeedbackManager.m +++ b/Classes/BITFeedbackManager.m @@ -234,7 +234,7 @@ typedef void (^BITLatestImageFetchCompletionBlock)(UIImage *_Nonnull latestImage #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated" - NSArray *preparedItems = self.feedbackComposerPreparedItems; + NSArray *preparedItems = self.feedbackComposerPreparedItems ?: [NSArray array]; #pragma clang diagnostic pop if ([self.delegate respondsToSelector:@selector(preparedItemsForFeedbackManager:)]) { preparedItems = [preparedItems arrayByAddingObjectsFromArray:[self.delegate preparedItemsForFeedbackManager:self]]; From 6520c77afdd59d7437cf652ec86c75c93e81b488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 5 May 2016 18:26:22 +0200 Subject: [PATCH 03/12] Fix scenario where wrong folder is excluded from backup --- Classes/BITPersistence.m | 104 ++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/Classes/BITPersistence.m b/Classes/BITPersistence.m index 5235ef4f39..b5721e2478 100644 --- a/Classes/BITPersistence.m +++ b/Classes/BITPersistence.m @@ -13,12 +13,22 @@ static NSString *const kBITTelemetry = @"Telemetry"; static NSString *const kBITMetaData = @"MetaData"; static NSString *const kBITFileBaseString = @"hockey-app-bundle-"; static NSString *const kBITFileBaseStringMeta = @"metadata"; -static NSString *const kBITTelemetryDirectoryPath = @"com.microsoft.HockeyApp/Telemetry/"; -static NSString *const kBITMetaDataDirectoryPath = @"com.microsoft.HockeyApp/MetaData/"; + +static NSString *const kBITHockeyDirectory = @"com.microsoft.HockeyApp"; +static NSString *const kBITTelemetryDirectory = @"Telemetry"; +static NSString *const kBITMetaDataDirectory = @"MetaData"; static char const *kBITPersistenceQueueString = "com.microsoft.HockeyApp.persistenceQueue"; static NSUInteger const BITDefaultFileCount = 50; + +@interface BITPersistence () + +@property (nonatomic, strong) NSString *appHockeySDKDirectoryPath; + +@end + + @implementation BITPersistence { BOOL _maxFileCountReached; BOOL _directorySetupComplete; @@ -44,9 +54,9 @@ static NSUInteger const BITDefaultFileCount = 50; NSString *directoryPath = [self folderPathForType:BITPersistenceTypeTelemetry]; NSError *error = nil; NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[NSURL fileURLWithPath:directoryPath] - includingPropertiesForKeys:@[NSURLNameKey] - options:NSDirectoryEnumerationSkipsHiddenFiles - error:&error]; + includingPropertiesForKeys:@[NSURLNameKey] + options:NSDirectoryEnumerationSkipsHiddenFiles + error:&error]; _maxFileCountReached = fileNames.count >= _maxFileCount; } return self; @@ -109,7 +119,7 @@ static NSUInteger const BITDefaultFileCount = 50; - (NSDictionary *)metaData { NSString *filePath = [self fileURLForType:BITPersistenceTypeMetaData]; NSObject *bundle = [self bundleAtFilePath:filePath withFileBaseString:kBITFileBaseStringMeta]; - if ([bundle isMemberOfClass:NSDictionary.class]) { + if ([bundle isKindOfClass:NSDictionary.class]) { return (NSDictionary *) bundle; } BITHockeyLog(@"INFO: The context meta data file could not be loaded."); @@ -169,8 +179,6 @@ static NSUInteger const BITDefaultFileCount = 50; #pragma mark - Private - (NSString *)fileURLForType:(BITPersistenceType)type { - NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); - NSString *appSupportPath = searchPaths.lastObject; NSString *fileName = nil; NSString *filePath; @@ -178,13 +186,13 @@ static NSUInteger const BITDefaultFileCount = 50; switch (type) { case BITPersistenceTypeMetaData: { fileName = kBITFileBaseStringMeta; - filePath = [appSupportPath stringByAppendingPathComponent:kBITMetaDataDirectoryPath]; + filePath = [self.appHockeySDKDirectoryPath stringByAppendingPathComponent:kBITMetaDataDirectory]; break; }; default: { NSString *uuid = bit_UUID(); fileName = [NSString stringWithFormat:@"%@%@", kBITFileBaseString, uuid]; - filePath = [appSupportPath stringByAppendingPathComponent:kBITTelemetryDirectoryPath]; + filePath = [self.appHockeySDKDirectoryPath stringByAppendingPathComponent:kBITTelemetryDirectory]; break; }; } @@ -198,39 +206,46 @@ static NSUInteger const BITDefaultFileCount = 50; * Create directory structure if necessary and exclude it from iCloud backup */ - (void)createDirectoryStructureIfNeeded { - //Application Support Dir + + NSURL *appURL = [NSURL fileURLWithPath:self.appHockeySDKDirectoryPath]; NSFileManager *fileManager = [NSFileManager defaultManager]; - NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject]; - if (appSupportURL) { + if (appURL) { NSError *error = nil; - //App Support and Telemetry Directory - NSURL *folderURL = [appSupportURL URLByAppendingPathComponent:kBITTelemetryDirectoryPath]; + + // Create HockeySDK folder if needed + if (![fileManager createDirectoryAtURL:appURL withIntermediateDirectories:YES attributes:nil error:&error]) { + BITHockeyLog(@"%@", error.localizedDescription); + return; + } + + //Exclude HockeySDK folder from backup + if (![appURL setResourceValue:@YES + forKey:NSURLIsExcludedFromBackupKey + error:&error]) { + BITHockeyLog(@"Error excluding %@ from backup %@", appURL.lastPathComponent, error.localizedDescription); + } else { + BITHockeyLog(@"Exclude %@ from backup", appURL); + } + + // Create metadata subfolder + NSURL *metaDataURL = [appURL URLByAppendingPathComponent:kBITMetaDataDirectory]; + if (![fileManager createDirectoryAtURL:metaDataURL withIntermediateDirectories:YES attributes:nil error:&error]) { + BITHockeyLog(@"%@", error.localizedDescription); + return; + } + + // Create telemetry subfolder + //NOTE: createDirectoryAtURL:withIntermediateDirectories:attributes:error //will return YES if the directory already exists and won't override anything. //No need to check if the directory already exists. - if (![fileManager createDirectoryAtURL:folderURL withIntermediateDirectories:YES attributes:nil error:&error]) { + NSURL *telemetryURL = [appURL URLByAppendingPathComponent:kBITTelemetryDirectory]; + if (![fileManager createDirectoryAtURL:telemetryURL withIntermediateDirectories:YES attributes:nil error:&error]) { BITHockeyLog(@"%@", error.localizedDescription); - return; //TODO we can't use persistence at all in this case, what do we want to do now? Notify the user? + return; } - - //MetaData Directory - folderURL = [appSupportURL URLByAppendingPathComponent:kBITMetaDataDirectoryPath]; - if (![fileManager createDirectoryAtURL:folderURL withIntermediateDirectories:NO attributes:nil error:&error]) { - BITHockeyLog(@"%@", error.localizedDescription); - return; //TODO we can't use persistence at all in this case, what do we want to do now? Notify the user? - } - + _directorySetupComplete = YES; - - //Exclude from Backup - if (![appSupportURL setResourceValue:@YES - forKey:NSURLIsExcludedFromBackupKey - error:&error]) { - BITHockeyLog(@"Error excluding %@ from backup %@", appSupportURL.lastPathComponent, error.localizedDescription); - } - else { - BITHockeyLog(@"Exclude %@ from backup", appSupportURL); - } } } @@ -262,21 +277,18 @@ static NSUInteger const BITDefaultFileCount = 50; } - (NSString *)folderPathForType:(BITPersistenceType)type { - NSString *path = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject]; NSString *subFolder = @""; switch (type) { case BITPersistenceTypeTelemetry: { - subFolder = kBITTelemetryDirectoryPath; + subFolder = kBITTelemetryDirectory; break; } case BITPersistenceTypeMetaData: { - subFolder = kBITMetaDataDirectoryPath; + subFolder = kBITMetaDataDirectory; break; } } - path = [path stringByAppendingPathComponent:subFolder]; - - return path; + return [self.appHockeySDKDirectoryPath stringByAppendingPathComponent:subFolder]; } /** @@ -291,6 +303,16 @@ static NSUInteger const BITDefaultFileCount = 50; }); } +- (NSString *)appHockeySDKDirectoryPath { + if (!_appHockeySDKDirectoryPath) { + NSString *appSupportPath = [[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject] stringByStandardizingPath]; + if (appSupportPath) { + _appHockeySDKDirectoryPath = [appSupportPath stringByAppendingPathComponent:kBITHockeyDirectory]; + } + } + return _appHockeySDKDirectoryPath; +} + @end #endif /* HOCKEYSDK_FEATURE_METRICS */ From bc9d9c463b430dda85877f045d1c4ad03df12099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 5 May 2016 18:27:47 +0200 Subject: [PATCH 04/12] Add logic to fix wrong flag on Application Support --- Classes/BITHockeyHelper.h | 6 ++++++ Classes/BITHockeyHelper.m | 25 +++++++++++++++++++++++++ Classes/BITHockeyManager.m | 5 +++++ Classes/BITMetricsManager.m | 9 +++++---- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/Classes/BITHockeyHelper.h b/Classes/BITHockeyHelper.h index 4de69a2c5d..8d120045e0 100644 --- a/Classes/BITHockeyHelper.h +++ b/Classes/BITHockeyHelper.h @@ -32,6 +32,8 @@ @interface BITHockeyHelper : NSObject +FOUNDATION_EXPORT NSString *const kBITExcludeApplicationSupportFromBackup; + + (BOOL)isURLSessionSupported; @end @@ -88,4 +90,8 @@ UIImage *bit_imageWithContentsOfResolutionIndependentFile(NSString * path); UIImage *bit_imageNamed(NSString *imageName, NSString *bundleName); UIImage *bit_screenshot(void); UIImage *bit_appIcon(void); + +/* Fix bug where Application Support was excluded from backup. */ +void bit_fixBackupAttributeForURL(NSURL *directoryURL); + #endif diff --git a/Classes/BITHockeyHelper.m b/Classes/BITHockeyHelper.m index a1afc6ff45..9ce169996e 100644 --- a/Classes/BITHockeyHelper.m +++ b/Classes/BITHockeyHelper.m @@ -38,6 +38,7 @@ #import static NSString *const kBITUtcDateFormatter = @"utcDateFormatter"; +NSString *const kBITExcludeApplicationSupportFromBackup = @"kBITExcludeApplicationSupportFromBackup"; @implementation BITHockeyHelper @@ -141,6 +142,28 @@ NSComparisonResult bit_versionCompare(NSString *stringA, NSString *stringB) { return result; } +#pragma mark Exclude from backup fix + +void bit_fixBackupAttributeForURL(NSURL *directoryURL) { + + BOOL shouldExcludeAppSupportDirFromBackup = [[NSUserDefaults standardUserDefaults] boolForKey:kBITExcludeApplicationSupportFromBackup]; + if (shouldExcludeAppSupportDirFromBackup) { + return; + } + + if (directoryURL) { + NSError *getResourceError = nil; + NSNumber *appSupportDirExcludedValue; + + if ([directoryURL getResourceValue:&appSupportDirExcludedValue forKey:NSURLIsExcludedFromBackupKey error:&getResourceError] && appSupportDirExcludedValue) { + NSError *setResourceError = nil; + [directoryURL setResourceValue:@NO forKey:NSURLIsExcludedFromBackupKey error:&setResourceError]; + } + } +} + +#pragma mark Identifiers + NSString *bit_mainBundleIdentifier(void) { return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; } @@ -246,6 +269,8 @@ NSString *bit_appAnonID(BOOL forceNewAnonID) { return appAnonID; } +#pragma mark Environment detection + BOOL bit_isPreiOS7Environment(void) { static BOOL isPreiOS7Environment = YES; static dispatch_once_t checkOS; diff --git a/Classes/BITHockeyManager.m b/Classes/BITHockeyManager.m index 8fa3af2bbb..083d3b37a6 100644 --- a/Classes/BITHockeyManager.m +++ b/Classes/BITHockeyManager.m @@ -228,6 +228,11 @@ bitstadium_info_t bitstadium_library_info __attribute__((section("__TEXT,__bit_h return; } + // Fix bug where Application Support directory was encluded from backup + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject]; + bit_fixBackupAttributeForURL(appSupportURL); + if (![self isSetUpOnMainThread]) return; if ((self.appEnvironment == BITEnvironmentAppStore) && [self isInstallTrackingDisabled]) { diff --git a/Classes/BITMetricsManager.m b/Classes/BITMetricsManager.m index 48ae3065dc..cb2c266b41 100644 --- a/Classes/BITMetricsManager.m +++ b/Classes/BITMetricsManager.m @@ -15,14 +15,15 @@ #import "BITHockeyBaseManagerPrivate.h" #import "BITSender.h" +NSString *const kBITApplicationWasLaunched = @"BITApplicationWasLaunched"; + static char *const kBITMetricsEventQueue = "net.hockeyapp.telemetryEventQueue"; -NSString *const kBITSessionFileType = @"plist"; -NSString *const kBITApplicationDidEnterBackgroundTime = @"BITApplicationDidEnterBackgroundTime"; -NSString *const kBITApplicationWasLaunched = @"BITApplicationWasLaunched"; +static NSString *const kBITSessionFileType = @"plist"; +static NSString *const kBITApplicationDidEnterBackgroundTime = @"BITApplicationDidEnterBackgroundTime"; -NSString *const BITMetricsEndpoint = @"https://gate.hockeyapp.net/v2/track"; +static NSString *const BITMetricsEndpoint = @"https://gate.hockeyapp.net/v2/track"; @implementation BITMetricsManager { id _appWillEnterForegroundObserver; From d26eed7e4fa20cc45396a2ad7df54941a3b61c7a Mon Sep 17 00:00:00 2001 From: chrwend Date: Thu, 5 May 2016 11:03:30 -0700 Subject: [PATCH 05/12] Test if fix removes exclude-attribute from folder --- Support/HockeySDKTests/BITHockeyHelperTests.m | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Support/HockeySDKTests/BITHockeyHelperTests.m b/Support/HockeySDKTests/BITHockeyHelperTests.m index 5f0cdc30cd..ee006d4f9f 100644 --- a/Support/HockeySDKTests/BITHockeyHelperTests.m +++ b/Support/HockeySDKTests/BITHockeyHelperTests.m @@ -31,6 +31,7 @@ - (void)tearDown { // Tear-down code here. [super tearDown]; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:kBITExcludeApplicationSupportFromBackup]; } - (void)testURLEncodedString { @@ -229,4 +230,47 @@ assertThat(result, nilValue()); } +- (void)testBackupFixRemovesExcludeAttribute { + + // Setup: Attribute is set and NSUSerDefaults DON'T contain kBITExcludeApplicationSupportFromBackup == YES + NSURL *testAppSupportURL = [self createBackupExcludedTastDirectoryForURL]; + assertThat(testAppSupportURL, notNilValue()); + assertThatBool([self excludeAttributeIsSetForURL:testAppSupportURL], isTrue()); + + // Test + bit_fixBackupAttributeForURL(testAppSupportURL); + + // Verify + assertThatBool([self excludeAttributeIsSetForURL:testAppSupportURL], isFalse()); +} + +#pragma mark - Test Helper + +- (NSURL *)createBackupExcludedTastDirectoryForURL{ + NSString *testDirectory = @"HockeyTest"; + NSFileManager *fileManager = [NSFileManager defaultManager]; + + NSURL *testAppSupportURL = [[[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:testDirectory]; + if ([fileManager createDirectoryAtURL:testAppSupportURL withIntermediateDirectories:YES attributes:nil error:nil]) { + if ([testAppSupportURL setResourceValue:@YES + forKey:NSURLIsExcludedFromBackupKey + error:nil]) { + return testAppSupportURL; + } + } + return nil; +} + +- (BOOL)excludeAttributeIsSetForURL:(NSURL *)directoryURL { + + NSError *getResourceError = nil; + NSNumber *appSupportDirExcludedValue; + if ([directoryURL getResourceValue:&appSupportDirExcludedValue forKey:NSURLIsExcludedFromBackupKey error:&getResourceError] && appSupportDirExcludedValue) { + if ([appSupportDirExcludedValue isEqualToValue:@YES]) { + return YES; + } + } + return NO; +} + @end From 375d70f698e068720e0867eaa020a3ec44e07a54 Mon Sep 17 00:00:00 2001 From: chrwend Date: Thu, 5 May 2016 11:04:01 -0700 Subject: [PATCH 06/12] Test if fix ignores removal of exclude-attribute if developer set flag in NSUserDefaults --- Support/HockeySDKTests/BITHockeyHelperTests.m | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Support/HockeySDKTests/BITHockeyHelperTests.m b/Support/HockeySDKTests/BITHockeyHelperTests.m index ee006d4f9f..62883f098f 100644 --- a/Support/HockeySDKTests/BITHockeyHelperTests.m +++ b/Support/HockeySDKTests/BITHockeyHelperTests.m @@ -233,20 +233,35 @@ - (void)testBackupFixRemovesExcludeAttribute { // Setup: Attribute is set and NSUSerDefaults DON'T contain kBITExcludeApplicationSupportFromBackup == YES - NSURL *testAppSupportURL = [self createBackupExcludedTastDirectoryForURL]; - assertThat(testAppSupportURL, notNilValue()); - assertThatBool([self excludeAttributeIsSetForURL:testAppSupportURL], isTrue()); + NSURL *testAppSupportURL = [self createBackupExcludedTestDirectoryForURL]; + XCTAssertNotNil(testAppSupportURL); + XCTAssertTrue([self excludeAttributeIsSetForURL:testAppSupportURL]); // Test bit_fixBackupAttributeForURL(testAppSupportURL); // Verify - assertThatBool([self excludeAttributeIsSetForURL:testAppSupportURL], isFalse()); + XCTAssertFalse([self excludeAttributeIsSetForURL:testAppSupportURL]); +} + +- (void)testBackupFixIgnoresRemovalOfExcludeAttributeUserDefaultsContainKey { + + // Setup: Attribute is set and NSUSerDefaults DO contain kBITExcludeApplicationSupportFromBackup == YES + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kBITExcludeApplicationSupportFromBackup]; + NSURL *testAppSupportURL = [self createBackupExcludedTestDirectoryForURL]; + XCTAssertNotNil(testAppSupportURL); + XCTAssertTrue([self excludeAttributeIsSetForURL:testAppSupportURL]); + + // Test + bit_fixBackupAttributeForURL(testAppSupportURL); + + // Verify + XCTAssertTrue([self excludeAttributeIsSetForURL:testAppSupportURL]); } #pragma mark - Test Helper -- (NSURL *)createBackupExcludedTastDirectoryForURL{ +- (NSURL *)createBackupExcludedTestDirectoryForURL{ NSString *testDirectory = @"HockeyTest"; NSFileManager *fileManager = [NSFileManager defaultManager]; From 85a468acb632c08c02003457b69a1e6b87f2e81a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 5 May 2016 21:18:33 +0200 Subject: [PATCH 07/12] Add tests for createDirectoryStructureIfNeeded --- Classes/BITPersistence.m | 8 ---- Classes/BITPersistencePrivate.h | 3 ++ Support/HockeySDKTests/BITPersistenceTests.m | 49 ++++++++++++++++++++ 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/Classes/BITPersistence.m b/Classes/BITPersistence.m index b5721e2478..256a4f4607 100644 --- a/Classes/BITPersistence.m +++ b/Classes/BITPersistence.m @@ -21,14 +21,6 @@ static NSString *const kBITMetaDataDirectory = @"MetaData"; static char const *kBITPersistenceQueueString = "com.microsoft.HockeyApp.persistenceQueue"; static NSUInteger const BITDefaultFileCount = 50; - -@interface BITPersistence () - -@property (nonatomic, strong) NSString *appHockeySDKDirectoryPath; - -@end - - @implementation BITPersistence { BOOL _maxFileCountReached; BOOL _directorySetupComplete; diff --git a/Classes/BITPersistencePrivate.h b/Classes/BITPersistencePrivate.h index 30be643d6a..7ae1a7332c 100644 --- a/Classes/BITPersistencePrivate.h +++ b/Classes/BITPersistencePrivate.h @@ -63,6 +63,8 @@ FOUNDATION_EXPORT NSString *const BITPersistenceSuccessNotification; */ @property (nonatomic, assign) NSUInteger maxFileCount; +@property (nonatomic, strong) NSString *appHockeySDKDirectoryPath; + /** * An array with all file paths, that have been requested by the sender. If the sender * triggers a delete, the appropriate path should also be removed here. We keep to @@ -171,6 +173,7 @@ FOUNDATION_EXPORT NSString *const BITPersistenceSuccessNotification; */ - (NSString *)fileURLForType:(BITPersistenceType)type; +- (void)createDirectoryStructureIfNeeded; #endif /* HOCKEYSDK_FEATURE_METRICS */ diff --git a/Support/HockeySDKTests/BITPersistenceTests.m b/Support/HockeySDKTests/BITPersistenceTests.m index 753290a59e..c06dc93a86 100644 --- a/Support/HockeySDKTests/BITPersistenceTests.m +++ b/Support/HockeySDKTests/BITPersistenceTests.m @@ -45,6 +45,55 @@ [self.mockNotificationCenter removeObserver:observerMock]; } +- (void)testCreateDirectoryStructureIfNeeded { + // Setup + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *appSupportPath = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject]; + NSString *path = self.sut.appHockeySDKDirectoryPath; + + NSError *fileRemovalError = nil; + [fileManager removeItemAtPath:path error:&fileRemovalError]; + + NSError *getResourceError = nil; + NSNumber *resourveValue = nil; + XCTAssertTrue([[NSURL fileURLWithPath:appSupportPath] setResourceValue:@NO + forKey:NSURLIsExcludedFromBackupKey + error:&getResourceError]); + + // Assert + XCTAssertNil(fileRemovalError); + XCTAssertFalse([fileManager fileExistsAtPath:path]); + + // Act + [self.sut createDirectoryStructureIfNeeded]; + + // Verify + BOOL isDirectory = NO; + [fileManager fileExistsAtPath:path isDirectory:&isDirectory]; + + XCTAssertTrue(isDirectory); + + // Flag stays @NO on Application Support directory + getResourceError = nil; + resourveValue = nil; + [[NSURL fileURLWithPath:appSupportPath] getResourceValue:&resourveValue + forKey:NSURLIsExcludedFromBackupKey + error:&getResourceError]; + XCTAssertNil(getResourceError); + XCTAssertEqual(resourveValue, @NO); + + // Flag is set to @YES on our custom subdirectory + getResourceError = nil; + resourveValue = nil; + [[NSURL fileURLWithPath:path] getResourceValue:&resourveValue + forKey:NSURLIsExcludedFromBackupKey + error:&getResourceError]; + XCTAssertNil(getResourceError); + XCTAssertEqual(resourveValue, @YES); + + // TODO: Check subdirectories have been created +} + - (void)testFolderPathForType { NSString *path = [self.sut folderPathForType:BITPersistenceTypeTelemetry]; XCTAssertFalse([path rangeOfString:@"com.microsoft.HockeyApp/Telemetry"].location == NSNotFound); From c8938847873b994edc83e75f5113bf9f765c83ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Thu, 5 May 2016 21:30:32 +0200 Subject: [PATCH 08/12] Set resourceValue after all directories have been created --- Classes/BITPersistence.m | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Classes/BITPersistence.m b/Classes/BITPersistence.m index 256a4f4607..2845f6c731 100644 --- a/Classes/BITPersistence.m +++ b/Classes/BITPersistence.m @@ -210,15 +210,6 @@ static NSUInteger const BITDefaultFileCount = 50; return; } - //Exclude HockeySDK folder from backup - if (![appURL setResourceValue:@YES - forKey:NSURLIsExcludedFromBackupKey - error:&error]) { - BITHockeyLog(@"Error excluding %@ from backup %@", appURL.lastPathComponent, error.localizedDescription); - } else { - BITHockeyLog(@"Exclude %@ from backup", appURL); - } - // Create metadata subfolder NSURL *metaDataURL = [appURL URLByAppendingPathComponent:kBITMetaDataDirectory]; if (![fileManager createDirectoryAtURL:metaDataURL withIntermediateDirectories:YES attributes:nil error:&error]) { @@ -237,6 +228,15 @@ static NSUInteger const BITDefaultFileCount = 50; return; } + //Exclude HockeySDK folder from backup + if (![appURL setResourceValue:@YES + forKey:NSURLIsExcludedFromBackupKey + error:&error]) { + BITHockeyLog(@"Error excluding %@ from backup %@", appURL.lastPathComponent, error.localizedDescription); + } else { + BITHockeyLog(@"Exclude %@ from backup", appURL); + } + _directorySetupComplete = YES; } } From 2401cd25fcd0cf421fa966a84288a061e101759d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Fri, 6 May 2016 03:08:05 +0200 Subject: [PATCH 09/12] Fix function declaration position in BITHockeyHelper --- Classes/BITHockeyHelper.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Classes/BITHockeyHelper.h b/Classes/BITHockeyHelper.h index 8d120045e0..6f77bc3ca6 100644 --- a/Classes/BITHockeyHelper.h +++ b/Classes/BITHockeyHelper.h @@ -43,6 +43,9 @@ NSString *bit_settingsDir(void); BOOL bit_validateEmail(NSString *email); NSString *bit_keychainHockeySDKServiceName(void); +/* Fix bug where Application Support was excluded from backup. */ +void bit_fixBackupAttributeForURL(NSURL *directoryURL); + NSComparisonResult bit_versionCompare(NSString *stringA, NSString *stringB); NSString *bit_mainBundleIdentifier(void); NSString *bit_encodeAppIdentifier(NSString *inputString); @@ -91,7 +94,4 @@ UIImage *bit_imageNamed(NSString *imageName, NSString *bundleName); UIImage *bit_screenshot(void); UIImage *bit_appIcon(void); -/* Fix bug where Application Support was excluded from backup. */ -void bit_fixBackupAttributeForURL(NSURL *directoryURL); - #endif From cc698da8549c9fc4a255eaacb09c5a5a765752bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Fri, 6 May 2016 01:53:54 +0200 Subject: [PATCH 10/12] Update version and build numbers to 4.0.1 --- HockeySDK-Source.podspec | 2 +- HockeySDK.podspec | 2 +- README.md | 8 ++++---- Support/buildnumber.xcconfig | 4 ++-- docs/Guide-Installation-Setup-template.md | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/HockeySDK-Source.podspec b/HockeySDK-Source.podspec index eb8bbb4f19..a9cca54380 100644 --- a/HockeySDK-Source.podspec +++ b/HockeySDK-Source.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'HockeySDK-Source' - s.version = '4.0.0' + s.version = '4.0.1' s.summary = 'Collect live crash reports, get feedback from your users, distribute your betas, and analyze your test coverage with HockeyApp.' s.description = <<-DESC diff --git a/HockeySDK.podspec b/HockeySDK.podspec index 89497e2f2c..b3bd03736a 100644 --- a/HockeySDK.podspec +++ b/HockeySDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'HockeySDK' - s.version = '4.0.0' + s.version = '4.0.1' s.summary = 'Collect live crash reports, get feedback from your users, distribute your betas, and analyze your test coverage with HockeyApp.' s.description = <<-DESC diff --git a/README.md b/README.md index f7f72931c5..756cea7bc8 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Version](http://cocoapod-badges.herokuapp.com/v/HockeySDK/badge.png)](http://cocoadocs.org/docsets/HockeySDK) -## Version 4.0.0 +## Version 4.0.1 -- [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.0.0/docs/docs/Changelog.html) +- [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.0.1/docs/docs/Changelog.html) ## Introduction @@ -494,7 +494,7 @@ To check if data is send properly to HockeyApp and also see some additional SDK ## 4. Documentation -Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/4.0.0/index.html). +Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/4.0.1/index.html). ## 5.Troubleshooting @@ -508,7 +508,7 @@ Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/ Make sure none of the following files are copied into your app bundle, check under app target, `Build Phases`, `Copy Bundle Resources` or in the `.app` bundle after building: - `HockeySDK.framework` (except if you build a dynamic framework version of the SDK yourself!) - - `de.bitstadium.HockeySDK-iOS-4.0.0.docset` + - `de.bitstadium.HockeySDK-iOS-4.0.1.docset` ### Feature are not working as expected diff --git a/Support/buildnumber.xcconfig b/Support/buildnumber.xcconfig index c036bda702..b6b68c6c57 100644 --- a/Support/buildnumber.xcconfig +++ b/Support/buildnumber.xcconfig @@ -1,7 +1,7 @@ #include "HockeySDK.xcconfig" -BUILD_NUMBER = 83 -VERSION_STRING = 4.0.0 +BUILD_NUMBER = 84 +VERSION_STRING = 4.0.1 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(HOCKEYSDK_GCC_PREPROCESSOR_DEFINITIONS) HOCKEYSDK_CONFIGURATION_$(CONFIGURATION) BITHOCKEY_VERSION="@\""$(VERSION_STRING)"\"" BITHOCKEY_BUILD="@\""$(BUILD_NUMBER)"\"" BITHOCKEY_C_VERSION="\""$(VERSION_STRING)"\"" BITHOCKEY_C_BUILD="\""$(BUILD_NUMBER)"\"" BIT_ARM_ARCHS = armv7 armv7s arm64 BIT_SIM_ARCHS = x86_64 i386 diff --git a/docs/Guide-Installation-Setup-template.md b/docs/Guide-Installation-Setup-template.md index b82f5d9b68..44bb9fd53a 100644 --- a/docs/Guide-Installation-Setup-template.md +++ b/docs/Guide-Installation-Setup-template.md @@ -1,6 +1,6 @@ -## Version 4.0.0 +## Version 4.0.1 -- [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.0.0/docs/docs/Changelog.html) +- [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.0.1/docs/docs/Changelog.html) ## Introduction @@ -474,7 +474,7 @@ To check if data is send properly to HockeyApp and also see some additional SDK ## 4. Documentation -Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/4.0.0/index.html). +Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/4.0.1/index.html). ## 5.Troubleshooting @@ -488,7 +488,7 @@ Our documentation can be found on [HockeyApp](http://hockeyapp.net/help/sdk/ios/ Make sure none of the following files are copied into your app bundle, check under app target, `Build Phases`, `Copy Bundle Resources` or in the `.app` bundle after building: - `HockeySDK.framework` (except if you build a dynamic framework version of the SDK yourself!) - - `de.bitstadium.HockeySDK-iOS-4.0.0.docset` + - `de.bitstadium.HockeySDK-iOS-4.0.1.docset` ### Feature are not working as expected From 61594c6d53912101de2c6cd5207e59eae463809e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Fri, 6 May 2016 01:56:13 +0200 Subject: [PATCH 11/12] Update Changelog --- docs/Changelog-template.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/Changelog-template.md b/docs/Changelog-template.md index 00d65efc38..93cd949c15 100644 --- a/docs/Changelog-template.md +++ b/docs/Changelog-template.md @@ -1,3 +1,20 @@ +## Version 4.0.1 + +- [BUGFIX] Fixes an issue where the whole app's Application Support directory was accidentally excluded from backups. +This SDK release explicitly includes the Application Support directory into backups. If you want to opt-out of this fix and keep the Application Directory's backup flag untouched, add the following line above the SDK setup code: + + - Objective-C: + ```objectivec + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"BITExcludeApplicationSupportFromBackup"]; + ``` + + - Swift: + ```swift + NSUserDefaults.standardUserDefaults().setBool(true, forKey: "BITExcludeApplicationSupportFromBackup") + ``` + +- [BUGFIX] Fixes an issue that prevented preparedItemsForFeedbackManager: delegate method from working + ## Version 4.0.0 - [NEW] Added official Carthage support From 63c9c7cf9e37c405feeb16abbeefbe6f22dbc186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Spie=C3=9F?= Date: Fri, 6 May 2016 02:58:58 +0200 Subject: [PATCH 12/12] Add note to Readme --- README.md | 4 ++++ docs/Guide-Installation-Setup-template.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 756cea7bc8..26e989329c 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ - [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.0.1/docs/docs/Changelog.html) +NOTE: With the release of HockeySDK 4.0.0-alpha.1 a bug was introduced which lead to the exclusion of the Application Support folder from iCloud and iTunes backups. + +If you have been using one of the affected versions (4.0.0-alpha.2, Version 4.0.0-beta.1, 4.0.0, 4.1.0-alpha.1, 4.1.0-alpha.2, or Version 4.1.0-beta.1), please make sure to update to at least version 4.0.1 or 4.1.0-beta.2 of our SDK as soon as you can. + ## Introduction HockeySDK-iOS implements support for using HockeyApp in your iOS applications. diff --git a/docs/Guide-Installation-Setup-template.md b/docs/Guide-Installation-Setup-template.md index 44bb9fd53a..f3634eec4f 100644 --- a/docs/Guide-Installation-Setup-template.md +++ b/docs/Guide-Installation-Setup-template.md @@ -2,6 +2,10 @@ - [Changelog](http://www.hockeyapp.net/help/sdk/ios/4.0.1/docs/docs/Changelog.html) +**NOTE:** With the release of HockeySDK 4.0.0-alpha.1 a bug was introduced which lead to the exclusion of the Application Support folder from iCloud and iTunes backups. + +If you have been using one of the affected versions (4.0.0-alpha.2, Version 4.0.0-beta.1, 4.0.0, 4.1.0-alpha.1, 4.1.0-alpha.2, or Version 4.1.0-beta.1), please make sure to update to at least version 4.0.1 or 4.1.0-beta.2 of our SDK as soon as you can. + ## Introduction This document contains the following sections: