Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions example/VKSDKTestApplication/TestViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ - (void)uploadPhoto {
[post executeWithResultBlock:^(VKResponse *postResponse) {
NSLog(@"Result: %@", postResponse);
NSNumber *postId = postResponse.json[@"post_id"];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://vk.com/wall-60479154_%@", postId]]];
[[VKUtil systemApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://vk.com/wall-60479154_%@", postId]]];
} errorBlock:^(NSError *error) {
NSLog(@"Error: %@", error);
}];
Expand All @@ -235,7 +235,7 @@ - (void)uploadPhotos {
[post executeWithResultBlock:^(VKResponse *response) {
NSLog(@"Result: %@", response);
NSNumber *postId = response.json[@"post_id"];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://vk.com/wall-60479154_%@", postId]]];
[[VKUtil systemApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://vk.com/wall-60479154_%@", postId]]];
} errorBlock:^(NSError *error) {
NSLog(@"Error: %@", error);
}];
Expand All @@ -249,7 +249,7 @@ - (void)uploadInAlbum {
[request executeWithResultBlock:^(VKResponse *response) {
NSLog(@"Result: %@", response);
VKPhoto *photo = [(VKPhotoArray *) response.parsedModel objectAtIndex:0];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://vk.com/photo-60479154_%@", photo.id]]];
[[VKUtil systemApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://vk.com/photo-60479154_%@", photo.id]]];
} errorBlock:^(NSError *error) {
NSLog(@"Error: %@", error);
}];
Expand Down
5 changes: 3 additions & 2 deletions library/Source/Core/VKHTTPOperation.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "VKHTTPOperation.h"
#import "VKRequest.h"
#import "NSError+VKError.h"
#import "VKUtil.h"

NSString *const VKNetworkingOperationFailingURLRequestErrorKey = @"VKNetworkingOperationFailingURLRequestErrorKey";
NSString *const VKNetworkingOperationFailingURLResponseErrorKey = @"VKNetworkingOperationFailingURLResponseErrorKey";
Expand Down Expand Up @@ -122,7 +123,7 @@ - (id)initWithURLRequest:(NSURLRequest *)urlRequest {

- (void)dealloc {
if (_backgroundTaskIdentifier) {
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier];
[[VKUtil systemApplication] endBackgroundTask:_backgroundTaskIdentifier];
_backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
if (_successCallbackQueue) {
Expand Down Expand Up @@ -156,7 +157,7 @@ - (NSOutputStream *)outputStream {
- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler {
[self.lock lock];
if (!self.backgroundTaskIdentifier) {
UIApplication *application = [UIApplication sharedApplication];
UIApplication *application = [VKUtil systemApplication];
__weak __typeof(&*self) weakSelf = self;

self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
Expand Down
5 changes: 5 additions & 0 deletions library/Source/Utils/VKUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ Breaks key=value string to dictionary

+ (NSString *)queryStringFromParams:(NSDictionary *)params;

/**
* Workaround to make 'sharedApplication' still be called if compiling for AppExtensions
*/
+ (UIApplication *)systemApplication;

/**
* Indicates that the current device system version at least conforms the argument version
*/
Expand Down
4 changes: 4 additions & 0 deletions library/Source/Utils/VKUtil.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ + (NSNumber *)parseNumberString:(id)number {
return value;
}

+ (UIApplication *)systemApplication {
return [UIApplication performSelector:@selector(sharedApplication)];
}

+ (UIColor *)colorWithRGB:(NSInteger)rgb {
return [UIColor colorWithRed:((CGFloat) ((rgb & 0xFF0000) >> 16)) / 255.f green:((CGFloat) ((rgb & 0xFF00) >> 8)) / 255.f blue:((CGFloat) (rgb & 0xFF)) / 255.f alpha:1.0f];
}
Expand Down
38 changes: 31 additions & 7 deletions library/Source/VKAccessToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,34 @@ Create token with existing properties
+ (instancetype)tokenWithToken:(NSString *)accessToken secret:(NSString *)secret userId:(NSString *)userId;

/**
Retrieve token from user defaults. Token must be saved to defaults with saveTokenToDefaults method
@param defaultsKey path to file with saved token
@return parsed token
*/
Retrieve token from user defaults. Token must be saved to defaults with saveTokenToDefaults method

@param defaultsKey path to file with saved token
@return parsed token
*/
+ (instancetype)savedToken:(NSString *)defaultsKey;

/**
Save token into user defaults by specified key
@param defaultsKey key for defaults
Retrieve token from user defaults. Token must be saved to defaults with saveTokenToDefaults method

@param defaultsKey path to file with saved token
@param sharedGroupName keychain sharing group name
@return parsed token
*/
+ (instancetype)savedToken:(NSString *)defaultsKey sharedGroupName:(NSString *)sharedGroupName;

/**
Save token into user defaults by specified key

@param defaultsKey key for defaults
*/
- (void)saveTokenToDefaults:(NSString *)defaultsKey;
/**
Save token into user defaults by specified key

@param defaultsKey key for defaults
@param sharedGroupName keychain sharing group name
*/
- (void)saveTokenToDefaults:(NSString *)defaultsKey sharedGroupName:(NSString *)sharedGroupName;

/// Return YES if token has expired
- (BOOL)isExpired;
Expand All @@ -97,6 +114,13 @@ Save token into user defaults by specified key
@param service Access token storage key name
*/
+ (void)delete:(NSString *)service;
/**
Remove token from storage

@param service Access token storage key name
@param sharedGroupName keychain sharing group name
*/
+ (void)delete:(NSString *)service sharedGroupName:(NSString *)sharedGroupName;

@end

Expand Down
44 changes: 31 additions & 13 deletions library/Source/VKAccessToken.m
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,19 @@ - (instancetype)initWithVKAccessToken:(VKAccessToken *)token {
}

+ (instancetype)savedToken:(NSString *)defaultsKey {
return [self savedToken:defaultsKey sharedGroupName:nil];
}

+ (instancetype)savedToken:(NSString *)defaultsKey sharedGroupName:(NSString *)sharedGroupName {
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:defaultsKey];
if (data) {
VKAccessToken *token = [self tokenFromUrlString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:defaultsKey];
[[NSUserDefaults standardUserDefaults] synchronize];
[self save:defaultsKey data:token];
[self save:defaultsKey data:token sharedGroupName:sharedGroupName];
return token;
}
return [self load:defaultsKey];
return [self load:defaultsKey sharedGroupName:sharedGroupName];
}

#pragma mark - Expire
Expand All @@ -203,7 +207,11 @@ - (NSString *)accessToken {
#pragma mark - Save / Load

- (void)saveTokenToDefaults:(NSString *)defaultsKey {
[[self class] save:defaultsKey data:[self copy]];
[self saveTokenToDefaults:defaultsKey sharedGroupName:nil];
}

- (void)saveTokenToDefaults:(NSString *)defaultsKey sharedGroupName:(NSString *)sharedGroupName{
[[self class] save:defaultsKey data:[self copy] sharedGroupName:sharedGroupName];
}

- (id)copy {
Expand All @@ -214,29 +222,35 @@ - (id)mutableCopy {
return [[VKAccessTokenMutable alloc] initWithVKAccessToken:self];
}

+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service sharedGroupName:(NSString *)sharedGroupName {
/**
Simple keychain requests
Source: http://stackoverflow.com/a/5251820/1271424
*/

return [@{(__bridge id) kSecClass : (__bridge id) kSecClassGenericPassword,
(__bridge id) kSecAttrService : service,
(__bridge id) kSecAttrAccount : service,
(__bridge id) kSecAttrAccessible : (__bridge id) kSecAttrAccessibleAfterFirstUnlock} mutableCopy];
NSMutableDictionary *queryDictionary = [NSMutableDictionary new];
[queryDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[queryDictionary setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible];
[queryDictionary setObject:service forKey:(__bridge id)kSecAttrService];
[queryDictionary setObject:service forKey:(__bridge id)kSecAttrAccount];
if (sharedGroupName) {
[queryDictionary setObject:sharedGroupName forKey:(__bridge id)kSecAttrAccessGroup];
}

return queryDictionary;
}

+ (void)save:(NSString *)service data:(VKAccessToken *)token {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
+ (void)save:(NSString *)service data:(VKAccessToken *)token sharedGroupName:(NSString *)sharedGroupName {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service sharedGroupName:sharedGroupName];
SecItemDelete((__bridge CFDictionaryRef) keychainQuery);
keychainQuery[(__bridge id) kSecValueData] = [NSKeyedArchiver archivedDataWithRootObject:token];
OSStatus status = SecItemAdd((__bridge CFDictionaryRef) keychainQuery, NULL);
NSAssert(status == errSecSuccess, @"Unable to store VKAccessToken in keychain. OSStatus: %i. Error Description: https://www.osstatus.com/search/results?platform=all&framework=all&search=%i or https://developer.apple.com/reference/security/1658642-keychain_services", status, status);
}

+ (VKAccessToken *)load:(NSString *)service {
+ (VKAccessToken *)load:(NSString *)service sharedGroupName:(NSString *)sharedGroupName {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service sharedGroupName:sharedGroupName];
keychainQuery[(__bridge id) kSecReturnData] = (id) kCFBooleanTrue;
keychainQuery[(__bridge id) kSecMatchLimit] = (__bridge id) kSecMatchLimitOne;
CFDataRef keyData = NULL;
Expand All @@ -256,7 +270,11 @@ + (VKAccessToken *)load:(NSString *)service {
}

+ (void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
[self delete:service sharedGroupName:nil];
}

+ (void)delete:(NSString *)service sharedGroupName:(NSString *)sharedGroupName {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service sharedGroupName:sharedGroupName];
SecItemDelete((__bridge CFDictionaryRef) keychainQuery);
}

Expand Down
8 changes: 8 additions & 0 deletions library/Source/VKSdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ Initialize SDK with responder for global SDK events.
+ (instancetype)initializeWithAppId:(NSString *)appId
apiVersion:(NSString *)version;


/**
Enable sharing authorize data with keychain sharing

@param sharedGroupName your keychain sharing group name
*/
+ (void)enableKeychainSharingWithGroupName:(NSString *)sharedGroupName;

/**
Adds a weak object reference to an object implementing the VKSdkDelegate protocol.

Expand Down
37 changes: 24 additions & 13 deletions library/Source/VKSdk.m
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ @implementation VKSdk
static VKSdk *vkSdkInstance = nil;
static NSArray *kSpecialPermissions = nil;
static NSString *VK_ACCESS_TOKEN_DEFAULTS_KEY = @"VK_ACCESS_TOKEN_DEFAULTS_KEY_DONT_TOUCH_THIS_PLEASE";
static NSString *_vkSDKSharedGroupName = nil;


#pragma mark Initialization
Expand Down Expand Up @@ -172,19 +173,18 @@ + (void)authorize:(NSArray *)permissions withOptions:(VKAuthorizationOptions)opt

if (vkApp) {

UIApplication *application = [UIApplication sharedApplication];
UIApplication *application = [VKUtil systemApplication];

// Since iOS 9 there is a dialog asking user if he wants to allow the running app
// to open another app via URL. If user rejects, then no VK SDK callbacks are called.
// Fixing this using new -[UIApplication openURL:options:completionHandler:] method (iOS 10+).

#ifdef __AVAILABILITY_INTERNAL__IPHONE_10_0_DEP__IPHONE_10_0
if ([application respondsToSelector:@selector(openURL:options:completionHandler:)]) {
SEL openURLOptionsCompletion = @selector(openURL:options:completionHandler:);
if ([application respondsToSelector:openURLOptionsCompletion]) {

NSDictionary *options = @{ UIApplicationOpenURLOptionUniversalLinksOnly: @NO };

[application openURL:urlToOpen options:options completionHandler:^(BOOL success) {

void(^completionHandler)(BOOL) = ^(BOOL success) {
if (!success) {

VKMutableAuthorizationResult *result = [VKMutableAuthorizationResult new];
Expand All @@ -193,12 +193,19 @@ + (void)authorize:(NSArray *)permissions withOptions:(VKAuthorizationOptions)opt

[[VKSdk instance] notifyDelegate:@selector(vkSdkAccessAuthorizationFinishedWithResult:) obj:result];
}
}];
};

// Workaround for AppExtensions and compiler :)
IMP imp = [application methodForSelector:openURLOptionsCompletion];
void (*function)(id, SEL, NSURL *, NSDictionary *, void(^)(BOOL)) = (void *)imp;
function(application, openURLOptionsCompletion, urlToOpen, options, completionHandler);
} else {
[application openURL:urlToOpen];
// Workaround for AppExtensions and compiler :)
[application performSelector:@selector(openURL:) withObject:urlToOpen];
}
#else
[application openURL:urlToOpen];
// Workaround for AppExtensions and compiler :)
[application performSelector:@selector(openURL:) withObject:urlToOpen];
#endif

instance.authState = VKAuthorizationExternal;
Expand All @@ -221,17 +228,21 @@ + (void)authorize:(NSArray *)permissions withOptions:(VKAuthorizationOptions)opt
}

+ (BOOL)vkAppMayExists {
return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:VK_AUTHORIZE_URL_STRING]];
return [[VKUtil systemApplication] canOpenURL:[NSURL URLWithString:VK_AUTHORIZE_URL_STRING]];
}

#pragma mark Access token

+ (void)enableKeychainSharingWithGroupName:(NSString *)sharedGroupName {
_vkSDKSharedGroupName = sharedGroupName;
}

+ (void)setAccessToken:(VKAccessToken *)token {
[token saveTokenToDefaults:VK_ACCESS_TOKEN_DEFAULTS_KEY];
[token saveTokenToDefaults:VK_ACCESS_TOKEN_DEFAULTS_KEY sharedGroupName:_vkSDKSharedGroupName];

id oldToken = vkSdkInstance.accessToken;
if (!token && oldToken) {
[VKAccessToken delete:VK_ACCESS_TOKEN_DEFAULTS_KEY];
[VKAccessToken delete:VK_ACCESS_TOKEN_DEFAULTS_KEY sharedGroupName:_vkSDKSharedGroupName];
}

vkSdkInstance.authState = token ? VKAuthorizationAuthorized : VKAuthorizationInitialized;
Expand Down Expand Up @@ -392,7 +403,7 @@ + (void)forceLogout {
[[NSHTTPCookieStorage sharedHTTPCookieStorage]
deleteCookie:cookie];
}
[VKAccessToken delete:VK_ACCESS_TOKEN_DEFAULTS_KEY];
[VKAccessToken delete:VK_ACCESS_TOKEN_DEFAULTS_KEY sharedGroupName:_vkSDKSharedGroupName];

if (vkSdkInstance) {
vkSdkInstance.accessToken = nil;
Expand All @@ -407,7 +418,7 @@ + (BOOL)isLoggedIn {
}

+ (void)wakeUpSession:(NSArray *)permissions completeBlock:(void (^)(VKAuthorizationState, NSError *error))wakeUpBlock {
VKAccessToken *token = [self accessToken] ?: [VKAccessToken savedToken:VK_ACCESS_TOKEN_DEFAULTS_KEY];
VKAccessToken *token = [self accessToken] ?: [VKAccessToken savedToken:VK_ACCESS_TOKEN_DEFAULTS_KEY sharedGroupName:_vkSDKSharedGroupName];
VKSdk *instance = [self instance];
if (!token || token.isExpired) {
[instance resetSdkState];
Expand Down
3 changes: 2 additions & 1 deletion library/Source/Views/VKActivity.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ + (UIActivityCategory)activityCategory {
}

+ (BOOL)vkShareExtensionEnabled {
return [VKUtil isOperatingSystemAtLeastIOS8] && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"vk-share://extension"]];

return [VKUtil isOperatingSystemAtLeastIOS8] && [[VKUtil systemApplication] canOpenURL:[NSURL URLWithString:@"vk-share://extension"]];
}

- (NSString *)activityType {
Expand Down
16 changes: 14 additions & 2 deletions library/Source/Views/VKShareDialogController.m
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ - (CGSize)preferredContentSize {

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
UIInterfaceOrientation orientation = [[VKUtil systemApplication] statusBarOrientation];
[self rotateToInterfaceOrientation:orientation appear:YES];
}

Expand Down Expand Up @@ -1009,7 +1009,19 @@ - (void)sendMessage:(id)sender {
[textView becomeFirstResponder];

#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
[[[UIAlertView alloc] initWithTitle:nil message:VKLocalizedString(@"ErrorWhilePosting") delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];

SEL initSelector = @selector(initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:);
UIAlertView *alertView = [UIAlertView alloc];

NSString *title = VKLocalizedString(@"ErrorWhilePosting");
NSString *cancel = @"OK";

// Workaround for AppExtensions and compiler :)
IMP imp = [alertView methodForSelector:initSelector];
void (*function)(id, SEL, NSString *, NSString *, id, NSString *, id) = (void *)imp;
function(alertView, initSelector, nil, title, nil, cancel, nil);

[alertView show];
#else
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:VKLocalizedString(@"ErrorWhilePosting") preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil];
Expand Down