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:
- 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.
- 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.
- 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:
- compiled libraries - * .a
- 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:
- 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
. - Be sure to specify the
Bundle ID
. Naturally, it should coincide with the one specified in the project settings. - Settings - Advanced: the
App Secret in Client
parameter App Secret in Client
set to No - 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:
ACAccountTypeIdentifierFacebook
- Facebook AccountsACAccountTypeIdentifierTwitter
- Twitter AccountsACAccountTypeIdentifierSinaWeibo
- 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:
- 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.
- 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.
- 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:
- The remote server doesn’t match the stored id.
- Facebook server couldn’t be installed.
- and a whole bunch of others
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
:
dict
. If you take a closer look, only ACFacebookPermissionsKey changes there. And in theory, one could use [dict setObject: forKey:]
. BUT, at the stage of writing the code, Xcode started issuing varnings that this method might not work. In the end, everything got stuck, started on the device and ... still fell, precisely on this method. Crash dumps, I repent, did not read, but simply walked around the problem in this way, putting myself a TODO for the future.fb_account_type
. It's all a little more complicated. Without re-creating the object, my application stably crashed on the second [store requestAccessToAccountsWithType:options:completion]
. Another TODO.
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:
- We receive
ACAccountType
. It is understood that all necessary permissions are obtained earlier. - Then we get all the accounts with this type and the permissions we need.
- From the resulting array, we take the very first element. I haven’t found anything concrete on this topic anywhere, but it seems like this is the account from the settings.
- Well, post.
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:
- 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. - 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.