Articles - RareSloth Games

ReplayKit Implementation Notes

I spent two days digging into ReplayKit’s simple API to discover undocumented subtleties that caused me to write more code. There is very little documentation on ReplayKit aside from the WWDC video’s example code, so it can be hard to find any good information. This is a guide of what to look out for when doing your ReplayKit implementation.

I’m on XCode 8.0 and my devices are an iPad Mini 2 and an iPhone SE, both running iOS 10.0.2. We support iOS 8.0 and up in King Rabbit so we had to do iOS version checks to make sure none of this code was called if ReplayKit (iOS 9.0+) wasn’t available. This means weak linking (making Optional) the ReplayKit framework in the project. We tested some of these issues out on other App Store games and they seemed to have similar failures/stuck points.

Edit: Here’s a gist of how we ask for permissions, start, and stop recording/broadcasting in King Rabbit. However, iOS 11 appears to have caused some flakiness/bugs in the way we record. iOS 11 also allows recording/broadcasting from the Control Center instead of doing it through apps.

Edit 2: Here’s a swift example written by victorchee that may be helpful: https://github.com/victorchee/ReplayKitDemo

Recording

If you’re looking to record video on the device, you’ll use the instance of [RPScreenRecorder sharedRecorder] to call either startRecordingWithMicrophoneEnabled:handler: (deprecated) or startRecordingWithHandler: (iOS 10+).

startRecordingWithMicrophoneEnabled:handler:

If you want to support iOS 9.0, you’ll have to use this deprecated method to start your recording.

Microphone recording

If you deny access for microphone recording (choosing ‘Record screen only’) in the permissions alert view when starting a recording, you can’t turn on the microphone until starting a new recording. Just keep this in mind for your UI, since your “mic” button will be useless.

startRecordingWithHandler:

If you want the front facing camera preview as most streamers have in their broadcasts, you’ll want to look at the isCameraEnabled and isMicrophoneEnabled flags on RPScreenRecorder. I found that I had to set these flags to YES on the [RPScreenRecorder sharedRecorder] instance before calling startRecordingWithHandler:.

Then I found that you have to request video permissions before the flags you just set are honored. Otherwise, the flag will be unset when the handler argument is called. Also, if you try asking for permissions after you’ve already started recording and you try to setCameraEnabled to YES when granted, it’ll stop the recording without letting you know!

Do something like this:

[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
    dispatch_async(dispatch_get_main_queue(), ^{
        if (granted)
		{
			[[RPScreenRecorder sharedRecorder] setCameraEnabled:YES];
			[[RPScreenRecorder sharedRecorder] setMicrophoneEnabled:YES];
			[[RPScreenRecorder sharedRecorder] startRecordingWithHandler:^{
				// Recording started
			}];
		}
    });
}];
Remove CFBundleDevelopmentRegion

From this simple use, I found that the handler wasn’t getting called on my iOS 10 devices (apparently is works fine on iOS 9). I found a tip on the Apple developer forum where someone removed the CFBundleDevelopmentRegion key from their Info.plist so I tried the same thing. And it worked! I don’t know how that could possibly tie in with ReplayKit, but there’s a bug in there somewhere.

Broadcasting (iOS 10+)

Broadcasting allows you to stream your recording directly to a third party service that you have installed on your device. I used Mobcrush and Periscope for testing.

To start a broadcast, you call:

[RPBroadcastActivityViewController loadBroadcastActivityViewControllerWithHandler:^(RPBroadcastActivityViewController * _Nullable broadcastActivityViewController, NSError * _Nullable error) {
}];

Presenting the broadcastActivityViewController allows the player to choose which service he wants to stream with. An important thing to note – when presenting this view controller on an iPad, you have to present it in a popover, otherwise it just won’t do anything. There’s no documentation and no obvious logs that let you know this! You’ll need something like:

broadcastActivityViewController.modalPresentationStyle = UIModalPresentationPopover;
broadcastActivityViewController.popoverPresentationController.sourceView = myView;
broadcastActivityViewController.popoverPresentationController.sourceRect = myView.frame;
broadcastActivityViewController.delegate = self;
[myViewController presentViewController:broadcastActivityViewController animated:YES completion:nil];

The delegate method

- (void)broadcastActivityViewController:(RPBroadcastActivityViewController *)broadcastActivityViewController didFinishWithBroadcastController:(RPBroadcastController *)broadcastController error:(NSError *)error

gives you an RPBroadcastController instance, which you call startBroadcastWithHandler: on to start up the broadcast.

Broadcasting simply did not work on my iPad Mini 2 until I used the CFBundleDevelopmentRegion key hack from above. However, even with that, the broadcast had unusable quality. We’re limiting our broadcasting to only newer devices to try to mitigate these failures. When it fails to broadcast in this manner, it won’t call the handler with an error – it just won’t call it so you’ll be stuck in a loading screen if you have one.

If you want to enable the microphone and/or camera during a broadcast, you’ll have to figure it out. It doesn’t seem like RPScreenRecorder should be dealt with when you’re broadcasting, but it might provide the cameraPreviewView for you. You’ll also have to ask for mic/camera permissions yourself since initiating a broadcast doesn’t ask by default.

Some other notes

We tried starting a recording after finishing a broadcasting, but found that the handler wouldn’t be called on the startRecording methods. The same issue occurs in the opposite direction, finishing a recording and trying to start a broadcast. Just keep this in mind if you want to allow players to do either. We’re simply going to hide the other option for the lifetime of the running app so players don’t get stuck. Apple have apparently fixed this bug, but I haven’t had a chance to test it.

If you’re a third party streaming service that wants to implement the broadcasting extension, good luck. There are some classes in the ReplayKit documentation, but you’ll find no written documentation on what they do.

There might be some other gotchas I’ve forgotten, but hopefully this guide will help get you started more quickly than we did!

King Rabbit indie mobile game sales stats

Indie mobile game sales stats for King Rabbit, the sequel to Furdemption.

Read More

King Rabbit Free App of the Week

King Rabbit Free App of the Week Figures

This past week we were featured as Apple’s Free App of the Week. It also marks the first time we’ve gone the freemium route with one of our games. We’ve previously had a $3 app, a $1 app with IAP, and now a free app with ads and IAP. As before, we want to share some figures with other indies out there so you can get a better idea of what to expect. Keep in mind we were also featured in numerous other places in the App Store like the Games page, Games > Puzzles, 15 Most Huggable Heroes, etc. We also have screenshots and descriptions localized in 15 languages.

Read More

Uploading App Store Localizations

Localizations are a big factor in how successful your game or app will be in foreign markets. There are many steps to localizing your app and they all take time, so decreasing or removing the time spent on any step will undoubtedly help your business. We’ll walk you through what you should do to cut down the time spent on uploading Apple App Store localizations. We’ll cover how to provide app descriptions, keywords, screenshots, in-app purchases, achievements, and leaderboards.

Background

When we published Furdemption – A Quest For Wings, we didn’t really have a good strategy for uploading all our localized data to iTunes Connect. In fact, due to the lack of tools and time, we were forced to input all of our localizations directly into the iTunes Connect website. This involves navigating pages and filling forms for each locale for each thing we wanted localized – a lot of mind numbing work. Our latest game King Rabbit had even more localizations to publish so we were determined to make this process easier and faster.

iTunes Connect App Store Localization Form Entry

iTunes Connect App Store Localization Form Entry

itms-scripts

Apple have provided a tool called iTMSTransporter that can be used to upload App Store localizations and images directly to iTunes Connect. However, it’s quite a time sink to modify the contents of the xml package with your new localizations. We wrote a few scripts that help with populating the xml with the localizations you provide through simple .csv files and uploading it. An example of the kind of data you need to provide can be found at this Google Sheets template. The ruby scripts and instructions can be found on our GitHub.

Essentially all you have to do is populate some .csv files with your localizations, put your images in the given folder structure, then type ./itms.rb

Localized Screenshots

We wanted to be able to put some text in our screenshots and have it all be localized in each language we planned on supporting. The process to do this will vary depending on what kind of game engine/framework you’re using, but in general, you can write an automated test that loops through your base images, applies a localized label, then renders the result to a file. We have a localization helper class that helps with switching languages during the automated test. We used the cocos2d-objc game engine for King Rabbit, so cocos2d devs – check out this gist for a method that does the trick.

Let us know if you have any questions about app store localizations!

Mobile Event Logging

Our current game in development, Furdemption 2, will be more of a service rather than a one-off product. Being a service, it’s crucially important for us as a company to be able to answer the questions: How are players playing the game and when are they buying in-app purchases (IAP)? Through the use of event logging, we’ll be able to look at certain data points to try to answer these questions as best as possible.

How are players playing the game?

We want Furdemption 2 to be as fun for as long as possible for our players. This means it’s important for us to know how they are progressing through the levels and which levels cause them to stop playing. Through event logging, we’re keeping track of how long it takes players to beat each level they play. Looking at the data points for each level, we can get an average value of completion time, telling us the general difficulty of the level. We’re then able to tweak the difficulty curve by moving levels around or updating the levels themselves.

We’re also using event logging to track what level a player was on when they background the app. This gives us an idea of which levels might be too difficult to keep trying or when in the game people tend to stop playing.

Probably not a level you want early in your game

When are players buying IAPs?

Since Furdemption 2 is going to be a game that could make money over the long term, we’ll be including IAPs. It’s easy to see which IAPs are being bought (check your revenue!), but the deeper question we want to answer is when they’re being bought, which will give us a better idea of why. We’re event logging a few pieces of information to help us answer these questions. Since we have multiple avenues where the store can be accessed from, event logging will tell us if players are looking at the store before they start playing, after they finish a level, or when they want to use a boost they don’t have. Other data points include how long players look at each store page before they make a purchase or close the store, and how long they’ve been playing the game before making a purchase.

Being able to inform ourselves of what players are doing with Furdemption 2 will help us figure out the tweaks we should make in order to make it a better service for all. If you’re a developer, what kind of data are you tracking that helps you make better decisions? If you’re a player, what do you want to know about how you or others are playing? Leave a comment with your thoughts!