I am using the following code (showed on WWDC 2012 videos):
self.accountStore = [[ACAccountStore alloc] init];
ACAccountType *facebookAccountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
[self.accountStore requestAccessToAccountsWithType:facebookAccountType
withCompletionHandler:^(BOOL granted, NSError *e) {
if (granted)
{
NSArray *accounts = [self.accountStore
accountsWithAccountType:facebookAccountType];
self.facebookAccount = [accounts lastObject];
} else {
// Fail gracefully...
}
}];
I have also added the NSDictionary to my .plist file:
So, my problem is that I am receiving the following exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Access options are required for this account type.'
I have tried with this ACAccountTypeIdentifierTwitter and ACAccountTypeIdentifierSinaWeibo. I am not receiving any exception although they are always returning granted == NO
Well, the WWDC 2012 shows one thing, but the documentation shows another... The method they are using is now deprecated:
– requestAccessToAccountsWithType:withCompletionHandler: Deprecated in iOS 6.0
What you should do:
ACAccountType *facebookAccountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *options = #{
#"ACFacebookAppIdKey" : #"123456789",
#"ACFacebookPermissionsKey" : #[#"publish_stream"],
#"ACFacebookAudienceKey" : ACFacebookAudienceEveryone}; // Needed only when write permissions are requested
[self.accountStore requestAccessToAccountsWithType:facebookAccountType options:options
completion:^(BOOL granted, NSError *error) {
if (granted)
{
NSArray *accounts = [self.accountStore
accountsWithAccountType:facebookAccountType];
self.facebookAccount = [accounts lastObject];
} else {
NSLog(#"%#",error);
// Fail gracefully...
}
}];
Swift 3
let account = ACAccountStore()
let accountType = account.accountType(withAccountTypeIdentifier: ACAccountTypeIdentifierFacebook)
let options: [AnyHashable : Any] = [ACFacebookAppIdKey: "Your App ID on FB", ACFacebookPermissionsKey: ["publish_stream", "publish_actions"], ACFacebookAudienceKey: ACFacebookAudienceEveryone]
account.requestAccessToAccounts(with: accountType, options: options) { (success, error) in
if success {
if let accounts = account.accounts(with: accountType) {
if accounts.isEmpty {
print("No facebook account found, please add your facebook account in phone settings")
} else {
let facebookAccount = accounts.first as! ACAccount
let message = ["status": "My first Facebook posting "]
let requestURL = URL(string: "https://graph.facebook.com/me/feed")
let postRequest = SLRequest(forServiceType: SLServiceTypeFacebook,
requestMethod: SLRequestMethod.POST,
url: requestURL,
parameters: message)
postRequest?.account = facebookAccount
postRequest?.perform(handler: {(_, urlResponse,
error) in
if let err = error {
print("Error : \(err.localizedDescription)")
}
print("Facebook HTTP response \(String(describing: urlResponse?.statusCode))")
})
}
}
} else {
print("Facebook account error: \(String(describing: error))")
}
}
Related
Is it possible to share a video using SLRequest ?
I'm able to share Images using the same
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:requestURL parameters:message];
if (isImage)
{
NSData *data = UIImagePNGRepresentation(imgSelected);
[postRequest addMultipartData:data withName:#"media" type:#"image/png" filename:#"TestImage.png"];
}
postRequest.account = account;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if (!error)
{
NSLog(#"Upload Sucess !");
}
}];
I have been reading through the Twitter Video upload API documentation and its really pretty simple. You basically need to make 3 POST requests to their API. The video you are uploading is also limited to 15 MB in size.
Uploads using this endpoint require at least 3 calls, one to
initialize the request, which returns the media_id, one or more calls
to append/upload binary or base64 encoded data, and one last call to
finalize the upload and make the media_id usable with other resources.
So it works like this:
Request 1: Send a init request with the video size in bytes. This will return a Media ID number which we have to use in request 2 and 3.
Request 2: Use the returned Media ID number from request 1 to upload the video data.
Request 3: Once the video upload has finished, send a "FINALIZE" request back to the Twitter API. This lets the Twitter API know that all the chunks of the video file has finished uploading.
Note The Twitter API accepts video uploads in "chunks". So if your video file is quite big, you may want to split it up into more than one file and thus you will have to repeat "Request 2" more than once (not forgetting to increment the "segment_index" number each time).
I have had a go at coding this below. Try it and experiment around with it. I will update my answer later on to improve it too.
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Assign the mediatype to a string
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
// Check the media type string so we can determine if its a video
if ([mediaType isEqualToString:#"public.movie"]) {
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
NSData *webData = [NSData dataWithContentsOfURL:videoURL];
// Get the size of the file in bytes.
NSString *yourPath = [NSString stringWithFormat:#"%", videoURL];
NSFileManager *man = [NSFileManager defaultManager];
NSDictionary *attrs = [man attributesOfItemAtPath:yourPath error: NULL];
UInt32 result = [attrs fileSize];
//[self tweetVideoStage1:webData :result];
[self tweetVideo:webData :result :1 :#"n/a"];
}
}
-(void)tweetVideo:(NSData *)videoData :(int)videoSize :(int)mode :(NSString *)mediaID {
NSURL *twitterVideo = [NSURL URLWithString:#"https://upload.twitter.com/1.1/media/upload.json"];
// Set the parameters for the first twitter video request.
NSDictionary *postDict;
if (mode == 1) {
postDict = #{#"command": #"INIT",
#"total_bytes" : videoSize,
#"media_type" : #"video/mp4"};
}
else if (mode == 2) {
postDict = #{#"command": #"APPEND",
#"media_id" : mediaID,
#"segment_index" : #"0",
#"media" : videoData };
}
else if (mode == 3) {
postDict = #{#"command": #"FINALIZE",
#"media_id" : mediaID };
}
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:requestURL:twitterVideo parameters:postDict];
// Set the account and begin the request.
postRequest.account = account;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (!error) {
if (mode == 1) {
// Parse the returned data for the JSON string
// which contains the media upload ID.
NSMutableDictionary *returnedData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&error]
NSString *tweetID = [NSString stringWithFormat:#"%#", [returnedData valueForKey:#"media_id_string"]];
[self tweetVideo:videoData :result :2 :tweetID];
}
else if (mode == 2) {
[self tweetVideo:videoData :result :3 :mediaID];
}
}
else {
NSLog(#"Error stage %d - %", mode, error);
}
}];
}
Update - Twitter API errors - https://dev.twitter.com/overview/api/response-codes
In answer to your first comment, error 503 means that the Twitter servers are overloaded and can't handle your request right now.
503 Service Unavailable The Twitter servers are up, but overloaded
with requests. Try again later.
I know how to upload video to twitter use the new API. And I have tried it, it works.
Please check this: https://github.com/liu044100/SocialVideoHelper
You just need to call this class method.
+(void)uploadTwitterVideo:(NSData*)videoData account:(ACAccount*)account withCompletion:(dispatch_block_t)completion;
Hope it can resolve your problem.
Best Regards.
Been looking for sharing video on Twitter solution with below features:
Support chunk upload
Built-in support for user's credential retrieval
Since I couldn't find one meeting my need, so I decided to write one.
https://github.com/mtrung/TwitterVideoUpload
I've been testing for awhile now and it works well for me.
Hope it helps,
Regards.
Try this based in #Dan answer. It not tested, but I think it can work.
Use Cocoa-pods: pod 'TwitterKit'
if you don't use Pods try with fabric
//for Extern call
//Mode is 1
//MediaId is 0
- (void)uploadTwitterVideo:(NSData*)videoData videoTitle:(NSString *)title desc:(NSString *)desc withMode:(int)mode mediaID:(NSString *)mediaID withCompletion:(dispatch_block_t)completion
{
NSString *twitterPostURL = #"https://upload.twitter.com/1.1/media/upload.json";
NSDictionary *postParams;
if (mode == 1) {
postParams = #{#"command": #"INIT",
#"total_bytes" : [NSNumber numberWithInteger: videoData.length].stringValue,
#"media_type" : #"video/mp4"};
} else if (mode == 2) {
postParams = #{#"command": #"APPEND",
#"media_id" : mediaID,
#"segment_index" : #"0"};
} else if (mode == 3) {
postParams = #{#"command": #"FINALIZE",
#"media_id" : mediaID };
} else if (mode == 4) {
postParams = #{#"status": desc,
#"media_ids" : #[mediaID]};
}
TWTRAPIClient *twitterInstance = [[Twitter sharedInstance] APIClient];
NSError *error;
NSURLRequest *requestTw = [twitterInstance URLRequestWithMethod:#"POST" URL:twitterPostURL parameters:postParams error:&error];
[twitterInstance sendTwitterRequest:requestTw completion:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSLog(#"HTTP Response: %li, responseData: %#", (long)response, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
if (error) {
NSLog(#"There was an error:%#", [error localizedDescription]);
} else {
if (mode == 1) {
NSMutableDictionary *returnedData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&connectionError];
NSString *mediaIDResponse = [NSString stringWithFormat:#"%#", [returnedData valueForKey:#"media_id_string"]];
NSLog(#"stage one success, mediaID -> %#", mediaID);
[self uploadTwitterVideo:videoData videoTitle:title desc:desc withMode:2 mediaID:mediaIDResponse withCompletion:completion];
} else if (mode == 2) {
[self uploadTwitterVideo:videoData videoTitle:title desc:desc withMode:3 mediaID:mediaID withCompletion:completion];
} else if (mode == 3) {
[self uploadTwitterVideo:videoData videoTitle:title desc:desc withMode:4 mediaID:mediaID withCompletion:completion];
} else if (mode == 4) {
DispatchMainThread(^(){completion();});
}
}
}];
}
This API Works as follows.
- Login when application (twitter) is installed and when is not installed
- First priority take credential from setting
Check this case
Swift
Its very Simple.
First you need to sign in to your Twitter Account. Go to Phone Setting and click on twitter app and sign in.
Now Just Call this videoUpload func anywhere
Video or Chunked uploads Method Reference
Replace your video type/extension on that code
And Carefully read all twitter requirements.
var twitterAccount = ACAccount()
func videoUpload{
let path = Bundle.main.path(forResource: "file-Name", ofType:"mp4")
let filePath = path
var fileSize = UInt64()
do {
//return [FileAttributeKey : Any]
let attr = try FileManager.default.attributesOfItem(atPath: filePath!)
fileSize = attr[FileAttributeKey.size] as! UInt64
//if you convert to NSDictionary, you can get file size old way as well.
let dict = attr as NSDictionary
fileSize = dict.fileSize()
} catch {
print("Error: \(error)")
}
let accountStore = ACAccountStore()
let twitterAccountType = accountStore.accountType(withAccountTypeIdentifier: ACAccountTypeIdentifierTwitter)
accountStore.requestAccessToAccounts(with: twitterAccountType, options: nil) { (granted, error) in
if granted {
let accounts = accountStore.accounts(with: twitterAccountType)
if (accounts?.count)! > 0 {
self.twitterAccount = accounts?.last as! ACAccount
}}}
twitterAccount = Twitter.sharedInstance().sessionStore.session() as! ACAccount
uploadVideoToTwitter(videoURL: URL(string : path!)! as NSURL, fileSize: UInt32(fileSize))
}
func uploadVideoToTwitter(videoURL:NSURL,fileSize: UInt32) {
if let videoData = NSData(contentsOfFile: videoURL.path!){
self.tweetVideoInit(videoData: videoData, videoSize: Int(fileSize))
}
}
func tweetVideoInit(videoData:NSData,videoSize:Int) {
let uploadURL = NSURL(string:"https://upload.twitter.com/1.1/media/upload.json")
var params = [String:String]()
params["command"] = "INIT"
params["total_bytes"] = String(videoData.length)
params["media_type"] = "video/mp4"
let postRequest = SLRequest(forServiceType: SLServiceTypeTwitter,
requestMethod: SLRequestMethod.POST,
url: uploadURL as URL!,
parameters: params)
postRequest?.account = self.twitterAccount;
postRequest?.perform(handler: { ( responseData, urlREsponse,error) in
if let err = error {
print(error as Any)
}else{
do {
let object = try JSONSerialization.jsonObject(with: responseData! as Data, options: .allowFragments)
if let dictionary = object as? [String: AnyObject] {
if let tweetID = dictionary["media_id_string"] as? String{
self.tweetVideoApped(videoData: videoData, videoSize: videoSize, mediaId: tweetID, chunk: 0)
}
}
}
catch {
print(error)
}
}
})
}
func tweetVideoApped(videoData:NSData,videoSize:Int ,mediaId:String,chunk:NSInteger) {
let uploadURL = NSURL(string:"https://upload.twitter.com/1.1/media/upload.json")
var params = [String:String]()
params["command"] = "APPEND"
params["media_id"] = mediaId
params["segment_index"] = String(chunk)
let postRequest = SLRequest(forServiceType: SLServiceTypeTwitter,
requestMethod: SLRequestMethod.POST,
url: uploadURL as URL!,
parameters: params)
postRequest?.account = self.twitterAccount
postRequest?.addMultipartData(videoData as Data!, withName: "media", type: "video/mov", filename:"mediaFile")
postRequest?.perform(handler: { ( responseData, urlREsponse,error) in
if let err = error {
print(err)
}else{
self.tweetVideoFinalize(mediaId: mediaId)
}
})
}
func tweetVideoFinalize(mediaId:String) {
let uploadURL = NSURL(string:"https://upload.twitter.com/1.1/media/upload.json")
var params = [String:String]()
params["command"] = "FINALIZE"
params["media_id"] = mediaId
let postRequest = SLRequest(forServiceType: SLServiceTypeTwitter,
requestMethod: SLRequestMethod.POST,
url: uploadURL as URL!,
parameters: params)
postRequest?.account = self.twitterAccount;
postRequest?.perform(handler: { ( responseData, urlREsponse,error) in
if let err = error {
print(err)
}else{
do {
let object = try JSONSerialization.jsonObject(with: responseData! as Data, options: .allowFragments)
if let dictionary = object as? [String: AnyObject] {
self.postStatus(mediaId: mediaId)
}
}
catch {
print(error)
}
}
})
}
func postStatus(mediaId:String) {
let uploadURL = NSURL(string:"https://api.twitter.com/1.1/statuses/update.json")
var params = [String:String]()
params["status"] = "my first Video Upload"
params["media_ids"] = mediaId
let postRequest = SLRequest(forServiceType: SLServiceTypeTwitter,
requestMethod: SLRequestMethod.POST,
url: uploadURL as URL!,
parameters: params)
postRequest?.account = self.twitterAccount;
postRequest?.perform(handler: { ( responseData, urlREsponse,error) in
if let err = error {
print(err)
}else{
do {
let object = try JSONSerialization.jsonObject(with: responseData! as Data, options: .allowFragments)
if let dictionary = object as? [String: AnyObject] {
print("video uploaded")
}
}
catch {
print(error)
}
}
})
}
I was able to upload video to twitter successfully!
Below are steps referred from twitter docs:
Request for twitter account
accountStore.requestAccessToAccounts(with: twitterAccountType,options:nil){(granted, error) in
POST media/upload (INIT)
params["command"] = "INIT"
params["total_bytes"] = String(videoData.length)
params["media_type"] = "video/mov"
POST media/upload (APPEND)
params["command"] = "APPEND"
params["media_id"] = mediaId
params["segment_index"] = String(chunk)
POST media/upload (FINALIZE)
params["command"] = "FINALIZE"
params["media_id"] = mediaId
POST media/upload
params["status"] = twitterDescription
params["media_ids"] = mediaId
Here is twitter doc link https://dev.twitter.com/rest/media/uploading-media.html
Please fine the detailed solution for video upload to twitter using SLRequest here.
http://swiftoverflow.blogspot.in/2017/04/upload-video-to-twitter-using-slrequest.html
I am trying to share location along with image to facebook. I have successfully shared image but unable to share location. Below is my code of sharing image.
UIImage *facebookImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#",imagesURL,str]]]];
NSMutableDictionary* params = [[NSMutableDictionary alloc] init];
[params setObject:#"New Happening created on the HappenShare mobile app." forKey:#"message"];
[params setObject:facebookImage forKey:#"picture"];
[FBRequestConnection startWithGraphPath:#"me/photos" parameters:params HTTPMethod:#"POST" completionHandler:^(FBRequestConnection *connection,id result,NSError *error)
{
if (error)
{
NSLog(#"error : %#",error);
}
else
{
NSLog(#"Result : %#",result);
}
}];
Now for sharing location what parameter should I add in above code. I am attaching an image also to understand better that how the shared location will look like.Below image shows that how the image with text will indicate a location into map. Please suggest me a solution for that.
Along with "message" and you also need "place" ID to post as param.
Request for a "publish_actions" so that you can post a place/location.
Below is the code i've used:
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:#"Hello World" forKey:#"message"];
[params setObject:#"110503255682430"/*sample place id*/ forKey:#"place"];
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"/me/feed" parameters:params HTTPMethod:#"POST"] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
NSLog(#"result %#",result);
NSLog(#"error %#",error);
}];
You can also check this with the "administrator/tester" accounts given in Developer page -> your app -> Roles.
Use Graph explorer for better practice:
https://developers.facebook.com/tools/explorer/108895529478793?method=POST&path=me%2Ffeed%3F&version=v2.5&message=Hello%20world&place=110503255682430
Below code may help you in getting place id near your location:
NSMutableDictionary *params2 = [NSMutableDictionary dictionaryWithCapacity:4L];
[params2 setObject:[NSString stringWithFormat:#"%#,%#",YourLocation latitude,YourLocation longitude] forKey:#"center"]; //Hard code coordinates for test
[params2 setObject:#"place" forKey:#"type"];
[params2 setObject:#"100"/*meters*/ forKey:#"distance"];
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"/search" parameters:params2 HTTPMethod:#"GET"] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
NSLog(#"RESPONSE!!! /search");
}];
OR
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"/search?type=place¢er=YourLocationLat,YourLocationLong&distance=500" parameters:nil HTTPMethod:#"GET"] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
NSLog(#"result %#",result);
}];
Hope it helps you..
Swift 3.2 version of #Satish A answer.
func getPlaceId() {
let locManager = CLLocationManager()
locManager.requestWhenInUseAuthorization()
var currentLocation = CLLocation()
if( CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == .authorizedAlways) {
currentLocation = locManager.location!
let param = ["center":"\(currentLocation.coordinate.latitude),\(currentLocation.coordinate.longitude)","type":"place","distance":"100"]
FBSDKGraphRequest(graphPath: "/search", parameters: param).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil) {
guard let data = result as? NSDictionary else {
return
}
guard let arrPlaceIDs = data.value(forKey: "data") as? [NSDictionary] else {
return
}
guard let firstPlace = arrPlaceIDs.first else {
return
}
//First facebook place id.
print(firstPlace.value(forKey: "id") as! String)
} else {
print(error?.localizedDescription ?? "error")
}
})
}
}
For Facebook sharing with attaching placeId
let photo = Photo(image: img, userGenerated: true)
var content = PhotoShareContent()
content.photos = [photo]
content.placeId = id //Facebook placeId
let sharer = GraphSharer(content: content)
sharer.failsOnInvalidData = true
do {
try sharer.share()
} catch {
print("errorrrr")
}
sharer.completion = { FBresult in
switch FBresult {
case .failed(let error):
print(error)
break
case .success(_):
//code
break
default:
break
}
}
You need to set the place (which is actually a Page ID) field within the POST request on /me/photos.
See https://developers.facebook.com/docs/graph-api/reference/v2.0/user/photos/#publish
On iOS, you can use the PlacePicker UI component of the FB iOS SDK for that, as described here: https://developers.facebook.com/docs/ios/ui-controls#placepicker
I am using facebook SSON in My application. When i am calling my method [self openSessionWithAllowLoginUI:NO]; Within the block, its crashing with the following error Message.
*** Assertion failure in -[FBSession checkThreadAffinity], /Users/chrisp/tmp/build-sdk/ios-sdk/src/FBSession.m:1571
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'FBSession: should only be used from a single thread'
*** First throw call stack:
(0x2f2c2fd3 0x39a42ccf 0x2f2c2ead 0x2fc6fd5b 0x407c27 0x4050a5 0x406ab1 0x405c43 0x40662d 0x39f2a833 0x39f2a81f 0x39f2a777 0x2f28d8f1 0x2f28c1c5 0x2f1f6f4f 0x2f1f6d33 0x340fb663 0x31b4216d 0x7fdc7 0x2f0c0)
libc++abi.dylib: terminating with uncaught exception of type NSException
I know it says something like i am calling the FBSession from two or more threads, where as i should call it from a single thread but i am not getting where else i am calling this thread.
My Code for this is:-
-(void)facebook
{
ACAccountStore *accountStore;
accountStore = [[ACAccountStore alloc]init];
ACAccountType *FBaccountType= [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *dictFB = [NSDictionary dictionaryWithObjectsAndKeys:kFACEBOOK_APPID,ACFacebookAppIdKey,#[#"email"],ACFacebookPermissionsKey, nil];
[accountStore requestAccessToAccountsWithType:FBaccountType options:dictFB completion:
^(BOOL granted, NSError *e) {
if (granted)
{
NSArray *accounts = [accountStore accountsWithAccountType:FBaccountType];
if ([accounts count] == 0) {
}
else{
ACAccount *facebookAccount;
NSLog(#"Facebook account Found");
facebookAccount = [accounts firstObject];
ACAccountCredential *facebookCredential = [facebookAccount credential];
NSString *accessToken = [facebookCredential oauthToken];
NSLog(#"Facebook Access Token: %#", accessToken);
NSLog(#"facebook account =%#",facebookAccount);
}
}
else
{
NSLog(#"error getting permission %#",e);
[self openSessionWithAllowLoginUI:NO];
}
}];
}
But if i am not calling from with in the block its working fine, But I need to implement Facebook SSO directly from settings app for which it need to be called with in the block.
Without block I am calling it like this:-
-(void)facebook
{
ACAccountStore *accountStore;
accountStore = [[ACAccountStore alloc]init];
ACAccountType *FBaccountType= [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *dictFB = [NSDictionary dictionaryWithObjectsAndKeys:kFACEBOOK_APPID,ACFacebookAppIdKey,#[#"email"],ACFacebookPermissionsKey, nil];
[self openSessionWithAllowLoginUI:NO];
}
Please let me know if you need more of the code.
Try do
dispatch_async(dispatch_get_main_queue(), ^{
[self openSessionWithAllowLoginUI:NO];
});
This is my facebook method:
- (void)getMeFacebook{
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
__block ACAccount *facebookAccount = nil;
ACAccountType *facebookAccountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
// Specify App ID and permissions
NSDictionary *options = #{ACFacebookAppIdKey : #"1405219416361729",
ACFacebookPermissionsKey : #[#"email", #"publish_stream"],
ACFacebookAudienceKey:ACFacebookAudienceFriends};
[accountStore requestAccessToAccountsWithType:facebookAccountType
options:options completion:^(BOOL granted, NSError *error)
{
if (granted)
{
NSArray *accounts = [accountStore accountsWithAccountType:facebookAccountType];
facebookAccount = [accounts lastObject];
NSLog(#"Granted!!!");
}
else {
NSLog(#"error.localizedDescription======= %#", error.localizedDescription);
}
}];
NSArray *accounts = [accountStore accountsWithAccountType:facebookAccountType];
facebookAccount = [accounts lastObject];
NSLog(#"Break 1");
When debugging the code, it never evaluates if(granted), seems like the method requestAccessToAccountsWithType is not behaving correctly.I have already check for the facebook settings in the app but still not working.
Any idea of this issue?
After some hours of debugging and research i reach the solution, everytime you have to debug a block (callback) of a method in objective-c, its needed to add extra breaktimes inside the block.
The issue was a problem of synchronism, as the method was call in the main.m and the UIAlert requesting permission to access facebook data in the iPhone never appears. I move the call of the method to the viewDidLoad and the entire app work ok!
I'm attempting to get an account from ACAccountStore using the following code:
- (void) facebookLoginOnSuccess: (void (^)(void)) successBlock onError:(void(^)(NSError *error))errorBlock{
self.facebookPermissions = #[
#"offline_access",
#"publish_stream",
#"user_birthday",
#"user_location",
#"email"
];
NSDictionary *options = #{
#"ACFacebookAppIDKey": [[NSBundle mainBundle] objectForInfoDictionaryKey:#"FacebookAppID"],
#"ACFacebookAppVersionKey": #"1.0",
#"ACFacebookPermissionsKey": self.facebookPermissions,
#"ACFacebookPermissionGroupKey": #"write"
};
[self accountLoginFor:ACAccountTypeIdentifierFacebook withOptions:options OnSuccess:successBlock onError:errorBlock];
}
- (void) accountLoginFor: (NSString *) accountTypeID withOptions: (NSDictionary *) options OnSuccess: (void (^)(void)) successBlock onError:(void(^)(NSError *error))errorBlock{
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:accountTypeID];
[accountStore requestAccessToAccountsWithType:accountType
options:options
completion:^(BOOL granted, NSError *error){
if (granted){
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
NSLog(#"%#",accountsArray);
}
else {
NSLog(#"Error accessing account: %#", [error localizedDescription]);
}
}];
}
But I'm getting this error:
Error Domain=com.apple.accounts Code=6 "The operation couldn't be completed. (com.apple.accounts error 6.)"
And I can't find anything related, just this question. Any ideas what could be wrong?
Update
I found this on the Apple Developer Docs.
Accounts Framework
When requesting access to Facebook accounts, the only key required in your options dictionary is ACFacebookAppIdKey. ACFacebookPermissionGroupKey and ACFacebookAppVersionKey are now obsolete.
If you request a write permission under ACFacebookPermissionsKey, such as publish_stream, you must provide a value for ACFacebookAudienceKey, which can be one of ACFacebookAudienceEveryone, ACFacebookAudienceFriends, or ACFacebookAudienceOnlyMe.
So I changed my options to:
NSDictionary *options = #{
#"ACFacebookAppIDKey": [[NSBundle mainBundle] objectForInfoDictionaryKey:#"FacebookAppID"],
#"ACFacebookPermissionsKey": self.facebookPermissions,
#"ACFacebookAudienceKey": ACFacebookAudienceFriends
};
But I'm getting the same error.
Ok so if you haven't setup an account from your Settings in iOS 6 it throws error code 6. If the user simply denies permission than it throws error code 7. In case 6 i suggest you ask the user to first setup her account in Settings.
NSDictionary *options = #{
ACFacebookAppIdKey: #"1234567890",
ACFacebookPermissionsKey: #[#"publish_stream"],
ACFacebookAudienceKey: ACFacebookAudienceFriends
};
[self.accountStore requestAccessToAccountsWithType:facebookAccountType options:options completion:^(BOOL granted, NSError *error)
{
if (granted)
{
NSArray *accounts = [self.accountStore accountsWithAccountType:facebookAccountType];
if([accounts count]>0)
self.facebookAccount = [accounts lastObject];
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
// Fail gracefully...
NSLog(#"%#",error.description);
if([error code]== ACErrorAccountNotFound)
[self throwAlertWithTitle:#"Error" message:#"Account not found. Please setup your account in settings app."];
else
[self throwAlertWithTitle:#"Error" message:#"Account access denied."];
});
}
}];
jAmi has the right idea, but I don't like the idea of the magic numbers in the code. There is an enum that has the numbers built into it (ACErrorCode).
My own solution was to use Facebook SDK instead of trying with the lib directly and now it's working.