📜 ⬆️ ⬇️

Development of Unity3d-plugin for working with Facebook

As an introduction


So, I will not talk about what social networks are and how they are used in gaming (and non-game) applications. Let me just say that once they set before me the task of teaching our game to publish all sorts of different things on Facebook and Twiter.
Our game is created using the Unity3d engine. There are no built-in opportunities for working with social networks in it. But there is an opportunity to write plug-ins on c / c ++ / objective c / ... Ie in native platform language. This was what I had to do. We develop the application for ipad, respectively, the iOS platform and the Objective-C language.
Below, I will tell you what and how I did it, I’ll share the code and ask a couple of questions to the distinguished community.
Immediately clarify that the plugin is not yet debugged to the end! But what to share is still there.

Is it necessary to create a bicycle?


There are several options for creating such a plugin:
  1. The first and probably the most logical is to buy! Yes, there are ready-made options, who are interested I can share a link to the most popular. The only disadvantage of this solution - Apple or developers from social. networks can easily change their API / Frameworks and it’s not a fact that the plug-in developer will immediately respond to this.
  2. Write using the Facebook SDK for iOS . Comrade Zuckerberg and his associates have written their SDK for native applications with hui, etc. It seems even turned out well.
  3. You can use the WebView and Client-side/Server-side options from here . But it seems to me that this is not very convenient for mobile applications, unless your application is not in HTML5

But there is another option, which, you guessed it, I will tell you. Apple in version 6 of iOS added tighter integration with Facebook. So close that in the settings of the devices appeared the same name in which it was possible to drive their username / password from the social. network. The same opportunity is for Twitter. At what Twitter in the settings appeared in earlier versions of iOS.
Apple provides fairly convenient Accounts , Social and Twitter frameworks for working with these accounts. The first - Accounts - provides access to the accounts specified by the user in the settings of the device / The basic idea is this - specify once the login / password of the account in the social. network and use it in all applications, without having to re-enter them every time. The second - Social - helps to form requests for social services. networks in accordance with their (network) requirements. And the third is that Twitter is not the topic of this article, because here I only consider working with Facebook.
Answering the question asked in the title of the paragraph, I’ll say: I personally think that it was not worthwhile to reinvent the bike and it was easier to buy, but I had a lot of free time and decided to try to build the plug myself. In addition, additional experience did not bother anyone.

General principles for creating plugins for Unity3d


Everything is simple - we put the plugin in Assets/Plugins/iOS and it is automatically hooked up by the engine during the next generation of the xcode project. The plugin can be:
  1. compiled libraries - * .a
  2. source codes are * .m, * .mm, * .cpp, * .c. And, of course, do not forget about the header files * .h (although they are not really needed)

When using C ++ or Objective-C code, exported functions must be declared in the C style in order to avoid problems with differences in the way functions are called.
And one more note - functions from plug-ins can be called only on specific devices, therefore, to avoid problems when launching the project in simulators, it is recommended that all imported (into the project from the plugin) functions be additionally wrapped in C # code that checks on which platform the project is launched .
Everybody is here.

Facebook application setup


You need to properly configure the application in Facebook before you start working with it. If you do not do this - you can catch a bunch of "mystical" and not very bugs.
Here are my recommendations for setting up:
  1. Specify the type of application Native iOS App . It does this on two pages: Settings - Basic, where they ask how your application is embedded in Facebook, and Settings - Advanced. Here we are interested in the Authentification section, the App type parameter -> Native/Desktop .
  2. Be sure to specify the Bundle ID . Naturally, it should coincide with the one specified in the project settings.
  3. Settings - Advanced: the App Secret in Client parameter App Secret in Client set to No
  4. Settings - Basic: Facebook Login - enabled

')

Algorithm of the plugin


Actually, the algorithm is quite simple - we get an account from the system, we request the rights to publish data in the social. network, publish. And now about each of the points in more detail.
As I wrote above, in the iOS SDK there is an Accounts framework, which provides access to user accounts specified in the settings. In fact, not only to them, but others do not interest us.
First you need to create an object of class ACAccountStore , which is a storage of accounts. Then we get an object of class ACAccountType , which will contain information about all accounts of the type that interests us.
Three types of accounts are supported:
  1. ACAccountTypeIdentifierFacebook - Facebook Accounts
  2. ACAccountTypeIdentifierTwitter - Twitter Accounts
  3. ACAccountTypeIdentifierSinaWeibo - and this is not interested in us, the Chinese social. network

Next, we must request access to the accounts of this type with the permissions we are interested in. Permissions are specific to each network and are specified as an array of strings.
After that, we can safely receive an account (provided, of course, that such accounts exist and the user has allowed all our Wishlist) and create with him everything we want.
It would seem that everything is simple and primitive. But no, not the case with Facebook.
The algorithm is a bit more complicated. In order for the iOS SDK, together with Facebook, not to send us at all, and so that we do not have to show the user an alert every time asking them to allow us something, we need to perform a special algorithm for requesting permissions:
  1. First, you must request access to some very primitive personal information. For example, to email. I, by the way, at the same time displayed that I ask for access to the user's personal information and a list of friends.
  2. Then (in the case of obtaining permission for the preceding paragraph), you must request read access to the tape — read_stream. The chip is that you cannot request publication rights before reading rights (and even more so without asking for read rights). Also, you cannot request access and reading rights in a single request.
  3. Well, now you can request the rights we are interested in - publish_stream

Read more about all possible rights / permissions here .
If you do not perform this algorithm, you can catch these errors:

Anyone interested can read Stackoverflow on this topic. There the people spread a lot of such mistakes!

So, I got about this code (I am recovering from memory, now it has already changed, but there is no repository with history at hand):
 ACAccountStore *store = [[ACAccountStore alloc] init]; ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook]; NSDictionary *dict = @{ACFacebookAppIdKey : fb_appid, ACFacebookPermissionsKey : @[@"email"], ACFacebookAudienceKey : ACFacebookAudienceEveryone}; [store requestAccessToAccountsWithType:fb_account_type options:dict completion:^(BOOL granted, NSError *error) { if (granted && error == nil) { ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook]; NSDictionary *dict = @{ACFacebookAppIdKey : fb_appid, ACFacebookPermissionsKey : @[@"read_permission"], ACFacebookAudienceKey : ACFacebookAudienceEveryone}; [store requestAccessToAccountsWithType:fb_account_type options:dict completion:^(BOOL granted, NSError *error) { if (granted && error == nil) { ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook]; NSDictionary *dict = @{ACFacebookAppIdKey : fb_appid, ACFacebookPermissionsKey : @[@"public_permission"], ACFacebookAudienceKey : ACFacebookAudienceEveryone}; [store requestAccessToAccountsWithType:fb_account_type options:dict completion:^(BOOL granted, NSError *error) { if (granted && error == nil) { //          } }]; } }]; } }]; 

I agree, it looks scary!
Immediately a couple notice about the constantly recreated dict and fb_account_type :

I simplified this code a bit:
 void _fb_request_access(NSArray *permissions, FBAccessGrantedHandler handler) { ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook]; NSDictionary *dict = @{ACFacebookAppIdKey : fb_appid, ACFacebookPermissionsKey : permissions, ACFacebookAudienceKey : ACFacebookAudienceEveryone}; [store requestAccessToAccountsWithType:fb_account_type options:dict completion:^(BOOL granted, NSError *error) { if (granted && error == nil) { handler(); } }]; } _fb_request_access(@[@"email"], ^() { _fb_request_access(@[@"read_stream"], ^() { _fb_request_access(@[@"publish_stream"], ^() { _fb_post_impl(post); }); }); }); 

Unfortunately, this code did not have time to protest - the ipad was taken away (and with the 6th version it is the only one with us). But the project was assembled without errors.

Posting on the wall


Now, about, actually, posting on the wall. I have a separate function for this - _fb_post_impl .
 void _fb_post_impl(NSString *text) { ACAccountType *fb_account_type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook]; NSArray *accounts = [store accountsWithAccountType:fb_account_type]; fb_account = [accounts objectAtIndex:0]; SLRequest *fb_request = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodPOST URL:[NSURL URLWithString:@"https://graph.facebook.com/me/feed"] parameters:[NSDictionary dictionaryWithObject:text forKey:@"message"]]; [fb_request setAccount:fb_account]; [fb_request performRequestWithHandler:^(NSData* responseData, NSHTTPURLResponse* urlResponse, NSError* error) { NSLog([[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding]); }]; } 

The algorithm is simple:

I now fast only text. To add a picture / link / caption / etc. need to supplement the parameters dictionary. See the list of possible fields here . I also advise you to read this if you need more than just posting on the wall.

Calling plugin functions in C #


Here, in fact, everything is simple and primitive.
  [DllImport ("__Internal")] private static extern void _fb_init(String appid); [DllImport ("__Internal")] private static extern void _fb_post(String text); public static void fb_init() { _fb_init(" application id"); } public static void fb_post(String text) { _fb_post(text); } 

A small note - to the type String from C #, corresponds to the const char* in the plugin. I, for example, tried to use NSString first.

Instead of conclusion


As a result, I managed to post the text (and not only) to my wall. Time, however, I spent nemer because of those small jambs about which I wrote - using the plug-in only on the device, a tricky algorithm for obtaining permissions.
The code is laid out on bitbucket - bitbucket.org/mnazarov/fb_plugin .
By the way, I ran into Objective-C for the first time, so please do not kick much, but treat with understanding. I will be glad to receive tips on how to improve the code.
Also I will be glad if someone can try it on their devices and write down how it works.
Well, I will be very happy if someone decides to help with the development!
Well, now a couple of questions:
  1. What I did wrong with the dictionary was that the [dict:setObject:forKey] method did not work for me. Given that Xcode knew about this method, and on the same Stackoverflow people use it in the same case as mine.
  2. The second question is more about debugging under the iPad - how to debug, how to catch "mystical" bugs. In particular, when debugging this plug-in, I observed the situation when the application crashed simply on the NSLog call!
    Tried to read crash dumps, but did not understand anything.


PS This article saved someone a couple of nerve cells.

Source: https://habr.com/ru/post/155435/


All Articles