Skip to content

Commit

Permalink
Update TNL to v2.2.0 (#7)
Browse files Browse the repository at this point in the history
* Update TNL to v2.2.0

- Fix buggy GZIP/DEFLATE content encoder
- Revise TNLRetryPolicyProvider to support updating the
request's config on retry
- drop iOS 7 support
- fix race condition with UIApplication background tasks for TNL
- clean up misc code
- improve misc logging
- fix tvOS, macOS, watchOS support
- add tvOS, watchOS targets
  • Loading branch information
NSProgrammer authored Aug 29, 2018
1 parent 64eda7b commit bf05c35
Show file tree
Hide file tree
Showing 52 changed files with 1,840 additions and 564 deletions.
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@

## Info

**Document version:** 2.0.2
**Document version:** 2.2.0

**Last updated:** 07/01/2018
**Last updated:** 08/27/2018

**Author:** Nolan O'Brien

## History

### 2.2.0
- drop iOS 7 support
- removes some compatibility check APIs that are no longer necessary too

### 2.1.0

- revise `TNLRetryPolicyProvider` interface to optionally provide a new `TNLRequestConfiguration` when retrying
- prior interface only permitted updating the _idleTimeout_ which was not sufficient for more use cases
- example use case: provide a custom content encoder that fails to encode a request, the retry policy and update the configuration to remove the custom content encoder to try again without encoding

### 2.0.2

- add demuxing support for `URLCache`, `URLCredentialStorage`, and `cookieStorage` on `TNLRequestConfiguration`
Expand Down
2 changes: 1 addition & 1 deletion Source/NSDictionary+TNLAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ - (nullable NSSet *)tnl_keysMatchingCaseInsensitiveKey:(NSString *)key
TNLAssert([key isKindOfClass:[NSString class]]);
if ([key isKindOfClass:[NSString class]]) {
for (NSString *otherKey in self.allKeys) {
TNLAssert([key isKindOfClass:[NSString class]]);
TNLAssert([otherKey isKindOfClass:[NSString class]]);
if ([otherKey caseInsensitiveCompare:key] == NSOrderedSame) { // TWITTER_STYLE_CASE_INSENSITIVE_COMPARE_NIL_PRECHECKED
if (!keys) {
keys = [NSMutableSet set];
Expand Down
6 changes: 1 addition & 5 deletions Source/NSHTTPCookieStorage+TNLAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ @interface TNLHTTPCookieStorageDemuxProxy : TNLSharedCookieStorageProxy
static TNLHTTPCookieStorageDemuxProxy *sProxy;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (@available(macOS 10.10, iOS 8, tvOS 9, watchOS 2, *)) {
sProxy = [TNLHTTPCookieStorageDemuxProxy alloc];
} else {
TNLAssertNever();
}
sProxy = [TNLHTTPCookieStorageDemuxProxy alloc];
});
return (id)sProxy;
}
Expand Down
21 changes: 7 additions & 14 deletions Source/NSOperationQueue+TNLSafety.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

#import "NSOperationQueue+TNLSafety.h"
#import "TNL_Project.h"

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -17,8 +18,6 @@ @interface TNLOperationSafetyGuard : NSObject
- (void)addOperation:(NSOperation *)op;
- (NSSet *)operations;
+ (nullable instancetype)operationSafetyGuard;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)new NS_UNAVAILABLE;
@end

@implementation NSOperationQueue (TNLSafety)
Expand All @@ -45,26 +44,20 @@ + (nullable instancetype)operationSafetyGuard
static TNLOperationSafetyGuard *sGuard = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSOperatingSystemVersion version = { 7, 0, 0 }; /* arbitrarily default as iOS 7 (minimum version for TNL) */
if ([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)]) {
version = [NSProcessInfo processInfo].operatingSystemVersion;
}
if (version.majorVersion < 9) {
sGuard = [[TNLOperationSafetyGuard alloc] initWithOperationSystemVersion:version];
if (tnl_available_ios_9) {
// no guard needed
} else {
sGuard = [[TNLOperationSafetyGuard alloc] init];
}
});
return sGuard;
}

- (instancetype)initWithOperationSystemVersion:(NSOperatingSystemVersion)version
- (instancetype)init
{
if (self = [super init]) {
_operations = [[NSMutableSet alloc] init];
dispatch_queue_attr_t queueAttr = DISPATCH_QUEUE_SERIAL;
if (version.majorVersion >= 8) {
queueAttr = dispatch_queue_attr_make_with_qos_class(queueAttr, QOS_CLASS_BACKGROUND, 0);
}
_queue = dispatch_queue_create("NSOperationQueue.tnl.safety", queueAttr);
_queue = dispatch_queue_create("NSOperationQueue.tnl.safety", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0));
}
return self;
}
Expand Down
6 changes: 1 addition & 5 deletions Source/NSURLCredentialStorage+TNLAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,7 @@ + (NSURLCredentialStorage *)tnl_sharedCredentialStorageProxy
static TNLSharedCredentialStorageProxy *sProxy;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (@available(macOS 10.10, iOS 8, tvOS 9, watchOS 2, *)) {
sProxy = [TNLSharedCredentialStorageProxy alloc];
} else {
TNLAssertNever();
}
sProxy = [TNLSharedCredentialStorageProxy alloc];
});
return (id)sProxy;
}
Expand Down
36 changes: 0 additions & 36 deletions Source/NSURLSessionConfiguration+TNLAdditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
@interface NSURLSessionConfiguration (TNLAdditions)

/**
Cement the provided _config_.
`NSURLSessionConfiguration` objects, by default, lazily generate certain properties.
These lazy generated values are not persisted in the underlying ivar though and subsequent calls to
those properties will yield new objects.
`tnl_cementConfiguration:` provides a mechanism to avoid this implementation detail by sending the
return value for a property's getter to its setter. This effectively caches that property for
reuse and "cements" that property. Since `NSURLSessionConfiguration` is a class cluster, we
provide the functionality with a class method instead of an instance method.
The properties are `URLCache`, `URLCredentialStorage` and `HTTPCookieStorage`
@discussion __See Also:__ `[NSURLSessionConfiguration URLCache]`,
`[NSURLSessionConfiguration URLCredentialStorage]` and
`[NSURLSessionConfiguration HTTPCookieStorage]`
@note The issue of needing to cement these properties was fixed in __iOS 8__ so this method is a
no-op on iOS 8+.
*/
+ (void)tnl_cementConfiguration:(NSURLSessionConfiguration *)config;

/**
Unifies between the two constructors for background session configurations on iOS 7 and iOS 8.
__See Also:__ `[NSURLSessionConfiguration backgroundSessionConfiguration:]` (iOS 7) and
`[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:]` (iOS 8+)
*/
+ (instancetype)tnl_backgroundSessionConfigurationWithIdentifier:(NSString *)identifier;

/**
Returns `YES` if `NSURLSessionConfiguration` supports the shared container identifier.
Since `NSURLSessionConfiguration` is a class cluster, you can't use `instancesRespondToSelector:`
*/
+ (BOOL)tnl_supportsSharedContainerIdentifier;

/**
Returns `NO` if the current OS has a critical bug where
`URLSession:dataTask:didReceiveResponse:completionHandler:` being implemented in a delegate while
Expand Down
146 changes: 34 additions & 112 deletions Source/NSURLSessionConfiguration+TNLAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,47 +13,20 @@

NS_ASSUME_NONNULL_BEGIN

#if TARGET_OS_IPHONE
#define TNL_URL_SESSION_CONFIG_LINKS_DEPRECATED_INIT_WITH_IDENTIFIER_METHOD (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0)
#elif TARGET_OS_MAC
#define TNL_URL_SESSION_CONFIG_LINKS_DEPRECATED_INIT_WITH_IDENTIFIER_METHOD (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_10)
#else
#define TNL_URL_SESSION_CONFIG_LINKS_DEPRECATED_INIT_WITH_IDENTIFIER_METHOD (0)
#endif

static BOOL _NSURLSessionConfigurationHasUncementedPersistenceValues(void);

@implementation NSURLSessionConfiguration (TNLAdditions)

+ (BOOL)tnl_supportsSharedContainerIdentifier
{
static BOOL sSupported;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Get the default config and check if it responds
NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sSupported = [defaultConfig respondsToSelector:@selector(setSharedContainerIdentifier:)];
});
return sSupported;
}

+ (BOOL)tnl_URLSessionCanReceiveResponseViaDelegate
{
static BOOL sBugExists;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

NSProcessInfo *processInfo = [NSProcessInfo processInfo];
const NSOperatingSystemVersion OSVersion = [processInfo respondsToSelector:@selector(operatingSystemVersion)] ? processInfo.operatingSystemVersion : (NSOperatingSystemVersion){ 0, 0, 0 };

#if TARGET_OS_IPHONE
sBugExists = (OSVersion.majorVersion == 8);
#elif TARGET_OS_OSX
sBugExists = (OSVersion.majorVersion == 10 && OSVersion.minorVersion == 10);
#else
(void)OSVersion;
sBugExists = NO;
#endif
if (tnl_available_ios_9) {
// ok
} else {
// iOS 8 only has this bug
sBugExists = YES;
}

});

Expand All @@ -66,86 +39,37 @@ + (BOOL)tnl_URLSessionCanUseTaskTransactionMetrics
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

NSProcessInfo *processInfo = [NSProcessInfo processInfo];
if (![processInfo respondsToSelector:@selector(operatingSystemVersion)]) {
// version is too low
return;
}

const NSOperatingSystemVersion OSVersion = processInfo.operatingSystemVersion;
#if TARGET_OS_IPHONE
if (OSVersion.majorVersion < 10) {
// task metrics don't exist
} else if (OSVersion.majorVersion == 10) {
// iOS 10.0.X and iOS 10.1.X have a crashing bug that crashes 1 million times a day
// iOS 10.2+ has a different crasher, 5 thousand per day
} else if (OSVersion.majorVersion == 11) {
// fixed in iOS 11 ...
if (OSVersion.minorVersion == 0 && OSVersion.patchVersion == 0) {
// ... but some betas still had the crash, so let's require the first patch revision
} else {
// definitely fixed on 11.0.1 and up
if (tnl_available_ios_10) {
// task metrics added as an API in iOS 10

if (tnl_available_ios_11) {
// Crashers of iOS 10 continue into iOS 11 betas
// Cannot differentiate iOS 11.0.0 and iOS 11 betas
// So...
// On iOS 11.0.1, consider fixed
// On iOS 11.0.0, consider bug present
// On non-iOS targets, just presume non-beta and consider fixed
#if TARGET_OS_IOS
if (@available(iOS 11.0.1, *)) {
// definitely fixed on iOS 11.0.1
sTaskMetricsAvailable = YES;
}
#else
// non-iOS, consider fixed
sTaskMetricsAvailable = YES;
}
} else {
// newer releases
sTaskMetricsAvailable = YES;
}
#elif TARGET_OS_OSX
if (OSVersion.majorVersion < 10) {
// task metrics don't exist
} else if (OSVersion.majorVersion == 10) {
if (OSVersion.minorVersion < 12) {
// task metrics don't exist
} else if (OSVersion.minorVersion == 12) {
// macOS 10.12.0 and macOS 10.12.1 has a bad crasher
// 10.12.2+ has a different (less severe) crasher
#endif
} else {
// finally fixed in macOS 10.13 GM
sTaskMetricsAvailable = YES;
// task metrics exist but have crashes on iOS 10.X
// iOS 10.0.X and iOS 10.1.X have a crashing bug that crashes 1 million times a day
// iOS 10.2+ has a different crasher, 5 thousand per day
}
} else {
// macOS 11 (if this every happens)
sTaskMetricsAvailable = YES;
}
#else
// Unexpected target, assume it cannot be used
(void)OSVersion;
sTaskMetricsAvailable = NO;
#endif

});

return sTaskMetricsAvailable;
}

+ (void)tnl_cementConfiguration:(NSURLSessionConfiguration *)config
{
if (config && _NSURLSessionConfigurationHasUncementedPersistenceValues()) {
// On iOS 7, Apple made a design choice with the URLCache, URLCredentialStorage and HTTPCookieStorage properties
// Those getters would lazily load their objects but would not populate the ivar.
// This meant that multiple calls to the same getter would yield different objects.
// When the setter is called, however, the ivar would be persistently set (including with nil).
// On iOS 8, Apple (thankfully) relented on this design choice and lazy loading will now populate the ivar.
config.URLCache = config.URLCache;
config.URLCredentialStorage = config.URLCredentialStorage;
config.HTTPCookieStorage = config.HTTPCookieStorage;
}
}

+ (instancetype)tnl_backgroundSessionConfigurationWithIdentifier:(NSString *)identifier
{
#if TNL_URL_SESSION_CONFIG_LINKS_DEPRECATED_INIT_WITH_IDENTIFIER_METHOD
if ([self tnl_supportsSharedContainerIdentifier] /* proxy for support of new constructor */) {
return [[self class] backgroundSessionConfigurationWithIdentifier:identifier];
} else {
return [[self class] backgroundSessionConfiguration:identifier];
}
#else
return [[self class] backgroundSessionConfigurationWithIdentifier:identifier];
#endif
}

+ (BOOL)tnl_URLSessionSupportsDecodingBrotliContentEncoding
{
static BOOL sBrotliSupported = NO;
Expand All @@ -158,9 +82,14 @@ + (BOOL)tnl_URLSessionSupportsDecodingBrotliContentEncoding
return;
}

// Brotli support requires 2 things:
// - Running OS version of iOS 11 (or equivalent platform version) or greater
// AND
// - Target SDK at compile time of iOS 11 (or equivalent platform version) or greater

const NSOperatingSystemVersion OSVersion = processInfo.operatingSystemVersion;
(void)OSVersion;
#if TARGET_OS_IPHONE
#if TARGET_OS_IOS
#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
if (OSVersion.majorVersion >= 11) {
sBrotliSupported = YES;
Expand Down Expand Up @@ -203,7 +132,7 @@ @implementation NSURLSessionConfiguration (TaggedIdentifier)

+ (instancetype)tnl_backgroundSessionConfigurationWithTaggedIdentifier:(NSString *)identifier
{
NSURLSessionConfiguration *config = [self tnl_backgroundSessionConfigurationWithIdentifier:identifier];
NSURLSessionConfiguration *config = [self backgroundSessionConfigurationWithIdentifier:identifier];
TNLRequestConfiguration *configParams = [TNLRequestConfiguration parseConfigurationFromIdentifier:identifier];
if (configParams) {
[configParams applySettingsToSessionConfiguration:config];
Expand All @@ -213,11 +142,4 @@ + (instancetype)tnl_backgroundSessionConfigurationWithTaggedIdentifier:(NSString

@end

static BOOL _NSURLSessionConfigurationHasUncementedPersistenceValues()
{
// iOS 7 has this issue, which we'll use the container identifier for
// (to also match on other platforms like macOS)
return ![NSURLSessionConfiguration tnl_supportsSharedContainerIdentifier];
}

NS_ASSUME_NONNULL_END
Loading

0 comments on commit bf05c35

Please sign in to comment.