Commit 17d6edd1 by zhukai

上传项目

parent d626f4ab

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

platform :ios, '8.0'
target 'UniversalApp' do
pod 'MJRefresh'
pod 'AFNetworking', '~> 3.0'
pod 'YYKit'
pod 'MBProgressHUD', '~> 1.0.0'
pod 'Masonry'
#pod 'NIMSDK', '~> 3.7.0'
pod 'YTKNetwork'
pod 'SDWebImage', '~>3.8'
pod 'IQKeyboardManager'
pod 'Qiniu'
pod 'LBXScan/LBXNative','~> 2.3'
pod 'LBXScan/UI','~> 2.3'
pod 'PNChart'
pod 'JPush'
end
PODS:
- AFNetworking (3.2.0):
- AFNetworking/NSURLSession (= 3.2.0)
- AFNetworking/Reachability (= 3.2.0)
- AFNetworking/Security (= 3.2.0)
- AFNetworking/Serialization (= 3.2.0)
- AFNetworking/UIKit (= 3.2.0)
- AFNetworking/NSURLSession (3.2.0):
- AFNetworking/Reachability
- AFNetworking/Security
- AFNetworking/Serialization
- AFNetworking/Reachability (3.2.0)
- AFNetworking/Security (3.2.0)
- AFNetworking/Serialization (3.2.0)
- AFNetworking/UIKit (3.2.0):
- AFNetworking/NSURLSession
- HappyDNS (0.3.12)
- IQKeyboardManager (5.0.8)
- JCore (1.1.7)
- JPush (3.0.8):
- JCore (~> 1.1.7)
- LBXScan/LBXNative (2.3):
- LBXScan/Types (~> 2.2)
- LBXScan/Types (2.3)
- LBXScan/UI (2.3):
- LBXScan/Types (~> 2.2)
- Masonry (1.1.0)
- MBProgressHUD (1.0.0)
- MJRefresh (3.1.15.3)
- PNChart (0.8.9):
- UICountingLabel (~> 1.2.0)
- Qiniu (7.2.3):
- HappyDNS (~> 0.3)
- SDWebImage (3.8.2):
- SDWebImage/Core (= 3.8.2)
- SDWebImage/Core (3.8.2)
- UICountingLabel (1.2.0)
- YTKNetwork (2.0.4):
- AFNetworking (~> 3.0)
- YYKit (1.0.9):
- YYKit/no-arc (= 1.0.9)
- YYKit/no-arc (1.0.9)
DEPENDENCIES:
- AFNetworking (~> 3.0)
- IQKeyboardManager
- JPush
- LBXScan/LBXNative (~> 2.3)
- LBXScan/UI (~> 2.3)
- Masonry
- MBProgressHUD (~> 1.0.0)
- MJRefresh
- PNChart
- Qiniu
- SDWebImage (~> 3.8)
- YTKNetwork
- YYKit
SPEC CHECKSUMS:
AFNetworking: 8ac6017b94ea105479f7776e5288e48ae9c59bb4
HappyDNS: 691a3041128055d09866f86e6e9ca90f24da1f18
IQKeyboardManager: 266cb7b1d11f94a3e71229d77f960f9a1b538e5b
JCore: f7459c81c135281a874a6beafdd321666adcc834
JPush: 9574d072cc37e93fa293ba69b43ce763c440943a
LBXScan: e51449f0832d1fe17da632af0d22adeb3cfa3543
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
MBProgressHUD: 4890f671c94e8a0f3cf959aa731e9de2f036d71a
MJRefresh: b48380ae2b927b46c4ef000de9adb8dc748e1df7
PNChart: 84774d225c2126ded6c93d4dbe6ae98c3a73c2d2
Qiniu: ae0fea1cc379fae1560d8748ac537e0fbeb8626e
SDWebImage: 098e97e6176540799c27e804c96653ee0833d13c
UICountingLabel: 1db4e7d023e1762171eb226d6dff47a7a84f27aa
YTKNetwork: df8a7a5597bda1664a4efaa7ef871a818a887fe0
YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7
PODFILE CHECKSUM: b1f7db49afde0569e1574b2078d69f9b0192be0e
COCOAPODS: 1.4.0
// AFNetworkReachabilityManager.h
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#if !TARGET_OS_WATCH
#import <SystemConfiguration/SystemConfiguration.h>
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,
AFNetworkReachabilityStatusNotReachable = 0,
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
NS_ASSUME_NONNULL_BEGIN
/**
`AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces.
Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability.
See Apple's Reachability Sample Code ( https://developer.apple.com/library/ios/samplecode/reachability/ )
@warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined.
*/
@interface AFNetworkReachabilityManager : NSObject
/**
The current network reachability status.
*/
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
/**
Whether or not the network is currently reachable.
*/
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
/**
Whether or not the network is currently reachable via WWAN.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
/**
Whether or not the network is currently reachable via WiFi.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
///---------------------
/// @name Initialization
///---------------------
/**
Returns the shared network reachability manager.
*/
+ (instancetype)sharedManager;
/**
Creates and returns a network reachability manager with the default socket address.
@return An initialized network reachability manager, actively monitoring the default socket address.
*/
+ (instancetype)manager;
/**
Creates and returns a network reachability manager for the specified domain.
@param domain The domain used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified domain.
*/
+ (instancetype)managerForDomain:(NSString *)domain;
/**
Creates and returns a network reachability manager for the socket address.
@param address The socket address (`sockaddr_in6`) used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified socket address.
*/
+ (instancetype)managerForAddress:(const void *)address;
/**
Initializes an instance of a network reachability manager from the specified reachability object.
@param reachability The reachability object to monitor.
@return An initialized network reachability manager, actively monitoring the specified reachability.
*/
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
/**
* Initializes an instance of a network reachability manager
*
* @return nil as this method is unavailable
*/
- (nullable instancetype)init NS_UNAVAILABLE;
///--------------------------------------------------
/// @name Starting & Stopping Reachability Monitoring
///--------------------------------------------------
/**
Starts monitoring for changes in network reachability status.
*/
- (void)startMonitoring;
/**
Stops monitoring for changes in network reachability status.
*/
- (void)stopMonitoring;
///-------------------------------------------------
/// @name Getting Localized Reachability Description
///-------------------------------------------------
/**
Returns a localized string representation of the current network reachability status.
*/
- (NSString *)localizedNetworkReachabilityStatusString;
///---------------------------------------------------
/// @name Setting Network Reachability Change Callback
///---------------------------------------------------
/**
Sets a callback to be executed when the network availability of the `baseURL` host changes.
@param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`.
*/
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
@end
///----------------
/// @name Constants
///----------------
/**
## Network Reachability
The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses.
enum {
AFNetworkReachabilityStatusUnknown,
AFNetworkReachabilityStatusNotReachable,
AFNetworkReachabilityStatusReachableViaWWAN,
AFNetworkReachabilityStatusReachableViaWiFi,
}
`AFNetworkReachabilityStatusUnknown`
The `baseURL` host reachability is not known.
`AFNetworkReachabilityStatusNotReachable`
The `baseURL` host cannot be reached.
`AFNetworkReachabilityStatusReachableViaWWAN`
The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS.
`AFNetworkReachabilityStatusReachableViaWiFi`
The `baseURL` host can be reached via a Wi-Fi connection.
### Keys for Notification UserInfo Dictionary
Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification.
`AFNetworkingReachabilityNotificationStatusItem`
A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification.
The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status.
*/
///--------------------
/// @name Notifications
///--------------------
/**
Posted when network reachability changes.
This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability.
@warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (`Prefix.pch`).
*/
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
///--------------------
/// @name Functions
///--------------------
/**
Returns a localized string representation of an `AFNetworkReachabilityStatus` value.
*/
FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
NS_ASSUME_NONNULL_END
#endif
// AFNetworkReachabilityManager.m
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "AFNetworkReachabilityManager.h"
#if !TARGET_OS_WATCH
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil);
case AFNetworkReachabilityStatusReachableViaWWAN:
return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil);
case AFNetworkReachabilityStatusReachableViaWiFi:
return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil);
case AFNetworkReachabilityStatusUnknown:
default:
return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil);
}
}
static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
if (isNetworkReachable == NO) {
status = AFNetworkReachabilityStatusNotReachable;
}
#if TARGET_OS_IPHONE
else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
status = AFNetworkReachabilityStatusReachableViaWWAN;
}
#endif
else {
status = AFNetworkReachabilityStatusReachableViaWiFi;
}
return status;
}
/**
* Queue a status change notification for the main thread.
*
* This is done to ensure that the notifications are received in the same order
* as they are sent. If notifications are sent directly, it is possible that
* a queued notification (for an earlier status condition) is processed after
* the later update, resulting in the listener being left in the wrong state.
*/
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block(status);
}
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
});
}
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
return Block_copy(info);
}
static void AFNetworkReachabilityReleaseCallback(const void *info) {
if (info) {
Block_release(info);
}
}
@interface AFNetworkReachabilityManager ()
@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
@end
@implementation AFNetworkReachabilityManager
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager];
});
return _sharedManager;
}
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)manager
{
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
struct sockaddr_in6 address;
bzero(&address, sizeof(address));
address.sin6_len = sizeof(address);
address.sin6_family = AF_INET6;
#else
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
return [self managerForAddress:&address];
}
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}
- (instancetype)init NS_UNAVAILABLE
{
return nil;
}
- (void)dealloc {
[self stopMonitoring];
if (_networkReachability != NULL) {
CFRelease(_networkReachability);
}
}
#pragma mark -
- (BOOL)isReachable {
return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}
- (BOOL)isReachableViaWWAN {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;
}
- (BOOL)isReachableViaWiFi {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;
}
#pragma mark -
- (void)startMonitoring {
[self stopMonitoring];
if (!self.networkReachability) {
return;
}
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
}
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
#pragma mark -
- (NSString *)localizedNetworkReachabilityStatusString {
return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
}
#pragma mark -
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
self.networkReachabilityStatusBlock = block;
}
#pragma mark - NSKeyValueObserving
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
return [NSSet setWithObject:@"networkReachabilityStatus"];
}
return [super keyPathsForValuesAffectingValueForKey:key];
}
@end
#endif
// AFNetworking.h
//
// Copyright (c) 2013 AFNetworking (http://afnetworking.com/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <Availability.h>
#import <TargetConditionals.h>
#ifndef _AFNETWORKING_
#define _AFNETWORKING_
#import "AFURLRequestSerialization.h"
#import "AFURLResponseSerialization.h"
#import "AFSecurityPolicy.h"
#if !TARGET_OS_WATCH
#import "AFNetworkReachabilityManager.h"
#endif
#import "AFURLSessionManager.h"
#import "AFHTTPSessionManager.h"
#endif /* _AFNETWORKING_ */
// AFSecurityPolicy.h
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <Security/Security.h>
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
};
/**
`AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections.
Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled.
*/
NS_ASSUME_NONNULL_BEGIN
@interface AFSecurityPolicy : NSObject <NSSecureCoding, NSCopying>
/**
The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`.
*/
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
/**
The certificates used to evaluate server trust according to the SSL pinning mode.
By default, this property is set to any (`.cer`) certificates included in the target compiling AFNetworking. Note that if you are using AFNetworking as embedded framework, no certificates will be pinned by default. Use `certificatesInBundle` to load certificates from your target, and then create a new policy by calling `policyWithPinningMode:withPinnedCertificates`.
Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches.
*/
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
/**
Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`.
*/
@property (nonatomic, assign) BOOL allowInvalidCertificates;
/**
Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`.
*/
@property (nonatomic, assign) BOOL validatesDomainName;
///-----------------------------------------
/// @name Getting Certificates from the Bundle
///-----------------------------------------
/**
Returns any certificates included in the bundle. If you are using AFNetworking as an embedded framework, you must use this method to find the certificates you have included in your app bundle, and use them when creating your security policy by calling `policyWithPinningMode:withPinnedCertificates`.
@return The certificates included in the given bundle.
*/
+ (NSSet <NSData *> *)certificatesInBundle:(NSBundle *)bundle;
///-----------------------------------------
/// @name Getting Specific Security Policies
///-----------------------------------------
/**
Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys.
@return The default security policy.
*/
+ (instancetype)defaultPolicy;
///---------------------
/// @name Initialization
///---------------------
/**
Creates and returns a security policy with the specified pinning mode.
@param pinningMode The SSL pinning mode.
@return A new security policy.
*/
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode;
/**
Creates and returns a security policy with the specified pinning mode.
@param pinningMode The SSL pinning mode.
@param pinnedCertificates The certificates to pin against.
@return A new security policy.
*/
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet <NSData *> *)pinnedCertificates;
///------------------------------
/// @name Evaluating Server Trust
///------------------------------
/**
Whether or not the specified server trust should be accepted, based on the security policy.
This method should be used when responding to an authentication challenge from a server.
@param serverTrust The X.509 certificate trust of the server.
@param domain The domain of serverTrust. If `nil`, the domain will not be validated.
@return Whether or not to trust the server.
*/
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(nullable NSString *)domain;
@end
NS_ASSUME_NONNULL_END
///----------------
/// @name Constants
///----------------
/**
## SSL Pinning Modes
The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes.
enum {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
}
`AFSSLPinningModeNone`
Do not used pinned certificates to validate servers.
`AFSSLPinningModePublicKey`
Validate host certificates against public keys of pinned certificates.
`AFSSLPinningModeCertificate`
Validate host certificates against pinned certificates.
*/
Copyright (c) 2011-2016 Alamofire Software Foundation (http://alamofire.org/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
// AFAutoPurgingImageCache.h
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <TargetConditionals.h>
#import <Foundation/Foundation.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
The `AFImageCache` protocol defines a set of APIs for adding, removing and fetching images from a cache synchronously.
*/
@protocol AFImageCache <NSObject>
/**
Adds the image to the cache with the given identifier.
@param image The image to cache.
@param identifier The unique identifier for the image in the cache.
*/
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier;
/**
Removes the image from the cache matching the given identifier.
@param identifier The unique identifier for the image in the cache.
@return A BOOL indicating whether or not the image was removed from the cache.
*/
- (BOOL)removeImageWithIdentifier:(NSString *)identifier;
/**
Removes all images from the cache.
@return A BOOL indicating whether or not all images were removed from the cache.
*/
- (BOOL)removeAllImages;
/**
Returns the image in the cache associated with the given identifier.
@param identifier The unique identifier for the image in the cache.
@return An image for the matching identifier, or nil.
*/
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier;
@end
/**
The `ImageRequestCache` protocol extends the `ImageCache` protocol by adding methods for adding, removing and fetching images from a cache given an `NSURLRequest` and additional identifier.
*/
@protocol AFImageRequestCache <AFImageCache>
/**
Asks if the image should be cached using an identifier created from the request and additional identifier.
@param image The image to be cached.
@param request The unique URL request identifing the image asset.
@param identifier The additional identifier to apply to the URL request to identify the image.
@return A BOOL indicating whether or not the image should be added to the cache. YES will cache, NO will prevent caching.
*/
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
/**
Adds the image to the cache using an identifier created from the request and additional identifier.
@param image The image to cache.
@param request The unique URL request identifing the image asset.
@param identifier The additional identifier to apply to the URL request to identify the image.
*/
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
/**
Removes the image from the cache using an identifier created from the request and additional identifier.
@param request The unique URL request identifing the image asset.
@param identifier The additional identifier to apply to the URL request to identify the image.
@return A BOOL indicating whether or not all images were removed from the cache.
*/
- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
/**
Returns the image from the cache associated with an identifier created from the request and additional identifier.
@param request The unique URL request identifing the image asset.
@param identifier The additional identifier to apply to the URL request to identify the image.
@return An image for the matching request and identifier, or nil.
*/
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
@end
/**
The `AutoPurgingImageCache` in an in-memory image cache used to store images up to a given memory capacity. When the memory capacity is reached, the image cache is sorted by last access date, then the oldest image is continuously purged until the preferred memory usage after purge is met. Each time an image is accessed through the cache, the internal access date of the image is updated.
*/
@interface AFAutoPurgingImageCache : NSObject <AFImageRequestCache>
/**
The total memory capacity of the cache in bytes.
*/
@property (nonatomic, assign) UInt64 memoryCapacity;
/**
The preferred memory usage after purge in bytes. During a purge, images will be purged until the memory capacity drops below this limit.
*/
@property (nonatomic, assign) UInt64 preferredMemoryUsageAfterPurge;
/**
The current total memory usage in bytes of all images stored within the cache.
*/
@property (nonatomic, assign, readonly) UInt64 memoryUsage;
/**
Initialies the `AutoPurgingImageCache` instance with default values for memory capacity and preferred memory usage after purge limit. `memoryCapcity` defaults to `100 MB`. `preferredMemoryUsageAfterPurge` defaults to `60 MB`.
@return The new `AutoPurgingImageCache` instance.
*/
- (instancetype)init;
/**
Initialies the `AutoPurgingImageCache` instance with the given memory capacity and preferred memory usage
after purge limit.
@param memoryCapacity The total memory capacity of the cache in bytes.
@param preferredMemoryCapacity The preferred memory usage after purge in bytes.
@return The new `AutoPurgingImageCache` instance.
*/
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity;
@end
NS_ASSUME_NONNULL_END
#endif
// AFAutoPurgingImageCache.m
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <TargetConditionals.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import "AFAutoPurgingImageCache.h"
@interface AFCachedImage : NSObject
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) NSString *identifier;
@property (nonatomic, assign) UInt64 totalBytes;
@property (nonatomic, strong) NSDate *lastAccessDate;
@property (nonatomic, assign) UInt64 currentMemoryUsage;
@end
@implementation AFCachedImage
-(instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier {
if (self = [self init]) {
self.image = image;
self.identifier = identifier;
CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
CGFloat bytesPerPixel = 4.0;
CGFloat bytesPerSize = imageSize.width * imageSize.height;
self.totalBytes = (UInt64)bytesPerPixel * (UInt64)bytesPerSize;
self.lastAccessDate = [NSDate date];
}
return self;
}
- (UIImage*)accessImage {
self.lastAccessDate = [NSDate date];
return self.image;
}
- (NSString *)description {
NSString *descriptionString = [NSString stringWithFormat:@"Idenfitier: %@ lastAccessDate: %@ ", self.identifier, self.lastAccessDate];
return descriptionString;
}
@end
@interface AFAutoPurgingImageCache ()
@property (nonatomic, strong) NSMutableDictionary <NSString* , AFCachedImage*> *cachedImages;
@property (nonatomic, assign) UInt64 currentMemoryUsage;
@property (nonatomic, strong) dispatch_queue_t synchronizationQueue;
@end
@implementation AFAutoPurgingImageCache
- (instancetype)init {
return [self initWithMemoryCapacity:100 * 1024 * 1024 preferredMemoryCapacity:60 * 1024 * 1024];
}
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity {
if (self = [super init]) {
self.memoryCapacity = memoryCapacity;
self.preferredMemoryUsageAfterPurge = preferredMemoryCapacity;
self.cachedImages = [[NSMutableDictionary alloc] init];
NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]];
self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(removeAllImages)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (UInt64)memoryUsage {
__block UInt64 result = 0;
dispatch_sync(self.synchronizationQueue, ^{
result = self.currentMemoryUsage;
});
return result;
}
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier {
dispatch_barrier_async(self.synchronizationQueue, ^{
AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier];
AFCachedImage *previousCachedImage = self.cachedImages[identifier];
if (previousCachedImage != nil) {
self.currentMemoryUsage -= previousCachedImage.totalBytes;
}
self.cachedImages[identifier] = cacheImage;
self.currentMemoryUsage += cacheImage.totalBytes;
});
dispatch_barrier_async(self.synchronizationQueue, ^{
if (self.currentMemoryUsage > self.memoryCapacity) {
UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge;
NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate"
ascending:YES];
[sortedImages sortUsingDescriptors:@[sortDescriptor]];
UInt64 bytesPurged = 0;
for (AFCachedImage *cachedImage in sortedImages) {
[self.cachedImages removeObjectForKey:cachedImage.identifier];
bytesPurged += cachedImage.totalBytes;
if (bytesPurged >= bytesToPurge) {
break ;
}
}
self.currentMemoryUsage -= bytesPurged;
}
});
}
- (BOOL)removeImageWithIdentifier:(NSString *)identifier {
__block BOOL removed = NO;
dispatch_barrier_sync(self.synchronizationQueue, ^{
AFCachedImage *cachedImage = self.cachedImages[identifier];
if (cachedImage != nil) {
[self.cachedImages removeObjectForKey:identifier];
self.currentMemoryUsage -= cachedImage.totalBytes;
removed = YES;
}
});
return removed;
}
- (BOOL)removeAllImages {
__block BOOL removed = NO;
dispatch_barrier_sync(self.synchronizationQueue, ^{
if (self.cachedImages.count > 0) {
[self.cachedImages removeAllObjects];
self.currentMemoryUsage = 0;
removed = YES;
}
});
return removed;
}
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier {
__block UIImage *image = nil;
dispatch_sync(self.synchronizationQueue, ^{
AFCachedImage *cachedImage = self.cachedImages[identifier];
image = [cachedImage accessImage];
});
return image;
}
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
[self addImage:image withIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}
- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
return [self removeImageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
return [self imageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}
- (NSString *)imageCacheKeyFromURLRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)additionalIdentifier {
NSString *key = request.URL.absoluteString;
if (additionalIdentifier != nil) {
key = [key stringByAppendingString:additionalIdentifier];
}
return key;
}
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier {
return YES;
}
@end
#endif
// AFImageDownloader.h
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <TargetConditionals.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <Foundation/Foundation.h>
#import "AFAutoPurgingImageCache.h"
#import "AFHTTPSessionManager.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
AFImageDownloadPrioritizationFIFO,
AFImageDownloadPrioritizationLIFO
};
/**
The `AFImageDownloadReceipt` is an object vended by the `AFImageDownloader` when starting a data task. It can be used to cancel active tasks running on the `AFImageDownloader` session. As a general rule, image data tasks should be cancelled using the `AFImageDownloadReceipt` instead of calling `cancel` directly on the `task` itself. The `AFImageDownloader` is optimized to handle duplicate task scenarios as well as pending versus active downloads.
*/
@interface AFImageDownloadReceipt : NSObject
/**
The data task created by the `AFImageDownloader`.
*/
@property (nonatomic, strong) NSURLSessionDataTask *task;
/**
The unique identifier for the success and failure blocks when duplicate requests are made.
*/
@property (nonatomic, strong) NSUUID *receiptID;
@end
/** The `AFImageDownloader` class is responsible for downloading images in parallel on a prioritized queue. Incoming downloads are added to the front or back of the queue depending on the download prioritization. Each downloaded image is cached in the underlying `NSURLCache` as well as the in-memory image cache. By default, any download request with a cached image equivalent in the image cache will automatically be served the cached image representation.
*/
@interface AFImageDownloader : NSObject
/**
The image cache used to store all downloaded images in. `AFAutoPurgingImageCache` by default.
*/
@property (nonatomic, strong, nullable) id <AFImageRequestCache> imageCache;
/**
The `AFHTTPSessionManager` used to download images. By default, this is configured with an `AFImageResponseSerializer`, and a shared `NSURLCache` for all image downloads.
*/
@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;
/**
Defines the order prioritization of incoming download requests being inserted into the queue. `AFImageDownloadPrioritizationFIFO` by default.
*/
@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritizaton;
/**
The shared default instance of `AFImageDownloader` initialized with default values.
*/
+ (instancetype)defaultInstance;
/**
Creates a default `NSURLCache` with common usage parameter values.
@returns The default `NSURLCache` instance.
*/
+ (NSURLCache *)defaultURLCache;
/**
The default `NSURLSessionConfiguration` with common usage parameter values.
*/
+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration;
/**
Default initializer
@return An instance of `AFImageDownloader` initialized with default values.
*/
- (instancetype)init;
/**
Initializer with specific `URLSessionConfiguration`
@param configuration The `NSURLSessionConfiguration` to be be used
@return An instance of `AFImageDownloader` initialized with default values and custom `NSURLSessionConfiguration`
*/
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration;
/**
Initializes the `AFImageDownloader` instance with the given session manager, download prioritization, maximum active download count and image cache.
@param sessionManager The session manager to use to download images.
@param downloadPrioritization The download prioritization of the download queue.
@param maximumActiveDownloads The maximum number of active downloads allowed at any given time. Recommend `4`.
@param imageCache The image cache used to store all downloaded images in.
@return The new `AFImageDownloader` instance.
*/
- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager
downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization
maximumActiveDownloads:(NSInteger)maximumActiveDownloads
imageCache:(nullable id <AFImageRequestCache>)imageCache;
/**
Creates a data task using the `sessionManager` instance for the specified URL request.
If the same data task is already in the queue or currently being downloaded, the success and failure blocks are
appended to the already existing task. Once the task completes, all success or failure blocks attached to the
task are executed in the order they were added.
@param request The URL request.
@param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
@param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
@return The image download receipt for the data task if available. `nil` if the image is stored in the cache.
cache and the URL request cache policy allows the cache to be used.
*/
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
/**
Creates a data task using the `sessionManager` instance for the specified URL request.
If the same data task is already in the queue or currently being downloaded, the success and failure blocks are
appended to the already existing task. Once the task completes, all success or failure blocks attached to the
task are executed in the order they were added.
@param request The URL request.
@param receiptID The identifier to use for the download receipt that will be created for this request. This must be a unique identifier that does not represent any other request.
@param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
@param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
@return The image download receipt for the data task if available. `nil` if the image is stored in the cache.
cache and the URL request cache policy allows the cache to be used.
*/
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
withReceiptID:(NSUUID *)receiptID
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
/**
Cancels the data task in the receipt by removing the corresponding success and failure blocks and cancelling the data task if necessary.
If the data task is pending in the queue, it will be cancelled if no other success and failure blocks are registered with the data task. If the data task is currently executing or is already completed, the success and failure blocks are removed and will not be called when the task finishes.
@param imageDownloadReceipt The image download receipt to cancel.
*/
- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt;
@end
#endif
NS_ASSUME_NONNULL_END
// AFNetworkActivityIndicatorManager.h
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
`AFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. When enabled, it will listen for notifications indicating that a session task has started or finished, and start or stop animating the indicator accordingly. The number of active requests is incremented and decremented much like a stack or a semaphore, and the activity indicator will animate so long as that number is greater than zero.
You should enable the shared instance of `AFNetworkActivityIndicatorManager` when your application finishes launching. In `AppDelegate application:didFinishLaunchingWithOptions:` you can do so with the following code:
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
By setting `enabled` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself.
See the Apple Human Interface Guidelines section about the Network Activity Indicator for more information:
http://developer.apple.com/library/iOS/#documentation/UserExperience/Conceptual/MobileHIG/UIElementGuidelines/UIElementGuidelines.html#//apple_ref/doc/uid/TP40006556-CH13-SW44
*/
NS_EXTENSION_UNAVAILABLE_IOS("Use view controller based solutions where appropriate instead.")
@interface AFNetworkActivityIndicatorManager : NSObject
/**
A Boolean value indicating whether the manager is enabled.
If YES, the manager will change status bar network activity indicator according to network operation notifications it receives. The default value is NO.
*/
@property (nonatomic, assign, getter = isEnabled) BOOL enabled;
/**
A Boolean value indicating whether the network activity indicator manager is currently active.
*/
@property (readonly, nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
/**
A time interval indicating the minimum duration of networking activity that should occur before the activity indicator is displayed. The default value 1 second. If the network activity indicator should be displayed immediately when network activity occurs, this value should be set to 0 seconds.
Apple's HIG describes the following:
> Display the network activity indicator to provide feedback when your app accesses the network for more than a couple of seconds. If the operation finishes sooner than that, you don’t have to show the network activity indicator, because the indicator is likely to disappear before users notice its presence.
*/
@property (nonatomic, assign) NSTimeInterval activationDelay;
/**
A time interval indicating the duration of time of no networking activity required before the activity indicator is disabled. This allows for continuous display of the network activity indicator across multiple requests. The default value is 0.17 seconds.
*/
@property (nonatomic, assign) NSTimeInterval completionDelay;
/**
Returns the shared network activity indicator manager object for the system.
@return The systemwide network activity indicator manager.
*/
+ (instancetype)sharedManager;
/**
Increments the number of active network requests. If this number was zero before incrementing, this will start animating the status bar network activity indicator.
*/
- (void)incrementActivityCount;
/**
Decrements the number of active network requests. If this number becomes zero after decrementing, this will stop animating the status bar network activity indicator.
*/
- (void)decrementActivityCount;
/**
Set the a custom method to be executed when the network activity indicator manager should be hidden/shown. By default, this is null, and the UIApplication Network Activity Indicator will be managed automatically. If this block is set, it is the responsiblity of the caller to manager the network activity indicator going forward.
@param block A block to be executed when the network activity indicator status changes.
*/
- (void)setNetworkingActivityActionWithBlock:(nullable void (^)(BOOL networkActivityIndicatorVisible))block;
@end
NS_ASSUME_NONNULL_END
#endif
// AFNetworkActivityIndicatorManager.m
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "AFNetworkActivityIndicatorManager.h"
#if TARGET_OS_IOS
#import "AFURLSessionManager.h"
typedef NS_ENUM(NSInteger, AFNetworkActivityManagerState) {
AFNetworkActivityManagerStateNotActive,
AFNetworkActivityManagerStateDelayingStart,
AFNetworkActivityManagerStateActive,
AFNetworkActivityManagerStateDelayingEnd
};
static NSTimeInterval const kDefaultAFNetworkActivityManagerActivationDelay = 1.0;
static NSTimeInterval const kDefaultAFNetworkActivityManagerCompletionDelay = 0.17;
static NSURLRequest * AFNetworkRequestFromNotification(NSNotification *notification) {
if ([[notification object] respondsToSelector:@selector(originalRequest)]) {
return [(NSURLSessionTask *)[notification object] originalRequest];
} else {
return nil;
}
}
typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisible);
@interface AFNetworkActivityIndicatorManager ()
@property (readwrite, nonatomic, assign) NSInteger activityCount;
@property (readwrite, nonatomic, strong) NSTimer *activationDelayTimer;
@property (readwrite, nonatomic, strong) NSTimer *completionDelayTimer;
@property (readonly, nonatomic, getter = isNetworkActivityOccurring) BOOL networkActivityOccurring;
@property (nonatomic, copy) AFNetworkActivityActionBlock networkActivityActionBlock;
@property (nonatomic, assign) AFNetworkActivityManagerState currentState;
@property (nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
- (void)updateCurrentStateForNetworkActivityChange;
@end
@implementation AFNetworkActivityIndicatorManager
+ (instancetype)sharedManager {
static AFNetworkActivityIndicatorManager *_sharedManager = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedManager = [[self alloc] init];
});
return _sharedManager;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.currentState = AFNetworkActivityManagerStateNotActive;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingTaskDidResumeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidSuspendNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidCompleteNotification object:nil];
self.activationDelay = kDefaultAFNetworkActivityManagerActivationDelay;
self.completionDelay = kDefaultAFNetworkActivityManagerCompletionDelay;
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_activationDelayTimer invalidate];
[_completionDelayTimer invalidate];
}
- (void)setEnabled:(BOOL)enabled {
_enabled = enabled;
if (enabled == NO) {
[self setCurrentState:AFNetworkActivityManagerStateNotActive];
}
}
- (void)setNetworkingActivityActionWithBlock:(void (^)(BOOL networkActivityIndicatorVisible))block {
self.networkActivityActionBlock = block;
}
- (BOOL)isNetworkActivityOccurring {
@synchronized(self) {
return self.activityCount > 0;
}
}
- (void)setNetworkActivityIndicatorVisible:(BOOL)networkActivityIndicatorVisible {
if (_networkActivityIndicatorVisible != networkActivityIndicatorVisible) {
[self willChangeValueForKey:@"networkActivityIndicatorVisible"];
@synchronized(self) {
_networkActivityIndicatorVisible = networkActivityIndicatorVisible;
}
[self didChangeValueForKey:@"networkActivityIndicatorVisible"];
if (self.networkActivityActionBlock) {
self.networkActivityActionBlock(networkActivityIndicatorVisible);
} else {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:networkActivityIndicatorVisible];
}
}
}
- (void)setActivityCount:(NSInteger)activityCount {
@synchronized(self) {
_activityCount = activityCount;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCurrentStateForNetworkActivityChange];
});
}
- (void)incrementActivityCount {
[self willChangeValueForKey:@"activityCount"];
@synchronized(self) {
_activityCount++;
}
[self didChangeValueForKey:@"activityCount"];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCurrentStateForNetworkActivityChange];
});
}
- (void)decrementActivityCount {
[self willChangeValueForKey:@"activityCount"];
@synchronized(self) {
_activityCount = MAX(_activityCount - 1, 0);
}
[self didChangeValueForKey:@"activityCount"];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateCurrentStateForNetworkActivityChange];
});
}
- (void)networkRequestDidStart:(NSNotification *)notification {
if ([AFNetworkRequestFromNotification(notification) URL]) {
[self incrementActivityCount];
}
}
- (void)networkRequestDidFinish:(NSNotification *)notification {
if ([AFNetworkRequestFromNotification(notification) URL]) {
[self decrementActivityCount];
}
}
#pragma mark - Internal State Management
- (void)setCurrentState:(AFNetworkActivityManagerState)currentState {
@synchronized(self) {
if (_currentState != currentState) {
[self willChangeValueForKey:@"currentState"];
_currentState = currentState;
switch (currentState) {
case AFNetworkActivityManagerStateNotActive:
[self cancelActivationDelayTimer];
[self cancelCompletionDelayTimer];
[self setNetworkActivityIndicatorVisible:NO];
break;
case AFNetworkActivityManagerStateDelayingStart:
[self startActivationDelayTimer];
break;
case AFNetworkActivityManagerStateActive:
[self cancelCompletionDelayTimer];
[self setNetworkActivityIndicatorVisible:YES];
break;
case AFNetworkActivityManagerStateDelayingEnd:
[self startCompletionDelayTimer];
break;
}
[self didChangeValueForKey:@"currentState"];
}
}
}
- (void)updateCurrentStateForNetworkActivityChange {
if (self.enabled) {
switch (self.currentState) {
case AFNetworkActivityManagerStateNotActive:
if (self.isNetworkActivityOccurring) {
[self setCurrentState:AFNetworkActivityManagerStateDelayingStart];
}
break;
case AFNetworkActivityManagerStateDelayingStart:
//No op. Let the delay timer finish out.
break;
case AFNetworkActivityManagerStateActive:
if (!self.isNetworkActivityOccurring) {
[self setCurrentState:AFNetworkActivityManagerStateDelayingEnd];
}
break;
case AFNetworkActivityManagerStateDelayingEnd:
if (self.isNetworkActivityOccurring) {
[self setCurrentState:AFNetworkActivityManagerStateActive];
}
break;
}
}
}
- (void)startActivationDelayTimer {
self.activationDelayTimer = [NSTimer
timerWithTimeInterval:self.activationDelay target:self selector:@selector(activationDelayTimerFired) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:self.activationDelayTimer forMode:NSRunLoopCommonModes];
}
- (void)activationDelayTimerFired {
if (self.networkActivityOccurring) {
[self setCurrentState:AFNetworkActivityManagerStateActive];
} else {
[self setCurrentState:AFNetworkActivityManagerStateNotActive];
}
}
- (void)startCompletionDelayTimer {
[self.completionDelayTimer invalidate];
self.completionDelayTimer = [NSTimer timerWithTimeInterval:self.completionDelay target:self selector:@selector(completionDelayTimerFired) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:self.completionDelayTimer forMode:NSRunLoopCommonModes];
}
- (void)completionDelayTimerFired {
[self setCurrentState:AFNetworkActivityManagerStateNotActive];
}
- (void)cancelActivationDelayTimer {
[self.activationDelayTimer invalidate];
}
- (void)cancelCompletionDelayTimer {
[self.completionDelayTimer invalidate];
}
@end
#endif
// UIActivityIndicatorView+AFNetworking.h
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <TargetConditionals.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
/**
This category adds methods to the UIKit framework's `UIActivityIndicatorView` class. The methods in this category provide support for automatically starting and stopping animation depending on the loading state of a session task.
*/
@interface UIActivityIndicatorView (AFNetworking)
///----------------------------------
/// @name Animating for Session Tasks
///----------------------------------
/**
Binds the animating state to the state of the specified task.
@param task The task. If `nil`, automatic updating from any previously specified operation will be disabled.
*/
- (void)setAnimatingWithStateOfTask:(nullable NSURLSessionTask *)task;
@end
#endif
// UIActivityIndicatorView+AFNetworking.m
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "UIActivityIndicatorView+AFNetworking.h"
#import <objc/runtime.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import "AFURLSessionManager.h"
@interface AFActivityIndicatorViewNotificationObserver : NSObject
@property (readonly, nonatomic, weak) UIActivityIndicatorView *activityIndicatorView;
- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView;
- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task;
@end
@implementation UIActivityIndicatorView (AFNetworking)
- (AFActivityIndicatorViewNotificationObserver *)af_notificationObserver {
AFActivityIndicatorViewNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver));
if (notificationObserver == nil) {
notificationObserver = [[AFActivityIndicatorViewNotificationObserver alloc] initWithActivityIndicatorView:self];
objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return notificationObserver;
}
- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task {
[[self af_notificationObserver] setAnimatingWithStateOfTask:task];
}
@end
@implementation AFActivityIndicatorViewNotificationObserver
- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView
{
self = [super init];
if (self) {
_activityIndicatorView = activityIndicatorView;
}
return self;
}
- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil];
if (task) {
if (task.state != NSURLSessionTaskStateCompleted) {
UIActivityIndicatorView *activityIndicatorView = self.activityIndicatorView;
if (task.state == NSURLSessionTaskStateRunning) {
[activityIndicatorView startAnimating];
} else {
[activityIndicatorView stopAnimating];
}
[notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task];
[notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task];
[notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidSuspendNotification object:task];
}
}
}
#pragma mark -
- (void)af_startAnimating {
dispatch_async(dispatch_get_main_queue(), ^{
[self.activityIndicatorView startAnimating];
});
}
- (void)af_stopAnimating {
dispatch_async(dispatch_get_main_queue(), ^{
[self.activityIndicatorView stopAnimating];
});
}
#pragma mark -
- (void)dealloc {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil];
}
@end
#endif
//
// UIImage+AFNetworking.h
//
//
// Created by Paulo Ferreira on 08/07/15.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
@interface UIImage (AFNetworking)
+ (UIImage*) safeImageWithData:(NSData*)data;
@end
#endif
// UIImageView+AFNetworking.h
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <TargetConditionals.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class AFImageDownloader;
/**
This category adds methods to the UIKit framework's `UIImageView` class. The methods in this category provide support for loading remote images asynchronously from a URL.
*/
@interface UIImageView (AFNetworking)
///------------------------------------
/// @name Accessing the Image Downloader
///------------------------------------
/**
Set the shared image downloader used to download images.
@param imageDownloader The shared image downloader used to download images.
*/
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader;
/**
The shared image downloader used to download images.
*/
+ (AFImageDownloader *)sharedImageDownloader;
///--------------------
/// @name Setting Image
///--------------------
/**
Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:`
@param url The URL used for the image request.
*/
- (void)setImageWithURL:(NSURL *)url;
/**
Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:`
@param url The URL used for the image request.
@param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes.
*/
- (void)setImageWithURL:(NSURL *)url
placeholderImage:(nullable UIImage *)placeholderImage;
/**
Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied.
@param urlRequest The URL request used for the image request.
@param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes.
@param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
@param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
*/
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(nullable UIImage *)placeholderImage
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
/**
Cancels any executing image operation for the receiver, if one exists.
*/
- (void)cancelImageDownloadTask;
@end
NS_ASSUME_NONNULL_END
#endif
// UIImageView+AFNetworking.m
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "UIImageView+AFNetworking.h"
#import <objc/runtime.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import "AFImageDownloader.h"
@interface UIImageView (_AFNetworking)
@property (readwrite, nonatomic, strong, setter = af_setActiveImageDownloadReceipt:) AFImageDownloadReceipt *af_activeImageDownloadReceipt;
@end
@implementation UIImageView (_AFNetworking)
- (AFImageDownloadReceipt *)af_activeImageDownloadReceipt {
return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, @selector(af_activeImageDownloadReceipt));
}
- (void)af_setActiveImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt {
objc_setAssociatedObject(self, @selector(af_activeImageDownloadReceipt), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
#pragma mark -
@implementation UIImageView (AFNetworking)
+ (AFImageDownloader *)sharedImageDownloader {
return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
}
+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark -
- (void)setImageWithURL:(NSURL *)url {
[self setImageWithURL:url placeholderImage:nil];
}
- (void)setImageWithURL:(NSURL *)url
placeholderImage:(UIImage *)placeholderImage
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
[self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure
{
if ([urlRequest URL] == nil) {
[self cancelImageDownloadTask];
self.image = placeholderImage;
return;
}
if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){
return;
}
[self cancelImageDownloadTask];
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
id <AFImageRequestCache> imageCache = downloader.imageCache;
//Use the image from the image cache if it exists
UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
if (cachedImage) {
if (success) {
success(urlRequest, nil, cachedImage);
} else {
self.image = cachedImage;
}
[self clearActiveDownloadInformation];
} else {
if (placeholderImage) {
self.image = placeholderImage;
}
__weak __typeof(self)weakSelf = self;
NSUUID *downloadID = [NSUUID UUID];
AFImageDownloadReceipt *receipt;
receipt = [downloader
downloadImageForURLRequest:urlRequest
withReceiptID:downloadID
success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
if (success) {
success(request, response, responseObject);
} else if(responseObject) {
strongSelf.image = responseObject;
}
[strongSelf clearActiveDownloadInformation];
}
}
failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
if (failure) {
failure(request, response, error);
}
[strongSelf clearActiveDownloadInformation];
}
}];
self.af_activeImageDownloadReceipt = receipt;
}
}
- (void)cancelImageDownloadTask {
if (self.af_activeImageDownloadReceipt != nil) {
[[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt];
[self clearActiveDownloadInformation];
}
}
- (void)clearActiveDownloadInformation {
self.af_activeImageDownloadReceipt = nil;
}
- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest {
return [self.af_activeImageDownloadReceipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString];
}
@end
#endif
// UIKit+AFNetworking.h
//
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
#ifndef _UIKIT_AFNETWORKING_
#define _UIKIT_AFNETWORKING_
#if TARGET_OS_IOS
#import "AFAutoPurgingImageCache.h"
#import "AFImageDownloader.h"
#import "AFNetworkActivityIndicatorManager.h"
#import "UIRefreshControl+AFNetworking.h"
#import "UIWebView+AFNetworking.h"
#endif
#import "UIActivityIndicatorView+AFNetworking.h"
#import "UIButton+AFNetworking.h"
#import "UIImageView+AFNetworking.h"
#import "UIProgressView+AFNetworking.h"
#endif /* _UIKIT_AFNETWORKING_ */
#endif
// UIProgressView+AFNetworking.h
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <TargetConditionals.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
This category adds methods to the UIKit framework's `UIProgressView` class. The methods in this category provide support for binding the progress to the upload and download progress of a session task.
*/
@interface UIProgressView (AFNetworking)
///------------------------------------
/// @name Setting Session Task Progress
///------------------------------------
/**
Binds the progress to the upload progress of the specified session task.
@param task The session task.
@param animated `YES` if the change should be animated, `NO` if the change should happen immediately.
*/
- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task
animated:(BOOL)animated;
/**
Binds the progress to the download progress of the specified session task.
@param task The session task.
@param animated `YES` if the change should be animated, `NO` if the change should happen immediately.
*/
- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task
animated:(BOOL)animated;
@end
NS_ASSUME_NONNULL_END
#endif
// UIProgressView+AFNetworking.m
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "UIProgressView+AFNetworking.h"
#import <objc/runtime.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import "AFURLSessionManager.h"
static void * AFTaskCountOfBytesSentContext = &AFTaskCountOfBytesSentContext;
static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext;
#pragma mark -
@implementation UIProgressView (AFNetworking)
- (BOOL)af_uploadProgressAnimated {
return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_uploadProgressAnimated)) boolValue];
}
- (void)af_setUploadProgressAnimated:(BOOL)animated {
objc_setAssociatedObject(self, @selector(af_uploadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)af_downloadProgressAnimated {
return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_downloadProgressAnimated)) boolValue];
}
- (void)af_setDownloadProgressAnimated:(BOOL)animated {
objc_setAssociatedObject(self, @selector(af_downloadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark -
- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task
animated:(BOOL)animated
{
if (task.state == NSURLSessionTaskStateCompleted) {
return;
}
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
[task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
[self af_setUploadProgressAnimated:animated];
}
- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task
animated:(BOOL)animated
{
if (task.state == NSURLSessionTaskStateCompleted) {
return;
}
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
[task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
[self af_setDownloadProgressAnimated:animated];
}
#pragma mark - NSKeyValueObserving
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(__unused NSDictionary *)change
context:(void *)context
{
if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
if ([object countOfBytesExpectedToSend] > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated];
});
}
}
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
if ([object countOfBytesExpectedToReceive] > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated];
});
}
}
if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) {
if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) {
@try {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))];
if (context == AFTaskCountOfBytesSentContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
}
if (context == AFTaskCountOfBytesReceivedContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
}
}
@catch (NSException * __unused exception) {}
}
}
}
}
@end
#endif
// UIRefreshControl+AFNetworking.m
//
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
This category adds methods to the UIKit framework's `UIRefreshControl` class. The methods in this category provide support for automatically beginning and ending refreshing depending on the loading state of a session task.
*/
@interface UIRefreshControl (AFNetworking)
///-----------------------------------
/// @name Refreshing for Session Tasks
///-----------------------------------
/**
Binds the refreshing state to the state of the specified task.
@param task The task. If `nil`, automatic updating from any previously specified operation will be disabled.
*/
- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task;
@end
NS_ASSUME_NONNULL_END
#endif
// UIRefreshControl+AFNetworking.m
//
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "UIRefreshControl+AFNetworking.h"
#import <objc/runtime.h>
#if TARGET_OS_IOS
#import "AFURLSessionManager.h"
@interface AFRefreshControlNotificationObserver : NSObject
@property (readonly, nonatomic, weak) UIRefreshControl *refreshControl;
- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl;
- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task;
@end
@implementation UIRefreshControl (AFNetworking)
- (AFRefreshControlNotificationObserver *)af_notificationObserver {
AFRefreshControlNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver));
if (notificationObserver == nil) {
notificationObserver = [[AFRefreshControlNotificationObserver alloc] initWithActivityRefreshControl:self];
objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return notificationObserver;
}
- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task {
[[self af_notificationObserver] setRefreshingWithStateOfTask:task];
}
@end
@implementation AFRefreshControlNotificationObserver
- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl
{
self = [super init];
if (self) {
_refreshControl = refreshControl;
}
return self;
}
- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil];
if (task) {
UIRefreshControl *refreshControl = self.refreshControl;
if (task.state == NSURLSessionTaskStateRunning) {
[refreshControl beginRefreshing];
[notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingTaskDidResumeNotification object:task];
[notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidCompleteNotification object:task];
[notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidSuspendNotification object:task];
} else {
[refreshControl endRefreshing];
}
}
}
#pragma mark -
- (void)af_beginRefreshing {
dispatch_async(dispatch_get_main_queue(), ^{
[self.refreshControl beginRefreshing];
});
}
- (void)af_endRefreshing {
dispatch_async(dispatch_get_main_queue(), ^{
[self.refreshControl endRefreshing];
});
}
#pragma mark -
- (void)dealloc {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil];
[notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil];
}
@end
#endif
// UIWebView+AFNetworking.h
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class AFHTTPSessionManager;
/**
This category adds methods to the UIKit framework's `UIWebView` class. The methods in this category provide increased control over the request cycle, including progress monitoring and success / failure handling.
@discussion When using these category methods, make sure to assign `delegate` for the web view, which implements `–webView:shouldStartLoadWithRequest:navigationType:` appropriately. This allows for tapped links to be loaded through AFNetworking, and can ensure that `canGoBack` & `canGoForward` update their values correctly.
*/
@interface UIWebView (AFNetworking)
/**
The session manager used to download all requests.
*/
@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;
/**
Asynchronously loads the specified request.
@param request A URL request identifying the location of the content to load. This must not be `nil`.
@param progress A progress object monitoring the current download progress.
@param success A block object to be executed when the request finishes loading successfully. This block returns the HTML string to be loaded by the web view, and takes two arguments: the response, and the response string.
@param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred.
*/
- (void)loadRequest:(NSURLRequest *)request
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success
failure:(nullable void (^)(NSError *error))failure;
/**
Asynchronously loads the data associated with a particular request with a specified MIME type and text encoding.
@param request A URL request identifying the location of the content to load. This must not be `nil`.
@param MIMEType The MIME type of the content. Defaults to the content type of the response if not specified.
@param textEncodingName The IANA encoding name, as in `utf-8` or `utf-16`. Defaults to the response text encoding if not specified.
@param progress A progress object monitoring the current download progress.
@param success A block object to be executed when the request finishes loading successfully. This block returns the data to be loaded by the web view and takes two arguments: the response, and the downloaded data.
@param failure A block object to be executed when the data task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred.
*/
- (void)loadRequest:(NSURLRequest *)request
MIMEType:(nullable NSString *)MIMEType
textEncodingName:(nullable NSString *)textEncodingName
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
failure:(nullable void (^)(NSError *error))failure;
@end
NS_ASSUME_NONNULL_END
#endif
// UIWebView+AFNetworking.m
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "UIWebView+AFNetworking.h"
#import <objc/runtime.h>
#if TARGET_OS_IOS
#import "AFHTTPSessionManager.h"
#import "AFURLResponseSerialization.h"
#import "AFURLRequestSerialization.h"
@interface UIWebView (_AFNetworking)
@property (readwrite, nonatomic, strong, setter = af_setURLSessionTask:) NSURLSessionDataTask *af_URLSessionTask;
@end
@implementation UIWebView (_AFNetworking)
- (NSURLSessionDataTask *)af_URLSessionTask {
return (NSURLSessionDataTask *)objc_getAssociatedObject(self, @selector(af_URLSessionTask));
}
- (void)af_setURLSessionTask:(NSURLSessionDataTask *)af_URLSessionTask {
objc_setAssociatedObject(self, @selector(af_URLSessionTask), af_URLSessionTask, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
#pragma mark -
@implementation UIWebView (AFNetworking)
- (AFHTTPSessionManager *)sessionManager {
static AFHTTPSessionManager *_af_defaultHTTPSessionManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_af_defaultHTTPSessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
_af_defaultHTTPSessionManager.requestSerializer = [AFHTTPRequestSerializer serializer];
_af_defaultHTTPSessionManager.responseSerializer = [AFHTTPResponseSerializer serializer];
});
return objc_getAssociatedObject(self, @selector(sessionManager)) ?: _af_defaultHTTPSessionManager;
}
- (void)setSessionManager:(AFHTTPSessionManager *)sessionManager {
objc_setAssociatedObject(self, @selector(sessionManager), sessionManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (AFHTTPResponseSerializer <AFURLResponseSerialization> *)responseSerializer {
static AFHTTPResponseSerializer <AFURLResponseSerialization> *_af_defaultResponseSerializer = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_af_defaultResponseSerializer = [AFHTTPResponseSerializer serializer];
});
return objc_getAssociatedObject(self, @selector(responseSerializer)) ?: _af_defaultResponseSerializer;
}
- (void)setResponseSerializer:(AFHTTPResponseSerializer<AFURLResponseSerialization> *)responseSerializer {
objc_setAssociatedObject(self, @selector(responseSerializer), responseSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark -
- (void)loadRequest:(NSURLRequest *)request
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success
failure:(void (^)(NSError *error))failure
{
[self loadRequest:request MIMEType:nil textEncodingName:nil progress:progress success:^NSData *(NSHTTPURLResponse *response, NSData *data) {
NSStringEncoding stringEncoding = NSUTF8StringEncoding;
if (response.textEncodingName) {
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
if (encoding != kCFStringEncodingInvalidId) {
stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding);
}
}
NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding];
if (success) {
string = success(response, string);
}
return [string dataUsingEncoding:stringEncoding];
} failure:failure];
}
- (void)loadRequest:(NSURLRequest *)request
MIMEType:(NSString *)MIMEType
textEncodingName:(NSString *)textEncodingName
progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
failure:(void (^)(NSError *error))failure
{
NSParameterAssert(request);
if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) {
[self.af_URLSessionTask cancel];
}
self.af_URLSessionTask = nil;
__weak __typeof(self)weakSelf = self;
__block NSURLSessionDataTask *dataTask;
dataTask = [self.sessionManager
dataTaskWithRequest:request
uploadProgress:nil
downloadProgress:nil
completionHandler:^(NSURLResponse * _Nonnull response, id _Nonnull responseObject, NSError * _Nullable error) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
if (error) {
if (failure) {
failure(error);
}
} else {
if (success) {
success((NSHTTPURLResponse *)response, responseObject);
}
[strongSelf loadData:responseObject MIMEType:MIMEType textEncodingName:textEncodingName baseURL:[dataTask.currentRequest URL]];
if ([strongSelf.delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[strongSelf.delegate webViewDidFinishLoad:strongSelf];
}
}
}];
self.af_URLSessionTask = dataTask;
if (progress != nil) {
*progress = [self.sessionManager downloadProgressForTask:dataTask];
}
[self.af_URLSessionTask resume];
if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[self.delegate webViewDidStartLoad:self];
}
}
@end
#endif
//
// Assessment.h
// HappyDNS
//
// Created by bailong on 16/7/19.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNJudge : NSObject
@end
@interface QNAssessment : NSObject
- (void)submitErrorRecord;
- (void)submitSpeedRecord;
@end
//
// Assessment.m
// HappyDNS
//
// Created by bailong on 16/7/19.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNAssessment.h"
@implementation QNAssessment
- (void)submitErrorRecord {
}
- (void)submitSpeedRecord {
}
@end
//
// HistoryModel.h
// HappyDNS
//
// Created by bailong on 16/7/19.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNIpModel : NSObject
@end
//
// HistoryModel.m
// HappyDNS
//
// Created by bailong on 16/7/19.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNIpModel.h"
@implementation QNIpModel
@end
//
// QNDnsManager.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
@class QNNetworkInfo;
@class QNDomain;
/**
* getaddrinfo 回调上层的函数
*
* @param host 请求的域名
* @return ip 列表
*/
typedef NSArray * (^QNGetAddrInfoCallback)(NSString *host);
/**
* ip status 回调上层的函数
*
* @param ip 请求的IP
* @param code 错误码
* @param ms 消耗时间
*/
typedef void (^QNIpStatusCallback)(NSString *ip, int code, int ms);
/**
* 外部IP 排序接口
*/
@protocol QNIpSorter <NSObject>
/**
* 排序方法
*
* @param ips 传入的IP列表
*
* @return 返回排序好的IP 列表
*/
- (NSArray *)sort:(NSArray *)ips;
@end
/**
* DNS请求客户端,集成了cache管理
*/
@interface QNDnsManager : NSObject
/**
* 解析域名
*
* @param domain 域名
*
* @return IP列表
*/
- (NSArray *)query:(NSString *)domain;
/**
* 解析域名,使用Domain对象进行详细约定
*
* @param domain 配置了一些domain 参数的 domain 对象
*
* @return IP 列表
*/
- (NSArray *)queryWithDomain:(QNDomain *)domain;
/**
* 通知网络发生变化
*
* @param netInfo 网络信息
*/
- (void)onNetworkChange:(QNNetworkInfo *)netInfo;
/**
* Dns client 初始化
*
* @param resolvers 解析服务器列表
* @param netInfo 当前网络信息
*
* @return DnsManager
*/
- (instancetype)init:(NSArray *)resolvers networkInfo:(QNNetworkInfo *)netInfo;
/**
* Dns client 初始化
*
* @param resolvers 解析服务器列表
* @param netInfo 当前网络信息
* @param sorter 外部排序函数
*
* @return DnsManager
*/
- (instancetype)init:(NSArray *)resolvers networkInfo:(QNNetworkInfo *)netInfo sorter:(id<QNIpSorter>)sorter;
/**
* 内置 Hosts 解析
*
* @param domain 域名
* @param ip 对应IP
*
* @return 当前Dnsmanager, 为了链式调用
*/
- (instancetype)putHosts:(NSString *)domain ip:(NSString *)ip;
/**
* 内置 Hosts 解析
*
* @param domain 域名
* @param ip 对应IP
* @param provider 网络运营商
*
* @return 当前Dnsmanager, 为了链式调用
*/
- (instancetype)putHosts:(NSString *)domain ip:(NSString *)ip provider:(int)provider;
/**
* 设置底层 getaddrinfo 使用的回调
*
* @param block 回调的代码块
*/
+ (void)setGetAddrInfoBlock:(QNGetAddrInfoCallback)block;
/**
* 设置底层 getaddrinfo 回调使用的dnsmanager
*
* @param dns 回调用的dnsmanager
*/
+ (void)setDnsManagerForGetAddrInfo:(QNDnsManager *)dns;
/**
* 设置底层 业务统计 如connect 回调使用的Callback
*
* @param block 回调返回该IP状态
*/
+ (void)setIpStatusCallback:(QNIpStatusCallback)block;
/**
* 根据时区判断是否要设置httpDns
*
*/
+ (BOOL)needHttpDns;
@end
/**
* DnsManager 的 URL 辅助类
*/
@interface QNDnsManager (NSURL)
/**
* 使用URL 进行请求
*
* @param url 请求的Url
*
* @return 返回IP 替换过的url
*/
- (NSURL *)queryAndReplaceWithIP:(NSURL *)url;
@end
//
// QNDnsManager.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNDnsManager.h"
#import "QNDomain.h"
#import "QNHosts.h"
#import "QNIP.h"
#import "QNLruCache.h"
#import "QNNetworkInfo.h"
#import "QNRecord.h"
#import "QNResolverDelegate.h"
#include "QNGetAddrInfo.h"
const int kQNDomainHijackingCode = -7001;
const int kQNDomainNotOwnCode = -7002;
const int kQNDomainSeverError = -7003;
@interface QNDnsManager ()
@property (nonatomic, strong) QNLruCache *cache;
@property (atomic) QNNetworkInfo *curNetwork;
@property (nonatomic) NSArray *resolvers;
@property (atomic) UInt32 resolverStatus;
@property (nonatomic) QNHosts *hosts;
@property (nonatomic, strong) id<QNIpSorter> sorter;
@end
//static inline BOOL bits_isSet(UInt32 v, int index) {
// return (v & (1 << index)) != 0;
//}
static inline UInt32 bits_set(UInt32 v, int bitIndex) {
return v |= (1 << bitIndex);
}
static inline UInt32 bits_leadingZeros(UInt32 x) {
UInt32 y;
int n = 32;
y = x >> 16;
if (y != 0) {
n = n - 16;
x = y;
}
y = x >> 8;
if (y != 0) {
n = n - 8;
x = y;
}
y = x >> 4;
if (y != 0) {
n = n - 4;
x = y;
}
y = x >> 2;
if (y != 0) {
n = n - 2;
x = y;
}
y = x >> 1;
if (y != 0) {
return n - 2;
}
return n - x;
}
static NSMutableArray *trimCname(NSArray *records) {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (QNRecord *r in records) {
if (r.type == kQNTypeA || r.type == kQNTypeAAAA) {
[array addObject:r];
}
}
return array;
}
static NSArray *records2Ips(NSArray *records) {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (QNRecord *r in records) {
[array addObject:r.value];
}
return array;
}
@interface DummySorter : NSObject <QNIpSorter>
@end
@implementation DummySorter
//sorted already
- (NSArray *)sort:(NSArray *)ips {
return ips;
}
@end
@implementation QNDnsManager
- (NSArray *)query:(NSString *)domain {
return [self queryWithDomain:[[QNDomain alloc] init:domain]];
}
- (NSArray *)queryWithDomain:(QNDomain *)domain {
if (domain.domain == nil) {
return nil;
}
if ([QNIP mayBeIpV4:domain.domain]) {
return [NSArray arrayWithObject:domain.domain];
}
NSArray *ips = [self queryInternalWithDomain:domain];
return [_sorter sort:ips];
}
- (NSArray *)queryInternalWithDomain:(QNDomain *)domain {
if (domain.hostsFirst) {
NSArray *ret = [_hosts query:domain networkInfo:_curNetwork];
if (ret != nil && ret.count != 0) {
return ret;
}
}
NSMutableArray *result;
if ([_curNetwork isEqualToInfo:[QNNetworkInfo normal]] && [QNNetworkInfo isNetworkChanged]) {
@synchronized(_cache) {
[_cache removeAllObjects];
}
_resolverStatus = 0;
} else {
@synchronized(_cache) {
result = [_cache objectForKey:domain.domain];
if (result != nil && result.count > 1) {
QNRecord *first = [result firstObject];
[result removeObjectAtIndex:0];
[result addObject:first];
}
}
}
@synchronized(_cache) {
if (result != nil && result.count > 0) {
QNRecord *record = [result objectAtIndex:0];
if (![record expired:[[NSDate date] timeIntervalSince1970]]) {
return records2Ips(result);
}
}
}
NSArray *records = nil;
NSError *error = nil;
int firstOk = 32 - bits_leadingZeros(_resolverStatus);
for (int i = 0; i < _resolvers.count; i++) {
int pos = (firstOk + i) % _resolvers.count;
id<QNResolverDelegate> resolver = [_resolvers objectAtIndex:pos];
QNNetworkInfo *previousNetwork = _curNetwork;
NSString *previousIp = [QNNetworkInfo getIp];
records = [resolver query:domain networkInfo:previousNetwork error:&error];
if (error != nil) {
NSError *tmp = error;
error = nil;
if (tmp.code == kQNDomainNotOwnCode) {
continue;
}
}
if (records == nil || records.count == 0) {
if (_curNetwork == previousNetwork && [previousIp isEqualToString:[QNNetworkInfo getIp]]) {
_resolverStatus = bits_set(_resolverStatus, pos);
}
} else {
NSMutableArray *ret = trimCname(records);
if (_curNetwork == previousNetwork && [previousIp isEqualToString:[QNNetworkInfo getIp]]) {
@synchronized(_cache) {
[_cache setObject:ret forKey:domain.domain];
}
}
return records2Ips(ret);
}
}
if (!domain.hostsFirst) {
return [_hosts query:domain networkInfo:_curNetwork];
}
return nil;
}
- (instancetype)init:(NSArray *)resolvers networkInfo:(QNNetworkInfo *)netInfo {
return [self init:resolvers networkInfo:netInfo sorter:nil];
}
- (instancetype)init:(NSArray *)resolvers networkInfo:(QNNetworkInfo *)netInfo sorter:(id<QNIpSorter>)sorter {
if (self = [super init]) {
_cache = [[QNLruCache alloc] init:1024];
_curNetwork = netInfo;
_resolvers = [[NSArray alloc] initWithArray:resolvers];
_hosts = [[QNHosts alloc] init];
if (sorter == nil) {
_sorter = [[DummySorter alloc] init];
} else {
_sorter = sorter;
}
}
return self;
}
- (void)onNetworkChange:(QNNetworkInfo *)netInfo {
@synchronized(_cache) {
[_cache removeAllObjects];
}
_curNetwork = netInfo;
}
- (instancetype)putHosts:(NSString *)domain ip:(NSString *)ip {
[_hosts put:domain ip:ip];
return self;
}
- (instancetype)putHosts:(NSString *)domain ip:(NSString *)ip provider:(int)provider {
[_hosts put:domain ip:ip provider:provider];
return self;
}
- (NSURL *)queryAndReplaceWithIP:(NSURL *)url {
NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:YES];
if (!urlComponents) {
return nil;
}
NSString *host = urlComponents.host;
NSArray *ips = [self query:host];
NSURL *URL = nil;
if (ips && ips.count > 0) {
urlComponents.host = [QNIP ipHost:ips[0]];
}
URL = urlComponents.URL;
return URL;
}
static QNGetAddrInfoCallback getAddrInfoCallback = nil;
static qn_ips_ret *dns_callback_internal(const char *host) {
if (getAddrInfoCallback == nil) {
return NULL;
}
NSString *s = [[NSString alloc] initWithUTF8String:host];
if (s == nil) {
return NULL;
}
NSArray *ips = getAddrInfoCallback(s);
if (ips == nil) {
return NULL;
}
qn_ips_ret *ret = calloc(sizeof(char *), ips.count + 1);
for (int i = 0; i < ips.count; i++) {
NSString *ip = ips[i];
char *ip2 = strdup([ip cStringUsingEncoding:NSUTF8StringEncoding]);
ret->ips[i] = ip2;
}
return ret;
}
static qn_ips_ret *dns_callback(const char *host) {
qn_ips_ret *ret = dns_callback_internal(host);
if (ret == NULL) {
//only for compatible
qn_ips_ret *ret = calloc(sizeof(char *), 2);
ret->ips[0] = strdup(host);
}
return ret;
}
static QNIpStatusCallback ipStatusCallback = nil;
static void ip_status_callback(const char *ip, int code, int time_ms) {
if (ipStatusCallback == nil) {
return;
}
NSString *s = [[NSString alloc] initWithUTF8String:ip];
if (s == nil) {
return;
}
ipStatusCallback(s, code, time_ms);
}
+ (void)setGetAddrInfoBlock:(QNGetAddrInfoCallback)block {
if ([QNIP isIpV6FullySupported] || ![QNIP isV6]) {
getAddrInfoCallback = block;
qn_set_dns_callback(dns_callback);
}
}
+ (void)setDnsManagerForGetAddrInfo:(QNDnsManager *)dns {
[QNDnsManager setGetAddrInfoBlock:^NSArray *(NSString *host) {
return [dns query:host];
}];
}
+ (void)setIpStatusCallback:(QNIpStatusCallback)block {
ipStatusCallback = block;
qn_set_ip_report_callback(ip_status_callback);
}
+ (BOOL)needHttpDns {
NSTimeZone *timeZone = [NSTimeZone localTimeZone];
NSString *tzName = [timeZone name];
return [tzName isEqual:@"Asia/Shanghai"] || [tzName isEqual:@"Asia/Chongqing"] || [tzName isEqual:@"Asia/Harbin"] || [tzName isEqual:@"Asia/Urumqi"];
}
@end
//
// QNDomain.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNDomain : NSObject
@property (nonatomic, strong, readonly) NSString *domain;
// 用来判断劫持
@property (nonatomic, readonly) BOOL hasCname;
// 用来判断劫持
@property (nonatomic, readonly) int maxTtl;
@property (nonatomic, readonly) BOOL hostsFirst;
- (instancetype)init:(NSString *)domain;
- (instancetype)init:(NSString *)domain hostsFirst:(BOOL)hostsFirst hasCname:(BOOL)hasCname;
- (instancetype)init:(NSString *)domain hostsFirst:(BOOL)hostsFirst hasCname:(BOOL)hasCname maxTtl:(int)maxTtl;
@end
//
// QNDomain.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNDomain.h"
@implementation QNDomain
- (instancetype)init:(NSString *)domain {
return [self init:domain hostsFirst:NO hasCname:NO maxTtl:0];
}
- (instancetype)init:(NSString *)domain hostsFirst:(BOOL)hostsFirst hasCname:(BOOL)hasCname {
return [self init:domain hostsFirst:hostsFirst hasCname:hasCname maxTtl:0];
}
- (instancetype)init:(NSString *)domain hostsFirst:(BOOL)hostsFirst hasCname:(BOOL)hasCname maxTtl:(int)maxTtl {
if (self = [super init]) {
_domain = domain;
_hasCname = hasCname;
_maxTtl = maxTtl;
_hostsFirst = hostsFirst;
}
return self;
}
@end
//
// QNLruCache.h
// HappyDNS
//
// Created by bailong on 16/7/5.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNLruCache : NSObject
- (instancetype)init:(NSUInteger)limit;
- (void)removeAllObjects;
- (void)removeObjectForKey:(NSString *)key;
- (id)objectForKey:(NSString *)key;
- (void)setObject:(id)obj forKey:(NSString *)key;
@end
//
// QNLruCache.m
// HappyDNS
//
// Created by bailong on 16/7/5.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNLruCache.h"
@interface QNLruCache ()
@property (nonatomic, readonly) NSUInteger limit;
@property (nonatomic, readonly) NSMutableDictionary* cache;
@property (nonatomic, readonly) NSMutableArray* list;
@end
@interface _QNElement : NSObject
@property (nonatomic, readonly, strong) NSString* key;
@property (nonatomic, strong) id obj;
- (instancetype)initObject:(id)obj forKey:(NSString*)key;
@end
@implementation _QNElement
- (instancetype)initObject:(id)obj forKey:(NSString*)key {
if (self = [super init]) {
_key = key;
_obj = obj;
}
return self;
}
@end
@implementation QNLruCache
- (instancetype)init:(NSUInteger)limit {
if (self = [super init]) {
_limit = limit;
_cache = [NSMutableDictionary new];
_list = [NSMutableArray new];
}
return self;
}
- (void)removeAllObjects {
[_cache removeAllObjects];
[_list removeAllObjects];
}
- (void)removeObjectForKey:(NSString*)key {
_QNElement* obj = [_cache objectForKey:key];
if (obj == nil) {
return;
}
[_cache removeObjectForKey:key];
[_list removeObjectIdenticalTo:obj];
}
- (id)objectForKey:(NSString*)key {
_QNElement* obj = [_cache objectForKey:key];
if (obj != nil) {
[_list removeObjectIdenticalTo:obj];
[_list insertObject:obj atIndex:0];
}
return obj.obj;
}
- (void)setObject:(id)obj forKey:(NSString*)key {
_QNElement* old = [_cache objectForKey:key];
if (old) {
old.obj = obj;
[_list removeObjectIdenticalTo:old];
[_list insertObject:old atIndex:0];
return;
} else if (_list.count == _limit) {
old = [_list lastObject];
[_list removeLastObject];
[_cache removeObjectForKey:old.key];
}
_QNElement* newElement = [[_QNElement alloc] initObject:obj forKey:key];
[_cache setObject:newElement forKey:key];
[_list insertObject:newElement atIndex:0];
}
@end
//
// QNNetworkInfo.h
// HappyDNS
//
// Created by bailong on 15/6/25.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
extern const int kQNNO_NETWORK;
extern const int kQNWIFI;
extern const int kQNMOBILE;
extern const int kQNISP_GENERAL;
extern const int kQNISP_CTC;
extern const int kQNISP_DIANXIN;
extern const int kQNISP_CNC;
extern const int kQNISP_LIANTONG;
extern const int kQNISP_CMCC;
extern const int kQNISP_YIDONG;
extern const int kQNISP_OTHER;
@interface QNNetworkInfo : NSObject
@property (nonatomic, readonly) int networkConnection;
@property (nonatomic, readonly) int provider;
- (instancetype)init:(int)connecton provider:(int)provider;
- (BOOL)isEqual:(id)other;
- (BOOL)isEqualToInfo:(QNNetworkInfo *)info;
+ (instancetype)noNet;
+ (instancetype)normal;
+ (BOOL)isNetworkChanged;
+ (NSString *)getIp;
@end
//
// QNNetworkInfo.m
// HappyDNS
//
// Created by bailong on 15/6/25.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#import "QNIP.h"
#import "QNNetworkInfo.h"
const int kQNNO_NETWORK = -1;
const int kQNWIFI = 1;
const int kQNMOBILE = 2;
const int kQNISP_GENERAL = 0;
const int kQNISP_CTC = 1;
const int kQNISP_DIANXIN = kQNISP_CTC;
const int kQNISP_CNC = 2;
const int kQNISP_LIANTONG = kQNISP_CNC;
const int kQNISP_CMCC = 3;
const int kQNISP_YIDONG = kQNISP_CMCC;
const int kQNISP_OTHER = 999;
#define IPLength 64
static char previousIp[IPLength] = {0};
static NSString *lock = @"";
@implementation QNNetworkInfo
- (instancetype)init:(int)connecton provider:(int)provider {
if (self = [super init]) {
_networkConnection = connecton;
_provider = provider;
}
return self;
}
+ (instancetype)noNet {
return [[QNNetworkInfo alloc] init:kQNNO_NETWORK provider:kQNISP_GENERAL];
}
+ (instancetype)normal {
return [[QNNetworkInfo alloc] init:kQNISP_GENERAL provider:kQNISP_GENERAL];
}
- (BOOL)isEqualToInfo:(QNNetworkInfo *)info {
if (self == info)
return YES;
return self.provider == info.provider && self.networkConnection == info.networkConnection;
}
- (BOOL)isEqual:(id)other {
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
return [self isEqualToInfo:other];
}
+ (BOOL)isNetworkChanged {
@synchronized(lock) {
char local[IPLength] = {0};
int err = qn_localIp(local, sizeof(local));
if (err != 0) {
return YES;
}
if (memcmp(previousIp, local, sizeof(local)) != 0) {
memcpy(previousIp, local, sizeof(local));
return YES;
}
return NO;
}
}
+ (NSString *)getIp {
return [QNIP local];
}
@end
//
// QNRecord.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* A 记录
*/
extern const int kQNTypeA;
/**
* AAAA 记录
*/
extern const int kQNTypeAAAA;
/**
* Cname 记录
*/
extern const int kQNTypeCname;
/**
* Txt 记录
*/
extern const int kQNTypeTXT;
@interface QNRecord : NSObject
@property (nonatomic, strong, readonly) NSString *value;
@property (nonatomic, readonly) int ttl;
@property (nonatomic, readonly) int type;
@property (nonatomic, readonly) long long timeStamp;
- (instancetype)init:(NSString *)value
ttl:(int)ttl
type:(int)type;
- (BOOL)expired:(long long)time;
@end
//
// QNRecord.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNRecord.h"
const int kQNTypeA = 1;
const int kQNTypeAAAA = 28;
const int kQNTypeCname = 5;
const int kQNTypeTXT = 16;
@implementation QNRecord
- (instancetype)init:(NSString *)value
ttl:(int)ttl
type:(int)type {
if (self = [super init]) {
_value = value;
_type = type;
_ttl = ttl;
_timeStamp = [[NSDate date] timeIntervalSince1970];
}
return self;
}
- (BOOL)expired:(long long)time {
return time > _timeStamp + _ttl;
}
@end
//
// QNResolverDelegate.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
extern const int kQNDomainHijackingCode;
extern const int kQNDomainNotOwnCode;
extern const int kQNDomainSeverError;
#define QN_DNS_DEFAULT_TIMEOUT 20 //seconds
@class QNDomain;
@class QNNetworkInfo;
@protocol QNResolverDelegate <NSObject>
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError **)error;
@end
//
// HappyDNS.h
// HappyDNS
//
// Created by bailong on 15/6/24.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNDnsManager.h"
#import "QNDnspodEnterprise.h"
#import "QNDnspodFree.h"
#import "QNDomain.h"
#import "QNHijackingDetectWrapper.h"
#import "QNIP.h"
#import "QNNetworkInfo.h"
#import "QNRecord.h"
#import "QNResolver.h"
#import "QNResolverDelegate.h"
#import "QNGetAddrInfo.h"
\ No newline at end of file
//
// QNDnspodEnterprise.h
// HappyDNS
//
// Created by bailong on 15/7/31.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNResolverDelegate.h"
#import <Foundation/Foundation.h>
extern const int kQN_ENCRYPT_FAILED;
extern const int kQN_DECRYPT_FAILED;
@interface QNDnspodEnterprise : NSObject <QNResolverDelegate>
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key;
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key
server:(NSString *)server;
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key
server:(NSString *)server
timeout:(NSUInteger)time;
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error;
@end
//
// QNDnspodFree.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNDnspodEnterprise.h"
#import <CommonCrypto/CommonCryptor.h>
#import "QNDes.h"
#import "QNDomain.h"
#import "QNHex.h"
#import "QNIP.h"
#import "QNRecord.h"
const int kQN_ENCRYPT_FAILED = -10001;
const int kQN_DECRYPT_FAILED = -10002;
@interface QNDnspodEnterprise ()
@property (readonly, strong) NSString *server;
@property (nonatomic, strong) NSString *userId;
@property (nonatomic, strong) QNDes *des;
@property (nonatomic) NSUInteger timeout;
@end
@implementation QNDnspodEnterprise
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key {
return [self initWithId:userId key:key server:@"119.29.29.29"];
}
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key
server:(NSString *)server {
return [self initWithId:userId key:key server:@"119.29.29.29" timeout:QN_DNS_DEFAULT_TIMEOUT];
}
- (instancetype)initWithId:(NSString *)userId
key:(NSString *)key
server:(NSString *)server
timeout:(NSUInteger)time {
if (self = [super init]) {
_server = server;
_userId = userId;
_des = [[QNDes alloc] init:[key dataUsingEncoding:NSUTF8StringEncoding]];
_timeout = time;
}
return self;
}
- (NSString *)encrypt:(NSString *)domain {
NSData *data = [_des encrypt:[domain dataUsingEncoding:NSUTF8StringEncoding]];
if (data == nil) {
return nil;
}
NSString *str = [QNHex encodeHexData:data];
return str;
}
- (NSString *)decrypt:(NSData *)raw {
NSData *enc = [QNHex decodeHexString:[[NSString alloc] initWithData:raw
encoding:NSUTF8StringEncoding]];
if (enc == nil) {
return nil;
}
NSData *data = [_des decrpyt:enc];
if (data == nil) {
return nil;
}
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
NSString *encrypt = [self encrypt:domain.domain];
if (encrypt == nil) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:domain.domain code:kQN_ENCRYPT_FAILED userInfo:nil];
}
return nil;
}
NSString *url = [NSString stringWithFormat:@"http://%@/d?ttl=1&dn=%@&id=%@", [QNIP ipHost:_server], encrypt, _userId];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:_timeout];
NSHTTPURLResponse *response = nil;
NSError *httpError = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&httpError];
if (httpError != nil) {
if (error != nil) {
*error = httpError;
}
return nil;
}
if (response.statusCode != 200) {
return nil;
}
NSString *raw = [self decrypt:data];
if (raw == nil) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:domain.domain code:kQN_DECRYPT_FAILED userInfo:nil];
}
return nil;
}
NSArray *ip1 = [raw componentsSeparatedByString:@","];
if (ip1.count != 2) {
return nil;
}
NSString *ttlStr = [ip1 objectAtIndex:1];
int ttl = [ttlStr intValue];
if (ttl <= 0) {
return nil;
}
NSString *ips = [ip1 objectAtIndex:0];
NSArray *ipArray = [ips componentsSeparatedByString:@";"];
NSMutableArray *ret = [[NSMutableArray alloc] initWithCapacity:ipArray.count];
for (int i = 0; i < ipArray.count; i++) {
QNRecord *record = [[QNRecord alloc] init:[ipArray objectAtIndex:i] ttl:ttl type:kQNTypeA];
[ret addObject:record];
}
return ret;
}
@end
//
// QNDnspodFree.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNResolverDelegate.h"
#import <Foundation/Foundation.h>
@interface QNDnspodFree : NSObject <QNResolverDelegate>
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error;
- (instancetype)init;
- (instancetype)initWithServer:(NSString *)server;
- (instancetype)initWithServer:(NSString *)server
timeout:(NSUInteger)time;
@end
//
// QNDnspodFree.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNDnspodFree.h"
#import "QNDomain.h"
#import "QNIP.h"
#import "QNRecord.h"
@interface QNDnspodFree ()
@property (readonly, nonatomic, strong) NSString *server;
@property (readonly, nonatomic) NSUInteger timeout;
@end
@implementation QNDnspodFree
- (instancetype)init {
return [self initWithServer:@"119.29.29.29"];
}
- (instancetype)initWithServer:(NSString *)server {
return [self initWithServer:@"119.29.29.29" timeout:QN_DNS_DEFAULT_TIMEOUT];
}
- (instancetype)initWithServer:(NSString *)server
timeout:(NSUInteger)time {
if (self = [super init]) {
_server = server;
_timeout = time;
}
return self;
}
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
NSString *url = [NSString stringWithFormat:@"http://%@/d?ttl=1&dn=%@", [QNIP ipHost:_server], domain.domain];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:_timeout];
NSHTTPURLResponse *response = nil;
NSError *httpError = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&httpError];
if (httpError != nil) {
if (error != nil) {
*error = httpError;
}
return nil;
}
if (response.statusCode != 200) {
return nil;
}
NSString *raw = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *ip1 = [raw componentsSeparatedByString:@","];
if (ip1.count != 2) {
return nil;
}
NSString *ttlStr = [ip1 objectAtIndex:1];
int ttl = [ttlStr intValue];
if (ttl <= 0) {
return nil;
}
NSString *ips = [ip1 objectAtIndex:0];
NSArray *ipArray = [ips componentsSeparatedByString:@";"];
NSMutableArray *ret = [[NSMutableArray alloc] initWithCapacity:ipArray.count];
for (int i = 0; i < ipArray.count; i++) {
QNRecord *record = [[QNRecord alloc] init:[ipArray objectAtIndex:i] ttl:ttl type:kQNTypeA];
[ret addObject:record];
}
return ret;
}
@end
//
// QNHijackingDetectWrapper.h
// HappyDNS
//
// Created by bailong on 15/7/16.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "QNResolverDelegate.h"
@class QNResolver;
@interface QNHijackingDetectWrapper : NSObject <QNResolverDelegate>
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error;
- (instancetype)initWithResolver:(QNResolver *)resolver;
@end
//
// QNHijackingDetectWrapper.m
// HappyDNS
//
// Created by bailong on 15/7/16.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNHijackingDetectWrapper.h"
#import "QNDomain.h"
#import "QNRecord.h"
#import "QNResolver.h"
@interface QNHijackingDetectWrapper ()
@property (nonatomic, readonly) QNResolver *resolver;
@end
@implementation QNHijackingDetectWrapper
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
NSArray *result = [_resolver query:domain networkInfo:netInfo error:error];
if (((!domain.hasCname) && domain.maxTtl == 0) || result == nil || result.count == 0) {
return result;
}
BOOL hasCname = NO;
BOOL outOfTtl = NO;
for (int i = 0; i < result.count; i++) {
QNRecord *record = [result objectAtIndex:i];
if (record.type == kQNTypeCname) {
hasCname = YES;
}
if (domain.maxTtl > 0 && record.type == kQNTypeA && record.ttl > domain.maxTtl) {
outOfTtl = YES;
}
}
if ((domain.hasCname && !hasCname) || outOfTtl) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:domain.domain code:kQNDomainHijackingCode userInfo:nil];
}
return nil;
}
return result;
}
- (instancetype)initWithResolver:(QNResolver *)resolver {
if (self = [super init]) {
_resolver = resolver;
}
return self;
}
@end
//
// QNHosts.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNResolverDelegate.h"
#import <Foundation/Foundation.h>
@interface QNHosts : NSObject
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo;
- (void)put:(NSString *)domain ip:(NSString *)ip;
- (void)put:(NSString *)domain ip:(NSString *)ip provider:(int)provider;
- (instancetype)init;
@end
//
// QNHosts.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNHosts.h"
#import "QNDomain.h"
//#import "QNIP.h"
#import "QNNetworkInfo.h"
@interface QNHosts ()
@property (nonatomic) NSMutableDictionary *dict;
@end
@interface QNHostsValue : NSObject
@property (nonatomic, copy, readonly) NSString *ip;
@property (readonly) int provider;
@end
@implementation QNHostsValue
- (instancetype)init:(NSString *)ip provider:(int)provider {
if (self = [super init]) {
_ip = ip;
_provider = provider;
}
return self;
}
@end
static NSArray *filter(NSArray *input, int provider) {
NSMutableArray *normal = [[NSMutableArray alloc] initWithCapacity:input.count];
NSMutableArray *special = [[NSMutableArray alloc] init];
for (QNHostsValue *v in input) {
NSString *ip = v.ip;
if (v.provider == kQNISP_GENERAL) {
[normal addObject:ip];
}
if (provider == v.provider && provider != kQNISP_GENERAL) {
[special addObject:ip];
}
}
if (special.count != 0) {
return special;
}
return normal;
}
@implementation QNHosts
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo {
NSMutableArray *x;
@synchronized(_dict) {
x = [_dict objectForKey:domain.domain];
}
if (x == nil || x.count == 0) {
return nil;
}
if (x.count >= 2) {
QNHostsValue *first = [x firstObject];
[x removeObjectAtIndex:0];
[x addObject:first];
}
return filter(x, netInfo.provider);
}
- (void)put:(NSString *)domain ip:(NSString *)ip {
[self put:domain ip:ip provider:kQNISP_GENERAL];
}
- (void)put:(NSString *)domain ip:(NSString *)ip provider:(int)provider {
QNHostsValue *v = [[QNHostsValue alloc] init:ip provider:provider];
@synchronized(_dict) {
NSMutableArray *x = [_dict objectForKey:domain];
if (x == nil) {
x = [[NSMutableArray alloc] init];
}
[x addObject:v];
[_dict setObject:x forKey:domain];
}
}
- (instancetype)init {
if (self = [super init]) {
_dict = [[NSMutableDictionary alloc] init];
}
return self;
}
@end
//
// QNResolv.h
// HappyDNS
//
// Created by bailong on 16/5/28.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#ifndef QNResolv_h
#define QNResolv_h
extern BOOL isV6(NSString *address);
extern int setup_dns_server(void *res, NSString *dns_server, NSUInteger timeout);
#endif /* QNResolv_h */
//
// QNResolvUtil.m
// HappyDNS
//
// Created by bailong on 16/5/28.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#import "QNIP.h"
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#endif
BOOL isV6(NSString *address) {
return strchr(address.UTF8String, ':') != NULL;
}
int setup_dns_server(void *_res_state, NSString *dns_server, NSUInteger timeout) {
res_state res = (res_state)_res_state;
int r = res_ninit(res);
if (r != 0) {
return r;
}
res->retrans = (int)timeout;
res->retry = 1;
if (dns_server == nil) {
return 0;
}
res->options |= RES_IGNTC;
union res_sockaddr_union server = {0};
struct addrinfo hints = {0}, *ai = NULL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(dns_server.UTF8String, "53", &hints, &ai);
if (ret != 0) {
return ret;
}
int family = ai->ai_family;
if (family == AF_INET6) {
((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = htons(53);
server.sin6 = *((struct sockaddr_in6 *)ai->ai_addr);
} else {
server.sin = *((struct sockaddr_in *)ai->ai_addr);
}
if (![QNIP isIpV6FullySupported] && family == AF_INET) {
if ([QNIP isV6]) {
freeaddrinfo(ai);
ai = NULL;
bzero(&hints, 0);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
char buf[64] = {0};
qn_nat64(buf, sizeof(buf), (uint32_t)server.sin.sin_addr.s_addr);
int ret = getaddrinfo(buf, "53", &hints, &ai);
if (ret != 0) {
return -1;
}
((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = htons(53);
server.sin6 = *((struct sockaddr_in6 *)ai->ai_addr);
}
}
freeaddrinfo(ai);
res_setservers(res, &server, 1);
return 0;
}
\ No newline at end of file
//
// QNResolver.h
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNResolverDelegate.h"
#import <Foundation/Foundation.h>
@interface QNResolver : NSObject <QNResolverDelegate>
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error;
// @deprecated typo
- (instancetype)initWithAddres:(NSString *)address DEPRECATED_ATTRIBUTE;
- (instancetype)initWithAddress:(NSString *)address;
- (instancetype)initWithAddress:(NSString *)address
timeout:(NSUInteger)time;
+ (instancetype)systemResolver;
+ (NSString *)systemDnsServer;
@end
//
// QNResolver.m
// HappyDNS
//
// Created by bailong on 15/6/23.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#include <arpa/inet.h>
#include <resolv.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#import "QNDomain.h"
#import "QNIP.h"
#import "QNRecord.h"
#import "QNResolver.h"
#import "QNResolvUtil.h"
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIKit.h>
#endif
@interface QNResolver ()
@property (nonatomic, readonly, strong) NSString *address;
@property (nonatomic, readonly) NSUInteger timeout;
@end
static NSArray *query_ip_v4(res_state res, const char *host) {
u_char answer[2000];
int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
int count = ns_msg_count(handle, ns_s_an);
if (count <= 0) {
res_ndestroy(res);
return nil;
}
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:count];
char buf[32];
char cnameBuf[NS_MAXDNAME];
memset(cnameBuf, 0, sizeof(cnameBuf));
for (int i = 0; i < count; i++) {
ns_rr rr;
if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) {
res_ndestroy(res);
return nil;
}
int t = ns_rr_type(rr);
int ttl = ns_rr_ttl(rr);
NSString *val;
if (t == ns_t_a) {
const char *p = inet_ntop(AF_INET, ns_rr_rdata(rr), buf, sizeof(buf));
val = [NSString stringWithUTF8String:p];
} else if (t == ns_t_cname) {
int x = ns_name_uncompress(answer, &(answer[len]), ns_rr_rdata(rr), cnameBuf, sizeof(cnameBuf));
if (x <= 0) {
continue;
}
val = [NSString stringWithUTF8String:cnameBuf];
memset(cnameBuf, 0, sizeof(cnameBuf));
} else {
continue;
}
QNRecord *record = [[QNRecord alloc] init:val ttl:ttl type:t];
[array addObject:record];
}
res_ndestroy(res);
return array;
}
@implementation QNResolver
- (instancetype)initWithAddres:(NSString *)address {
return [self initWithAddress:address];
}
- (instancetype)initWithAddress:(NSString *)address {
return [self initWithAddress:address timeout:QN_DNS_DEFAULT_TIMEOUT];
}
- (instancetype)initWithAddress:(NSString *)address
timeout:(NSUInteger)time {
if (self = [super init]) {
_address = address;
_timeout = time;
}
return self;
}
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
struct __res_state res;
int r = setup_dns_server(&res, _address, _timeout);
if (r != 0) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:@"qiniu.dns" code:kQNDomainSeverError userInfo:nil];
}
return nil;
}
NSArray *ret = query_ip_v4(&res, [domain.domain cStringUsingEncoding:NSUTF8StringEncoding]);
if (ret != nil && ret.count != 0) {
return ret;
}
if (error != nil) {
*error = [[NSError alloc] initWithDomain:@"qiniu.dns" code:NSURLErrorDNSLookupFailed userInfo:nil];
}
return nil;
}
+ (instancetype)systemResolver {
return [[QNResolver alloc] initWithAddress:nil];
}
+ (NSString *)systemDnsServer {
struct __res_state res;
int r = res_ninit(&res);
if (r != 0) {
return nil;
}
union res_sockaddr_union server[MAXNS] = {0};
r = res_getservers(&res, server, MAXNS);
res_ndestroy(&res);
if (r <= 0) {
return nil;
}
int family = server[0].sin.sin_family;
char buf[64] = {0};
const void *addr;
if (family == AF_INET6) {
addr = &server[0].sin6.sin6_addr;
} else {
addr = &server[0].sin.sin_addr;
}
const char *p = inet_ntop(family, addr, buf, sizeof(buf));
if (p == NULL) {
return nil;
}
return [NSString stringWithUTF8String:p];
}
@end
//
// QNTxtResolver.h
// HappyDNS
//
// Created by bailong on 16/1/5.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNResolverDelegate.h"
#import <Foundation/Foundation.h>
@interface QNTxtResolver : NSObject <QNResolverDelegate>
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error;
/**
* 根据服务器地址进行初始化
*
* @param address DNS 服务器地址,nil 表示系统的
*/
- (instancetype)initWithAddress:(NSString *)address;
- (instancetype)initWithAddress:(NSString *)address timeout:(NSUInteger)time;
@end
//
// QNTxtResolver.m
// HappyDNS
//
// Created by bailong on 16/1/5.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNTxtResolver.h"
#include <arpa/inet.h>
#include <resolv.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#import "QNDomain.h"
#import "QNRecord.h"
#import "QNResolvUtil.h"
#import "QNResolver.h"
@interface QNTxtResolver ()
@property (nonatomic, readonly, strong) NSString *address;
@property (nonatomic, readonly) NSUInteger timeout;
@end
static NSArray *query_ip(res_state res, const char *host) {
u_char answer[1500];
int len = res_nquery(res, host, ns_c_in, ns_t_txt, answer, sizeof(answer));
ns_msg handle;
ns_initparse(answer, len, &handle);
int count = ns_msg_count(handle, ns_s_an);
if (count != 1) {
res_ndestroy(res);
return nil;
}
char txtbuf[256];
memset(txtbuf, 0, sizeof(txtbuf));
ns_rr rr;
if (ns_parserr(&handle, ns_s_an, 0, &rr) != 0) {
res_ndestroy(res);
return nil;
}
int t = ns_rr_type(rr);
int ttl = ns_rr_ttl(rr);
int rdlen = ns_rr_rdlen(rr);
if (rdlen <= 1 + 7) {
res_ndestroy(res);
return nil;
}
NSString *val;
if (t == ns_t_txt) {
memcpy(txtbuf, ns_rr_rdata(rr) + 1, rdlen - 1);
val = [NSString stringWithUTF8String:txtbuf];
} else {
res_ndestroy(res);
return nil;
}
NSArray *ipArray = [val componentsSeparatedByString:@","];
NSMutableArray *ret = [[NSMutableArray alloc] initWithCapacity:ipArray.count];
for (int i = 0; i < ipArray.count; i++) {
QNRecord *record = [[QNRecord alloc] init:[ipArray objectAtIndex:i] ttl:ttl type:kQNTypeA];
[ret addObject:record];
}
res_ndestroy(res);
return ret;
}
@implementation QNTxtResolver
- (instancetype)initWithAddress:(NSString *)address {
return [self initWithAddress:address timeout:QN_DNS_DEFAULT_TIMEOUT];
}
- (instancetype)initWithAddress:(NSString *)address timeout:(NSUInteger)time {
if (self = [super init]) {
_address = address;
_timeout = time;
}
return self;
}
- (NSArray *)query:(QNDomain *)domain networkInfo:(QNNetworkInfo *)netInfo error:(NSError *__autoreleasing *)error {
struct __res_state res;
int r = setup_dns_server(&res, _address, _timeout);
if (r != 0) {
if (error != nil) {
*error = [[NSError alloc] initWithDomain:@"qiniu.dns" code:kQNDomainSeverError userInfo:nil];
}
return nil;
}
NSArray *ret = query_ip(&res, [domain.domain cStringUsingEncoding:NSUTF8StringEncoding]);
if (ret == nil && error != nil) {
*error = [[NSError alloc] initWithDomain:@"qiniu.dns" code:NSURLErrorDNSLookupFailed userInfo:nil];
}
return ret;
}
@end
//
// QNRefresher.h
// HappyDNS
//
// Created by bailong on 16/7/20.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface QNRefresher : NSObject
@end
//
// QNRefresher.m
// HappyDNS
//
// Created by bailong on 16/7/20.
// Copyright © 2016年 Qiniu Cloud Storage. All rights reserved.
//
#import "QNRefresher.h"
@implementation QNRefresher
@end
//
// QNDes.h
// HappyDNS
//
// Created by bailong on 15/8/1.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <Foundation/Foundation.h>
extern const int kQN_ENCRYPT_FAILED;
extern const int kQN_DECRYPT_FAILED;
@interface QNDes : NSObject
- (NSData *)encrypt:(NSData *)input;
- (NSData *)decrpyt:(NSData *)input;
- (instancetype)init:(NSData *)key;
@end
//
// QNDes.m
// HappyDNS
//
// Created by bailong on 15/8/1.
// Copyright (c) 2015年 Qiniu Cloud Storage. All rights reserved.
//
#import <CommonCrypto/CommonCryptor.h>
#import "QNDes.h"
@interface QNDes ()
@property (nonatomic, strong) NSData *key;
@end
@implementation QNDes
- (NSData *)encrypt:(NSData *)data {
const void *input = data.bytes;
size_t inputSize = data.length;
size_t bufferSize = (inputSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
uint8_t *buffer = malloc(bufferSize * sizeof(uint8_t));
memset((void *)buffer, 0x0, bufferSize);
size_t movedBytes = 0;
const void *vkey = _key.bytes;
CCCryptorStatus ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionECBMode | kCCOptionPKCS7Padding,
vkey,
kCCKeySizeDES,
NULL,
input,
inputSize,
(void *)buffer,
bufferSize,
&movedBytes);
if (ccStatus != kCCSuccess) {
NSLog(@"error code %d", ccStatus);
free(buffer);
return nil;
}
NSData *encrypted = [NSData dataWithBytes:(const void *)buffer length:(NSUInteger)movedBytes];
free(buffer);
return encrypted;
}
- (NSData *)decrpyt:(NSData *)raw {
const void *input = raw.bytes;
size_t inputSize = raw.length;
size_t bufferSize = 1024;
uint8_t *buffer = malloc(bufferSize * sizeof(uint8_t));
memset((void *)buffer, 0x0, bufferSize);
size_t movedBytes = 0;
const void *vkey = _key.bytes;
CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionECBMode | kCCOptionPKCS7Padding,
vkey,
kCCKeySizeDES,
NULL,
input,
inputSize,
(void *)buffer,
bufferSize,
&movedBytes);
if (ccStatus != kCCSuccess) {
NSLog(@"error code %d", ccStatus);
free(buffer);
return nil;
}
NSData *decrypted = [NSData dataWithBytes:(const void *)buffer length:(NSUInteger)movedBytes];
free(buffer);
return decrypted;
}
- (instancetype)init:(NSData *)key {
if (self = [super init]) {
_key = key;
}
return self;
}
@end
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment