Skip to content
This repository has been archived by the owner on Dec 18, 2022. It is now read-only.

Commit

Permalink
0.6.0: Casper token cache
Browse files Browse the repository at this point in the history
- Fixed a bug where the wrong JWT was sent to Casper upon login
- Implemented a Casper token cache
  • Loading branch information
NSExceptional committed Feb 22, 2016
1 parent 8ddc7ab commit bb44e68
Show file tree
Hide file tree
Showing 9 changed files with 2,611 additions and 2,404 deletions.
4,570 changes: 2,294 additions & 2,276 deletions Example/Pods/Pods.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Example/SnapchatKit-OSX/SnapchatKit-OSX/main.m
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ int main(int argc, const char * argv[]) {
[SKClient sharedClient].casperUserAgent = kCasperUserAgent;
#endif

// [[SKClient sharedClient] signInWithUsername:kUsername password:kPassword completion:^(NSDictionary *dict, NSError *error) {
[[SKClient sharedClient] restoreSessionWithUsername:kUsername snapchatAuthToken:kAuthToken doGetUpdates:^(NSError *error) {
if (!error) {
SKSession *session = [SKClient sharedClient].currentSession;
Expand Down
32 changes: 0 additions & 32 deletions Example/SnapchatKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@
6003F587195388D20070C39A /* Frameworks */,
6003F588195388D20070C39A /* Resources */,
6FC020030A9FFD4336F9ED58 /* Copy Pods Resources */,
C106B3D66706B81CB1C510C4 /* Embed Pods Frameworks */,
);
buildRules = (
);
Expand All @@ -231,7 +230,6 @@
6003F5AB195388D20070C39A /* Frameworks */,
6003F5AC195388D20070C39A /* Resources */,
EEA01C34554587095871E45D /* Copy Pods Resources */,
D54B0F88C0CDC1A389FC9322 /* Embed Pods Frameworks */,
);
buildRules = (
);
Expand Down Expand Up @@ -345,36 +343,6 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
C106B3D66706B81CB1C510C4 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SnapchatKit_Example/Pods-SnapchatKit_Example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
D54B0F88C0CDC1A389FC9322 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SnapchatKit_Tests/Pods-SnapchatKit_Tests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
EEA01C34554587095871E45D /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand Down
16 changes: 16 additions & 0 deletions Pod/Classes/Categories/NSMutableURLRequest+Util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// NSMutableURLRequest+Util.h
// Pods
//
// Created by Tanner on 2/21/16.
//
//

#import <Foundation/Foundation.h>


@interface NSMutableURLRequest (Util)

+ (instancetype)POST:(NSString *)url body:(NSDictionary *)body headers:(NSDictionary *)headers;

@end
30 changes: 30 additions & 0 deletions Pod/Classes/Categories/NSMutableURLRequest+Util.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// NSMutableURLRequest+Util.m
// Pods
//
// Created by Tanner on 2/21/16.
//
//

#import "NSMutableURLRequest+Util.h"
#import "NSString+SnapchatKit.h"


@implementation NSMutableURLRequest (Util)

+ (instancetype)POST:(NSString *)urlString body:(NSDictionary *)body headers:(NSDictionary *)headers {
NSURL *url = [NSURL URLWithString:urlString];
if (!url) { [NSException raise:NSInvalidArgumentException format:@"Tried to create an NSURL with a malformed URL string: %@", urlString]; }

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [[NSString queryStringWithParams:body] dataUsingEncoding:NSUTF8StringEncoding];

[headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
[request setValue:value forHTTPHeaderField:key];
}];

return request;
}

@end
44 changes: 44 additions & 0 deletions Pod/Classes/Model/SKCasperCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// SKCasperCache.h
// Pods
//
// Created by Tanner on 2/21/16.
//
//

#import <Foundation/Foundation.h>


/// See the SKCasperCache class for an example implementaiton.
@protocol SKCasperCache <NSObject>
+ (id)fromDictionary:(NSDictionary *)oldCache;
@property (nonatomic, readonly) NSDictionary *dictionaryValue;

/// Add entries to the cache. Takes a full response from the casper serves.
- (void)update:(NSDictionary *)response;
/// Remove all cache entries
- (void)clear;
/// Expected to return the same data returned from the Casper API for a given endpoint.
- (NSDictionary *)objectForKeyedSubscript:(NSString *)endpoint;
@end


@interface SKCasperCache : NSObject <SKCasperCache>

/// Use this for new caches
- (instancetype)init;
/// @return nil if the file was empty or could not be opened.
+ (instancetype)fromDictionary:(NSDictionary *)oldCache;

/// Useful for serialization
@property (nonatomic, readonly) NSDictionary *dictionaryValue;
- (void)clear;

- (void)update:(NSDictionary *)response;

/// @return The data for the given endpoint with the keys "headers" and "params"
- (NSDictionary *)dataForEndpoint:(NSString *)endpoint;
/// Same as dataForEndpoint:
- (NSDictionary *)objectForKeyedSubscript:(NSString *)endpoint;

@end
104 changes: 104 additions & 0 deletions Pod/Classes/Model/SKCasperCache.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// SKCasperCache.m
// Pods
//
// Created by Tanner on 2/21/16.
//
//

#import "SKCasperCache.h"
#import "SnapchatKit-Constants.h"
#import "NSDictionary+SnapchatKit.h"

@interface SKCasperCache ()
@property (nonatomic) NSMutableDictionary *cache;
@end

@implementation SKCasperCache

#pragma mark - Initializers

- (instancetype)init {
self = [super init];
if (self) {
_cache = [NSMutableDictionary dictionary];
}

return self;
}

+ (instancetype)fromDictionary:(NSDictionary *)oldCache {
NSDate *now = [NSDate date];
NSMutableDictionary *cache = oldCache.mutableCopy;

// Remove expired objects now or at a later date
for (NSDictionary *endpoint in oldCache) {
if ([now compare:endpoint[@"expires"]] == NSOrderedDescending) {
[cache removeObjectForKey:endpoint[@"endpoint"]];
} else {
NSInteger timeLeft = [endpoint[@"expires"] timeIntervalSinceDate:now];
[self performSelector:@selector(safelyRemoveDataForEndpoint:) withObject:endpoint[@"endpoint"] afterDelay:timeLeft];
}
}

// Fill out and return new cache
SKCasperCache *ret = [SKCasperCache new];
ret.cache = cache;
return ret;
}

#pragma mark - Public interface

- (void)update:(NSDictionary *)response {
if ([response[@"code"] integerValue] == 200) {
if ([response[@"force_expire_cached"] boolValue]) {
[_cache removeAllObjects];
}

// Cache the objects with a specified expiration date
for (NSDictionary *endpoint in response[@"endpoints"]) {
NSInteger cacheTime = [endpoint[@"cache_millis"] floatValue]/1000;
NSMutableDictionary *m = endpoint.mutableCopy;

// Content-Type header for specific endpoints
if ([endpoint[@"endpoint"] isEqualToString:SKEPStories.upload] || [endpoint[@"endpoint"] isEqualToString:SKEPSnaps.upload]) {
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", SKConsts.boundary];
m[@"headers"] = SKMergeDictionaries(endpoint[@"headers"], @{SKHeaders.contentType: contentType});
}

m[@"expires"] = [NSDate dateWithTimeIntervalSinceNow:cacheTime];
_cache[endpoint[@"endpoint"]] = m.copy;

// Remove object from cache after delay
[self performSelector:@selector(safelyRemoveDataForEndpoint:) withObject:endpoint[@"endpoint"] afterDelay:cacheTime];
}
}
}

- (void)clear {
[_cache removeAllObjects];
}

- (NSDictionary *)dictionaryValue { return _cache.copy; }

- (NSDictionary *)dataForEndpoint:(NSString *)endpoint {
return _cache[endpoint];
}

- (NSDictionary *)objectForKeyedSubscript:(NSString *)endpoint {
return _cache[endpoint];
}

#pragma mark - Private

- (void)safelyRemoveDataForEndpoint:(NSString *)endpoint {
if ([NSThread isMainThread]) {
[_cache removeObjectForKey:endpoint];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[_cache removeObjectForKey:endpoint];
});
}
}

@end
20 changes: 15 additions & 5 deletions Pod/Classes/Networking/SKClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import "SnapchatKit-Constants.h"

#import "SKSession.h"
#import "SKCasperCache.h"

extern NSString *SKMakeCapserSignature(NSDictionary *params, NSString *secret);

Expand All @@ -32,6 +33,7 @@ typedef NS_ENUM(NSUInteger, SKScreenIdiom) {
- (void)restructureJSON:(NSDictionary *)json error:(NSError *)error response:(NSURLResponse *)response completion:(ResponseBlock)completion;
@end


@interface SKClient : NSObject

/** The default Snapchat session manager. To use more than one account, simply create and manage your own instances of \c SKClient instead of using the singleton. */
Expand All @@ -46,6 +48,12 @@ typedef NS_ENUM(NSUInteger, SKScreenIdiom) {
/** See the \c SKMiddleMan protocol. */
@property (nonatomic) id<SKMiddleMan> middleMan;

/** Used internally to cache header request tokens from the Casper API.
@discussion See the \c SKCasperCache protocol or class.
@warning You may use your own custom cache if you wish. It is cleared automatically
when you log out. */
@property (nonatomic) id<SKCasperCache> cache;

/** The size of your device's screen. On iOS, this defaults to the actual screen size. */
@property (nonatomic) CGSize screenSize;
/** The maxium sized to load videos in. On iOS, this defaults to the actual screen size. */
Expand Down Expand Up @@ -79,18 +87,20 @@ typedef NS_ENUM(NSUInteger, SKScreenIdiom) {

#pragma mark Signing in
/** Signs into Snapchat.
@warning Clears the Casper cache before calling the completion block.
@discussion A valid GMail account is necessary to trick Snapchat into thinking we're using the first party client. Your data is only ever sent to Google, Scout's honor.
@param username The Snapchat username to sign in with.
@param password The password to the Snapchat account to sign in with.
@param completion Takes an error, if any, and the JSON response from signing in as a dictionary. */
- (void)signInWithUsername:(NSString *)username password:(NSString *)password completion:(DictionaryBlock)completion;
/** Use this to restore a session that ended within the last hour. The google auth token must be re-generated every hour.
@warning Clears the Casper cache before calling the completion block.
@discussion If you have a stale Google auth token, consider using \c -restoreSessionWithUsername:snapchatAuthToken:doGetUpdates:.
@param username Your Snapchat username.
@param authToken Your Snapchat auth token. Can be retrieved from the \c authToken property.
@param completion Takes an error, if any. */
- (void)restoreSessionWithUsername:(NSString *)username snapchatAuthToken:(NSString *)authToken doGetUpdates:(ErrorBlock)completion;
/** Signs out.
/** Signs out and clears the Casper cache before calling the completion block.
@param completion Takes an error, if any. */
- (void)signOut:(ErrorBlock)completion;
/** Tells you if you're signed in or not. */
Expand All @@ -103,15 +113,15 @@ typedef NS_ENUM(NSUInteger, SKScreenIdiom) {

#pragma mark Registration
/**
The first step in creating a new Snapchat account. Registers an email, password, and birthday in preparation for creating a new account.
The first step in creating a new Snapchat account. Registers an email, password, and birthday in preparation for creating a new account.
The dictionary passed to completion has the following keys:
- \c email: the email you registered with.
- \c email: the email you registered with.
- \c snapchat_phone_number: a number you can use to verify your phone number later.
- \c snapchat_phone_number: a number you can use to verify your phone number later.
- \c username_suggestions: an array of available usernames for the next step.
- \c username_suggestions: an array of available usernames for the next step.
@param email The email address to be associated with the account.
@param password The password of the account to be created.
Expand Down
Loading

0 comments on commit bb44e68

Please sign in to comment.