3 Sharing and referrals

3.1 Share directly to channels

Ready to integrate a breathtaking solution? Megacool makes it possible to get deeply integrated into the most popular services for a better user experience. Instead of showing the share modal view with a lot of options, it's possible to add your own buttons that share directly to the channels.

It's possible to customize the share channels below by passing a config as argument. More on that in the following sections.

We recommend to show buttons for the channels below and a general button that calls presentShare using this icon .

3.1.1 Messenger

Megacool has integrated the Messenger API. To share to Messenger you simply create a button with a Messenger icon that calls this method instead of presentShare:

[Megacool sharedMegacool] presentShareToMessenger];

In addition you also have to configure XCode according to Facebook's Getting Started guide: Configure Xcode Project

There are two ways for a user to share a GIF to Messenger, either by selecting friend(s) or send it as a reply to a previous conversation. Sending as a reply to a previous conversation requires that the user has opened your app from a conversation in Messenger.

Note: Sharing directly to Messenger doesn't support only sharing a URL. You have to provide a GIF and a fallback image, in case the GIF fails, or just a fallback image. Go to 4.7 Fallback image to learn how to add a fallback image.

Note 2: When your app is installed from a direct Messenger share, the receiver will never get a received share opened event. Hence you shouldn't use this for rewarded referrals. Opening shares with your app already installed works as expected.

3.1.2 Twitter

The native share feature to Twitter has a tiny issue. It converts GIFs to JPG. So we created a new share feature to Twitter to ensure users can share your GIFs to the world.

[Megacool sharedMegacool] presentShareToTwitter];

Note: The first time a user choose Twitter, a prompt will ask to get access to their Twitter account.

3.1.3 iMessage

Sharing directly to iMessage gives a better user experience.

[Megacool sharedMegacool] presentShareToMessages];

3.1.4 Mail

Sharing directly to Mail gives a better user experience.

[Megacool sharedMegacool] presentShareToMail];

3.2 Delegates after a share

To know when the Share modal view completes, assign the optional MCLDelegate for status calls and configuration to your ViewController.m:

#import <Megacool/Megacool.h>
@interface ViewController () <MCLDelegate>

Set the delegate

- (void)viewDidLoad{
[super viewDidLoad];
[[Megacool sharedMegacool] setDelegate:self];
}

Include these functions to get notified for the given events.

- (void)megacoolDidCompleteShare {
NSLog(@"Did complete share");
}
- (void)megacoolDidDismissShareView {
NSLog(@"Did dismiss share view");
}

3.3 Share object

megacoolShare is an object for everything related to a share. It contains a state of the share, createdAt, updatedAt, data and an url. With this, you can add your own data and a path and query which will be merged with the final link like this: 'https://mgcl.co/appId/level5/battle?_m=12345678&username=nbroby'.

NSURL *URL = [NSURL URLWithString:@"level5/battle?username=nbroby"];
NSDictionary *data = @{ @"welcomeText" : @"Nick has invited you!" };
MCLShare *share = [MCLShare shareWithURL:URL data:data];
[[Megacool sharedMegacool]
presentShareWithConfig:@{kMCLConfigShareKey:share}];

The share object can be passed as an argument to presentShare as shown above. When the receiving user presses the link, the app will open and the share object will be received through a MegacoolEvent in startWithAppConfig:andEventHandler which you can read more about in 3.4 below.

The final link will look like this:

3.4 Share state

Call getShares:callback to get the latest state of a sent share. The locally cached shares will be returned immediately and are useful to determine the total number of shares. If the block is non-nil a request will be sent to the server to get the latest state update for each share.

States can be:

  • MCLShareStateSent
  • MCLShareStateClicked
  • MCLShareStateOpened
  • MCLShareStateInstalled
NSArray *shares = [[Megacool sharedMegacool]
getShares:^(NSArray<MCLShare *> *shares) {
//Handled updated shares
for (MCLShare *share in shares) {
if (share.state == MCLShareStateClicked) {
NSLog(@"Share sent on %@ was clicked", share.createdAt);
}
}
}];

You can use the share state to show the user how many friends users have invited and what the state is:

To delete local shares you don’t need updates from anymore, simply call deleteSharesMatchingFilterFromDevice:filter, where filter is a block with MegacoolShare as an argument. Based on state, createdAt, updatedAt etc. you can return YES to delete the share object or NO to keep it. If you call this method around getShares:callback, make sure getShares:callback has completed the request to the server, otherwise the deleted shares will just be re-added.

[[Megacool sharedMegacool]
deleteSharesMatchingFilterFromDevice:^BOOL(MCLShare *share) {
// Delete all installed shares
return share.state == MCLShareStateInstalled;
}];

3.5 Tracking sharing and referrals

In addition to customizing the link and adding data from the sender, you can fetch this information on the receiving side to e.g.:

  • reward your users for referring friends,
  • send users to a specific place in the app, and
  • automatically make two users friends.

startWithAppConfig:andEventHandler is a block passing an array of MegacoolEvent from the server. A MegacoolEvent can contain a MegacoolShare object.

There are three main events to handle when a receiving user clicks a link:

3.5.1 Received share opened

When a receiving user clicks a link to either open or install your app, startWithAppConfig:andEventHandler will receive the MegacoolEvent from the server right after the app is opened. The megacoolEvent.type is MCLEventReceivedShareOpened. isFirstSession is a boolean telling whether the app was opened for the first time (new install) or just opened. The megacoolEvent.share contains the MegacoolShare object that was sent. With the data from the MegacoolShare object you can reward the user and give a custom onboarding experience.

You can improve the experience for existing users by sending them to the right place in your app immediately after they've clicked the link through Universal Link. E.g. to a game session. This is only for users that have the app installed. A MegacoolEvent where megacoolEvent.type is MCLEventLinkClicked will be triggered. The MegacoolEvent has a megacoolEvent.data with the path, query, referralCode and URL from the link. In this case, the MegacoolShare object is nil because it has to be fetched from the server which is handled in Received Share Opened above.

3.5.2 Sent share opened

The sender gets a MegacoolEvent of the type MCLEventSentShareOpened when the receiver has opened the app from the shared link. The MegacoolEvent contains isFirstSession to indicate whether the app was installed or just opened. This can be used to reward the sender. In addition megacoolEvent.data contains the receiving user's id from MCLEventDataReceiverUserId. This can be used to add the receiving user as a friend.

3.5.3 Implementation

Replace the code from step: 2.1 Initialize the framework from Quickstart with the following:

[Megacool startWithAppConfig:@"YOUR_APP_CONFIG"
andEventHandler:^(NSArray *events) {
for (MCLEvent *event in events) {
/* THIS DEVICE */
// MCLEventLinkClicked
// The app has been opened from a link click, send the user instantly to
// the right scene
if (event.type == MCLEventLinkClicked) {
NSURL *URL = event.data[MCLEventDataURL];
MCLReferralCode *referralCode = event.data[MCLEventDataReferralCode];
NSLog(@"To path: %@ with query: %@", URL.path, URL.query);
NSLog(@"Invited by userId: %@", referralCode.userId);
}
// MCLEventReceivedShareOpened && isFirstSession
// This device has received a share to the app and installed it
if (event.type == MCLEventReceivedShareOpened && event.isFirstSession) {
NSURL *URL = event.share.URL;
MCLReferralCode *referralCode = event.share.referralCode;
NSDictionary *shareData = event.share.data;
NSString *welcomeText = shareData[@"welcomeText"];
NSLog(@"Invited by userId: %@", referralCode.userId);
NSLog(@"To path: %@ with query: %@", URL.path, URL.query);
NSLog(@"Welcome text: %@", welcomeText);
}
// MCLEventReceivedShareOpened && !isFirstSession
// This device has received a share to the app and opened it
if (event.type == MCLEventReceivedShareOpened && !event.isFirstSession) {
NSURL *URL = event.share.URL;
MCLReferralCode *referralCode = event.share.referralCode;
NSDictionary *shareData = event.share.data;
NSString *welcomeText = shareData[@"welcomeText"];
NSLog(@"Invited by userId: %@", referralCode.userId);
NSLog(@"To path: %@ with query: %@", URL.path, URL.query);
NSLog(@"Welcome text: %@", welcomeText);
}
/* FRIEND'S DEVICE */
// MCLEventSentShareOpened && isFirstSession
// Friend's device has received a share to the app and installed it, add
// him as
// friend!
if (event.type == MCLEventSentShareOpened && event.isFirstSession) {
NSString *userId = event.data[MCLEventDataReceiverUserId];
NSLog(@"User id %@ has installed the app", userId);
}
// MCLEventSentShareOpened && !isFirstSession
// Friend's device has received a share to the app and opened it, join his
// session!
if (event.type == MCLEventSentShareOpened && !event.isFirstSession) {
NSString *userId = event.data[MCLEventDataReceiverUserId];
NSLog(@"User id %@ has opened the app", userId);
}
}
}];

3.6 Customize sharing text

Want to change the text that is presented when sharing? Add the code below with your choice of words. Make it personal! At this point, the text is equal on all sharing platforms that allow predefined sharing text.

[[Megacool sharedMegacool] setSharingText:@"Try to beat my score!"];

Note: Avoid mentioning the GIF-feature in the text in case the GIF is not available or disabled. We can remotely turn off this feature if an issue occurs, to ensure stability.

4 GIF recording customization

Interested in getting fancy?

4.1 Recording frame rate

Set numbers of frames to record per second. Recommended range: 1 - 10. The default is 10:

[[Megacool sharedMegacool] setFrameRate:5];

Set the frame rate right before calling startRecording.
NB: This only applies to recordings made with startRecording, not captureFrame.

Frame rate examples (with Playback frame rate: 10):

Frame rate: 5
Frame rate: 10
Frame rate: 25

4.2 Playback frame rate

Set the number of frames per second when playing the GIF. The default is 10 frames / second. The GIF will be exported with this frame rate and it doesn't affect the recording frame rate.

[[Megacool sharedMegacool] setPlaybackFrameRate:5];

Make sure to call setPlaybackFrameRate before presentShare when sharing the GIF.

Playback frame rate: 5
Playback frame rate: 10
Playback frame rate: 25

4.3 Max frames

Max number of frames to record, default is 50 frames. Change the maximum amount of captured frames by setting:

[[Megacool sharedMegacool] setMaxFrames:25];
Max frames: 25
Max frames: 50
Max frames: 100

4.4 Delay last frame

Set a delay (in milliseconds) for the last frame of the GIF. The default is 1 second to give a natural break before the GIF loops again.

[[Megacool sharedMegacool] setLastFrameDelay:2000];
No extra delay on the last frame
1000 ms delay (default)
2000 ms delay

4.5 Rendering preview

This creates a rendered preview of the GIF that can be displayed to the user before sharing.

Call startAnimating / stopAnimating to play/pause the animation of the GIF. But you're free to change the size later. If you want to do something advanced with this preview, please review the UIImageView documentation.

UIImageView *imageView =
[[Megacool sharedMegacool] renderPreviewOfGif];
if (imageView != nil) {
[self.view addSubview:imageView];
[imageView startAnimating];
}
else{
NSLog(@"Don't show GIF preview");
}

Customize position and size of the rendered GIF preview that can be showed before sharing:

UIImageView *imageView =
[[Megacool sharedMegacool]
renderPreviewOfGifWithConfig:
@{kMCLConfigPreviewFrameKey:[NSValue valueWithCGRect:CGRectMake(100,100,200,300)]}
];

4.6 Last frame overlay

With last frame overlay you can add a transparent image on top of the last GIF frame.

+ =

[Megacool sharedMegacool].lastFrameOverlay = [UIImage imageNamed:@"overlay.png"];

The last frame overlay will now appear on the last frame of the shared GIF. If you want to show it in the gif preview as well, call the following function:

UIImageView *imageView =
[[Megacool sharedMegacool]
renderPreviewOfGifWithConfig:
@{kMCLConfigIncludeLastFrameOverlayKey:[NSNumber numberWithBool:YES]}
];

Note 1: Given that the GIF color space is 256 colors, additional colors in the overlay image might mess up your coloring when the GIF is generated. Make sure to test this properly.

Note 2: On some channels GIFs may get cropped depending on the aspect ratio. To be on the safe side, make sure to place the text within the visible preview area on different channels with the following aspect ratio:

  • (1:1.4) Facebook, iMessage
  • (1:1.1) Twitter

->

4.7 Fallback image

Fallback image let's you share a predefined image in cases where you don't want a recording or if a recording has failed. The fallback image can be JPG, PNG or even a GIF!

4.7.1 Local file

Add the image to your project and add the path as part of the config.

NSURL *filePathURL = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:@"fallbackImage"
ofType:@"png"]];
[[Megacool sharedMegacool]
presentShareWithConfig:@{kMCLConfigFallbackImageURLKey:filePathURL}];

4.7.2 UIImage

You may also use a UIImage, sometimes it's more convenient. The only drawback is that UIImages can't handle animated GIFs.

UIImage *image = [UIImage imageNamed:@"fallbackImage.png"];
[[Megacool sharedMegacool]
presentShareWithConfig:@{kMCLConfigFallbackImageKey:image}];

When choosing your image please keep in mind that there's a size limit for each social media platform, so use a reasonable size. E.g. Twitter has a 3MB limit for uploading an image.

Note: The image will not be uploaded on the following platforms because they only support sharing links:

  • Facebook
  • Facebook Messenger

4.8 Cropping

Cropping can be used to record parts of the screen. The area you would like to crop is defined by a rectangle.

startRecording:

[[Megacool sharedMegacool]
startRecording:self.view
withConfig:@{kMCLConfigCropKey:[NSValue valueWithCGRect:CGRectMake(0, 100, 300, 300)]}];

captureFrame:

[[Megacool sharedMegacool]
captureFrame:self.view
withConfig:@{kMCLConfigCropKey:[NSValue valueWithCGRect:CGRectMake(0, 100, 300, 300)]}];

Note: 4.6 Last frame overlay will be scaled to the cropped area's width / height, so make sure you have the proper sized last frame overlay for that.

4.9 Timelapse

Identical to the normal captureFrame, but timelapse has different behavior when overflowing maxFrames. If it's about to overflow, it will delete every other frame captured so far, and half the rate of capturing (thus only storing every second image on the first overflow, every fourth image on the next, and so on).

[[Megacool sharedMegacool]
captureFrame:self.view
withConfig:@{kMCLConfigOverflowKey:kMCLConfigOverflowTimelapseValue}];

Timelapse

4.9.1 Force add

If you want to guarantee that a frame is added to the timelapse (like an end screen or final state of the game), pass a forceAdd:YES parameter in config like this:

[[Megacool sharedMegacool]
captureFrame:self.view
withConfig:@{
kMCLConfigOverflowKey:kMCLConfigOverflowTimelapseValue,
kMCLConfigForceAddKey:@YES
}
];

Important: Only pass this parameter once! Normally this function returns as soon as it has spawned a thread to do the actual capture, but if you pass forceAdd:YES it will run synchronously (e.g. not return until it has actually stored the frame. This is because usually this is called as the last step before a session ends and then GIF is created, thus we need the call to complete before creating the GIF. This enables you to change visible elements on the screen after this call completes and be sure that it's not going to be included in the GIF.

4.10 Color table

Introduced in v2.2.0

A gif consists of 256 colors and there are two ways to set the color table. We recommend testing both options to see which looks best for your particular app. Default is Color table fixed.

4.10.1 Fixed

This is a fixed color table, which is very fast since no analysis is needed and performs well in the general case, but usually underperforms when representing nuanced differences and gradients.

[Megacool sharedMegacool].gifColorTable = kMCLGIFColorTableFixed;

4.10.2 Analyze first frame

The first frame is analyzed to generate a representative color table. This requires more CPU to create the GIF, but often yields better results when a GIF contains many shades of the same color. If new colors are introduced later in the GIF they will not be as accurately represented.

[Megacool sharedMegacool].gifColorTable = kMCLGIFColorTableAnalyzeFirst;

5 Save recordings

Save and resume recordings is useful for combining multiple game sessions together. These recordings are stored to disk to be retrieved and potentially resumed later. This is a great case for games like Words with Friends where waiting for the other user to finish the round could take many hours, and still create a GIF of the whole game play.

5.1 Start/Resume recording

All completed recordings will by default be deleted whenever a new recording is started with either captureFrame or startRecording. To save a recording you can set keepCompletedRecordings to YES.

To save multiple recordings you can set a recording id kMCLConfigRecordingIdKey in the config dictionary at startRecording:withConfig or captureFrame:withConfig. The kMCLConfigRecordingIdKey can be anything you prefer, for instance a name of the current level like "level3".

A recording with a given kMCLConfigRecordingIdKey can be resumed until stopRecording is called. After calling stopRecording you can either share the recording or start a new one from scratch with the same kMCLConfigRecordingIdKey.

//Save recording of level 3 to disk
[Megacool sharedMegacool].keepCompletedRecordings = @YES;
[[Megacool sharedMegacool]
startRecording:self.view
withConfig:@{kMCLConfigRecordingIdKey:@"level3"}];

5.2 Pause recording

When you want to pause a recording that can be continued later, you call pauseRecording.

[[Megacool sharedMegacool] pauseRecording];

5.3 Delete recording

deleteRecording will remove any frames of the recording in memory and on disk. Both completed and incomplete recordings will take space on disk, thus particularly if you're using keepCompletedRecordings you might want to provide an interface to your users for removing recordings they don't care about anymore to free up space for new recordings.

[[Megacool sharedMegacool] deleteRecording:@"level3"];

5.4 Show a GIF from a specific recording

To show a GIF from a specific recording you pass the kMCLConfigRecordingIdKey as a parameter to renderPreviewOfGifWithConfig.

UIImageView *imageView =
[[Megacool sharedMegacool]
renderPreviewOfGifWithConfig:@{kMCLConfigRecordingIdKey:@"level3"}];
if (imageView != nil) {
[self.view addSubview:imageView];
[imageView startAnimating];
}

5.5 Share a GIF from a specific recording

To share a GIF from a specific recording you pass the RecordingId as a parameter to presentShareWithConfig.

[[Megacool sharedMegacool]
presentShareWithConfig:@{kMCLConfigRecordingIdKey:@"level3"}];

Note: presentShare calls pauseRecording automatically, so if you haven't called stopRecording before next time you call startRecording, the recording will continue from where you paused.

6 Testing

6.1 Reset device identity

If you are testing the invite/sharing process local on your devices it would be great to reset them so they act as new devices. To reset the device identity for the Megacool SDK you simply call resetIdentity as the first thing when your app starts, before startWithAppConfig:andEventHandler. Also clear all cookies in Safari as this is used to identify the device from the link click.

[Megacool resetIdentity];

7 Troubleshooting

When I find myself in times of trouble
Mother Mary comes to me
Speaking words of wisdom:
"submit a detailed debug log"

Paul McCartney

When things turn sour, and you're done running in circles screaming and shouting, you can swap the SDK over in debug mode to let it capture detailed call traces of what's happening, and automatically submit a debug report to us. Do this by setting debugMode=YES on the Megacool singleton, and when your problem has been reproduced (or you believe it is about to happen), call [[Megacool sharedMegacool] submitDebugDataWithMessage:debugMessage]; to share the issue with us. Please try to explain in the message what you expected to happen, and what happened instead. If you follow this procedure it will be much easier for us to reproduce the issue you're having and get it fixed ASAP.

We appreciate you taking the time to help us find the cause of the bugs, and we're very sorry that we didn't find them ourselves.