How to implement ChimpKit3? - objective-c

I'm having big difficulties following the example in the ChimpKit readme on github.
NSDictionary *params = #{#"id": #"8ce4588803", #"email": #{#"email": #"foo#example.com"}, #"merge_vars": #{#"FNAME": #"Freddie", #"LName":#"von Chimpenheimer"}};
[[ChimpKit sharedKit] callApiMethod:#"lists/subscribe" withParams:params andCompletionHandler:^(ChimpKitRequest *request, NSError *error) {
NSLog(#"HTTP Status Code: %d", request.response.statusCode);
NSLog(#"Response String: %#", request.responseString);
if (error) {
//Handle connection error
NSLog(#"Error, %#", error);
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI here
});
} else {
NSError *parseError = nil;
id response = [NSJSONSerialization JSONObjectWithData:request.responseData
options:0
error:&parseError];
if ([response isKindOfClass:[NSDictionary class]]) {
id email = [response objectForKey:#"email"];
if ([email isKindOfClass:[NSString class]]) {
//Successfully subscribed email address
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI here
});
}
}
}
}];
Apparently this is the code to subscribe an email to a list in MailChimp
I am getting errors:
/SubscribeEmailViewController.m:146:105: Expected ')'
SubscribeEmailViewController.m:146:103: Type specifier missing,
defaults to 'int'
SubscribebeEmailViewController.m:147:44: Use of undeclared identifier
'request'
SubscribeEmailViewController.m:146:103: Incompatible block pointer
types sending 'int ((^)(void))' to parameter of type
'ChimpKitRequestCompletionBlock' (aka 'void (^)(NSURLResponse
*__strong, NSData *__strong, NSError *__strong)')
This is basically copy and paste from the example, I can't figure out what detail I'm missing here.
ChimpKit was installed via cocoapods

Related

Using RCTAsyncLocalStorage + getAllKeys

I'm trying to get the AsyncStorage on iOS native code. So this is my code
- (void)jsonFromLocalRNStrogeForKey:(NSString *)key completion:(void (^)(NSDictionary * _Nullable, NSError * _Nullable))completion {
RCTResponseSenderBlock rnCompletion = ^(NSArray *response) {
NSString *jsonAsString;
if (response.count > 1) {
NSArray *response1 = response[1];
if (response1.count > 0) {
NSArray *response2 = response1[0];
if (response2.count > 1) {
jsonAsString = response2[1];
}
}
}
#try {
NSData *jsonAsData = [jsonAsString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *json = [
NSJSONSerialization
JSONObjectWithData:jsonAsData
options:NSJSONReadingMutableContainers
error:&error
];
completion(json, error);
}
#catch (NSException *exception) {
NSLog(#"error: %#", exception.reason);
NSMutableDictionary * info = [NSMutableDictionary dictionary];
[info setValue:exception.name forKey:#"ExceptionName"];
[info setValue:exception.reason forKey:#"ExceptionReason"];
[info setValue:exception.callStackReturnAddresses forKey:#"ExceptionCallStackReturnAddresses"];
[info setValue:exception.callStackSymbols forKey:#"ExceptionCallStackSymbols"];
[info setValue:exception.userInfo forKey:#"ExceptionUserInfo"];
NSError *error = [[NSError alloc] initWithDomain:#"" code:1 userInfo:info];
completion(nil, error);
}
};
// RCTAsyncLocalStorage *storage = [RCTAsyncLocalStorage new];
RCTAsyncLocalStorage *storage = [[RCTAsyncLocalStorage alloc] init];
dispatch_async(storage.methodQueue, ^{
#try {
// [storage performSelector:#selector(multiGet:callback:) withObject:#[key] withObject:rnCompletion];
[storage performSelector:#selector(getAllKeys:callback:) withObject:rnCompletion];
}
#catch (NSException *exception) {
NSLog(#"error: %#", exception.reason);
}
});
}
When I try to get one of my keys (multiGet)
[self jsonFromLocalRNStrogeForKey:#"session" completion:^(NSDictionary* data,NSError* error) {
if (data) {
NSString * name = [data valueForKeyPath: #"token"];
if (![name isKindOfClass:[NSNull class]]) {
[self reportIncomingCallFrom:name withUUID:callInvite.uuid];
}
} else {
NSLog(#"error: JSON Parsing Error: %#",error.localizedFailureReason);
}
}];
I'm always getting null
And when I try to get all the keys (...#selector(getAllKeys:...) to see what do I have in my AsyncStorage I got the exception
#"NSInvalidArgumentException" - reason: #"-[RCTAsyncLocalStorage getAllKeys:callback:]: unrecognized selector sent to instance 0x1085512c0"
The RN have RCT_EXPORT_METHOD(getAllKeys:(RCTResponseSenderBlock)callback) in RCTAsyncLocalStorage.m; but at RCTAsyncLocalStorage.h (void)getAllKeys:(RCTResponseSenderBlock)callback it doesn't exists and even adding it doesn't work (https://github.com/facebook/react-native/blob/master/React/Modules/RCTAsyncLocalStorage.h).
"react-native": "^0.48.4",
How can I return NSJsonSerialization
Firstly,
The RN have
RCT_EXPORT_METHOD(getAllKeys:(RCTResponseSenderBlock)callback) in
RCTAsyncLocalStorage.m; but at RCTAsyncLocalStorage.h
(void)getAllKeys:(RCTResponseSenderBlock)callback it doesn't exists
In Objective-C, you can invoke a method even though it is not declared in the header file using performSelector:withObject:.
Invoking this method directly (without first checking if the target respondsToSelector:) is bad practice, as the internal method declaration may change.
Secondly, this line is incorrect:
[storage performSelector:#selector(getAllKeys:callback:) withObject:rnCompletion];
Here, you're saying getAllKeys:: takes two arguments, however the implementation declares only one.
Hence, the correct way to extract all keys is the following:
dispatch_async(storage.methodQueue, ^{
if([storage respondsToSelector:#selector(getAllKeys:)]){
[storage performSelector:#selector(getAllKeys:) withObject:[^(NSArray* response){
NSLog(#"Contents: %#",response);
} copy]];
}else{
NSLog(#"storage does not respond to selector `getAllKeys:`");
}
});

Creating NSError * results in application crash

I have the following situation:
My domain class gets some input validates it, and if validation passes it proceeds with saving data.
When control flow reaches the if statements, the application crashes
- (BOOL)createGmailAccountWithName:(NSString *)name
email:(NSString *)email
andPassword:(NSString *)password
error: (NSError **) error {
if (!name || name.length == 0) {
*error = [self createError:#"name"];
return NO;
}
if (!email || email.length == 0) {
*error = [self createError:#"email"];
return NO;
}
if (!password || password.length == 0) {
*error = [self createError:#"password"];
return NO;
}
//..
}
-(NSError *) createError: (NSString *) field {
NSString *errorMessage = [NSString stringWithFormat:#"Property %# is required", field];
NSDictionary *userInfo = #{
NSLocalizedFailureReasonErrorKey: NSLocalizedString(errorMessage, nil)
};
NSError *error = [NSError errorWithDomain:ACCOUNT_STORE_ERROR_DOMAIN
code:-1
userInfo:userInfo];
return error;
}
When I comment out all lines where the validation happens, the application does not crash.
I have no idea why this is happening. Can anyone point me into the right direction?
If you have this method:
- (BOOL)createGmailAccountWithName:(NSString *)name
email:(NSString *)email
andPassword:(NSString *)password
error: (NSError **) error
Folks are probably going to call it either like this:
NSError *error;
[accountCreator createGmailAccountWithName:#"Ben"
email:#"foo#example.com"
andPassword:#"pwd"
error:&error];
if (error)
{
NSLog(#"Hey I got an error: %#", error);
}
Or like this:
[accountCreator createGmailAccountWithName:#"Ben"
email:#"foo#example.com"
andPassword:#"pwd"
error:NULL];
// I couldn't care less about an error
In the second case, your code will will try to dereference **error, *error is not a valid pointer and would cause a crash.

How to stub a call to a method with a reference argument?

Consider this:
+(NSDictionary *)getDictionaryFromData:(id)data {
#synchronized(self) {
NSError *error = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (error) {
DLog(#"SERIALIZATION FAILED: %#", error.localizedDescription);
return nil;
}
DLog(#"SUCCESS: %#", dict);
return dict;
}
}
How do I mock getDictionaryFromData to get coverage if error is not nil? Is it possible or do I have to actually mock the JSONObjectWithData method?
For this answer I'm assuming you don't actually want to mock the getDictionaryFromData: method. I assume you want to test its implementation, how it deal with an error case.
You can stub that JSONObjectWithData:options:error: method and return an error in the pass by ref argument; somehow like this:
id serializerMock = [OCMock mockForClass:[NSJSONSerialization class]];
NSError *theError = /* create an error */
[[[serializerMock stub] andReturn:nil] JSONObjectWithData:data
options:NSJSONReadingAllowFragments error:[OCMArg setTo:theError]];
The trick here is obviously the setTo: method.
Here is what worked for me (OCMock 3):
id serializerMock = OCMClassMock(NSJSONSerialization.class);
NSError *theError = [[NSError alloc] initWithDomain:NSCocoaErrorDomain code:NSPropertyListWriteInvalidError userInfo:nil];
OCMStub(ClassMethod([serializerMock dataWithJSONObject:OCMOCK_ANY
options:0
error:[OCMArg setTo:theError]]));
Note: For the line [serializerMock dataWithJSONObject...] Xcode's code completion does not work.

Native Facebook/iOS6 integration: Cocoa error 3840 when trying to get profile picture

I am currently using only Social.framework (no FacebookSDK) and getting starting with requesting and getting access to basic user data. Here's all the code I have (with a few properties declared outside, as you'll notice). Everything works fine in terms of getting the right permissions, but I'm getting the following output error when asking for user's profile picture:
2013-01-27 21:28:44.324 TestApp[9230:1a703] Account saved to accountStore
2013-01-27 21:28:45.002 TestApp[9230:1d603] (null)
2013-01-27 21:28:45.003 TestApp[9230:1d603] Request error: The operation couldn’t be completed. (Cocoa error 3840.)
And here is the code:
- (IBAction)btnFbLoginPressed:(id)sender
{
ACAccountType *fbAccountType = [self.accountStore accountTypeWithAccountTypeIdentifier: ACAccountTypeIdentifierFacebook];
NSArray *permissions = #[#"email"];
self.requestAccessOptions = #{ACFacebookAppIdKey:FB_API_KEY, ACFacebookPermissionsKey:permissions, ACFacebookAudienceKey:ACFacebookAudienceOnlyMe};
[self.accountStore requestAccessToAccountsWithType:fbAccountType options:self.requestAccessOptions completion:^(BOOL granted, NSError *e) {
if (granted && e == nil) {
NSArray *readPermissions = #[#"user_photos"];
NSDictionary *readAcccessOptions = #{ACFacebookAppIdKey:FB_API_KEY, ACFacebookPermissionsKey:readPermissions, ACFacebookAudienceKey:ACFacebookAudienceOnlyMe};
[self.accountStore requestAccessToAccountsWithType:fbAccountType options:readAcccessOptions completion:^(BOOL granted, NSError *e) {
if (granted && e == nil) {
NSArray *accounts = [self.accountStore accountsWithAccountType:fbAccountType];
self.facebookAccount = [accounts lastObject];
[self.accountStore saveAccount:self.facebookAccount withCompletionHandler:^(BOOL success, NSError *error) {
if (error != nil || !success) {
NSLog(#"Error saving account to accountStore: %#", error.localizedDescription);
} else {
NSLog(#"Account saved to accountStore");
}
}];
NSString *uid = [NSString stringWithFormat:#"%#", [[self.facebookAccount valueForKey:#"properties"] valueForKey:#"uid"]];
NSString *url = [NSString stringWithFormat:#"https://graph.facebook.com/%#/picture", uid];
NSURL *profilePictureURL = [NSURL URLWithString:url];
SLRequest *profilePictureRequest = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodGET URL:profilePictureURL parameters:nil];
profilePictureRequest.account = self.facebookAccount;
[profilePictureRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *e)
{
NSDictionary *responseDataDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:&e];
NSLog(#"%#", responseDataDictionary);
if (e != nil) {
NSLog(#"Request error: %#", e.localizedDescription);
} else {
}
}];
} else {
NSLog(#"Read permissions request error: %#", e.localizedDescription);
}
}];
} else {
NSLog(#"Basic permissions request error: %#", e.localizedDescription);
}
}];
}
You can see that the reponseDataDictionary is null and something happens when parsing the data. I noticed a couple of other threads on SO about the same error code, but no luck so far. My guess is that either 1) there's something wrong with my Facebook code, or 2) I'm parsing the data incorrectly. Any help's appreciated!
Note: I would like to stick to using the Social/Account frameworks only.
UPDATE: Slight modification thanks to a suggestion in the comments.
Changed code:
[profilePictureRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *e)
{
NSLog(#"Request error value: %#", e.localizedDescription);
NSError *jsonError = nil;
NSDictionary *responseDataDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:&jsonError];
NSLog(#"Response data dictionary value: %#", responseDataDictionary);
if (jsonError != nil) {
NSLog(#"Serialization error: %#", jsonError.localizedDescription);
} else {
NSLog(#"Serialization successful");
}
}];
Output:
2013-01-28 18:55:16.265 TestApp[9565:1ae03] Account saved to accountStore
2013-01-28 18:55:17.640 TestApp[9565:1b503] Request error value: (null)
2013-01-28 18:55:17.640 TestApp[9565:1b503] Response data dictionary value: (null)
2013-01-28 18:55:17.642 TestApp[9565:1b503] Serialization error: The operation couldn’t be completed. (Cocoa error 3840.)
The profilePictureRequest is returning an image and not JSON. Use + (CIImage *)imageWithData:(NSData *)data to convert the response to an image.

How to comment on a post in facebook3.1 using graph api in ios

I use Facebook 3.1 and try comment on a particular post. I have used the following code. I have used the below mentioned permissions also while creating session:
read_stream,offline_access,publish_stream,share_item
The code:
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"This is my comment", #"message", nil];
NSString *string=[NSString stringWithFormat:#"https://graph.facebook.com/153125724720582_184234384932460/comments?access_token=%#",appDelegate.session.accessToken];
[FBRequestConnection startWithGraphPath:string
parameters:params
HTTPMethod:#"GET"
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error) {
if (error) {
NSLog(#"Error: %#", [error localizedDescription]);
} else {
NSLog(#"Result: %#", result);
}
}];
Response :
Result: {
comments = 1;
id = "https://graph.facebook.com/153125724720582_184234384932460/comments";
}
But my message is not updated on commented post id.
Same code with Post request also I have tried but Response is:
Result: {
"FACEBOOK_NON_JSON_RESULT" = false;
}
You should use an HTTP POST and make sure that the user has granted publish_stream permissions.
You should copy and paste the API call (and the access token) into the Graph Explorer to test your call out.
(Also use the access_token debugger to make sure the permission is there)
You should use this
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"Test Comment", #"message", nil];
[FBRequestConnection startWithGraphPath:#"153125724720582_184234384932460/comments"
parameters:params
HTTPMethod:#"POST"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (error)
NSLog(#"Error: %#", [error localizedDescription]);
else
NSLog(#"Result: %#", result);
}];