ReplayKit Won't Record Twice in One Session - swift

I am using ReplayKit in an app to record the visible screen with some text and a video playing. The issue I am facing is that ReplayKit is working just fine for the first screen recording, but if I am to record again in the same session (ie without closing the app) it runs into this error:
MyViewController[423:39346] viewServiceDidTerminateWithError:: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}
In this scenario, I am actually trying to screen record on the same ViewController (only with a different video being played and some text content altered). Below is my recording code:
#objc func startRecording() {
let recorder = RPScreenRecorder.shared()
recorder.startRecording{ [unowned self] (error) in
if let unwrappedError = error {
print(unwrappedError.localizedDescription)
print("NOT Recording")
} else {
self.video.play()
print("Recording")
self.isRecording = true
}
}
recordIcon.isHidden = true
ring.isHidden = true
}
#objc func stopRecording() {
let recorder = RPScreenRecorder.shared()
recorder.stopRecording( handler: { previewViewController, error in
if let error = error {
print("\(error.localizedDescription)")
}
// Handling iPads
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad {
previewViewController?.modalPresentationStyle = UIModalPresentationStyle.popover
previewViewController?.popoverPresentationController?.sourceRect = CGRect.zero
previewViewController?.popoverPresentationController?.sourceView = self.view
}
if previewViewController != nil {
self.previewViewController = previewViewController
previewViewController?.previewControllerDelegate = self
}
self.present(previewViewController!, animated: true, completion: nil)
})
isRecording = false
recordIcon.isHidden = false
ring.isHidden = false
return
}
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
dismiss(animated: true)
}
Any help on this is greatly appreciated. I'd hate to force users to have to reopen the app before recording again.

It might be that your app keeps the screen recording longer than it should. If this is the case, try implementing the discardRecording(handler: #escaping () -> Void) function. Here are more details on the discardRecording.

Related

Not Receiving Remote Command Center events while using Music Player Controller

I am trying to receive notifications from the remote command center for when the play/pause button is tapped so that I can appropriately update the image for my play/pause button. However, I am not receiving the notification of when the playcommand/pausecommand is pressed (ie. when it should print "tapped play" or "tapped pause"). This is my first time using this library, and so I followed Apples docs and it says to implement a music controller using either a applicationMusicPlayer or applicationQueuePlayer to receive these events, but so far I am not able to. I don't know if there is anything else I need to do besides just setting it up as a applicationQueuePlayer.
Here is my code for a very plain music player controller that produces this situation:
let musicPlayer = MPMusicPlayerController.applicationQueuePlayer
override func viewDidLoad() {
super.viewDidLoad()
setupRemoteControl()
musicPlayer.setQueue(with: .songs())
}
func setupRemoteControl() {
UIApplication.shared.beginReceivingRemoteControlEvents()
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget { (_) -> MPRemoteCommandHandlerStatus in
print("tapped play")
return .success
}
commandCenter.pauseCommand.isEnabled = true
commandCenter.pauseCommand.addTarget {(_) -> MPRemoteCommandHandlerStatus in
print("tapped pause")
return .success
}
#IBAction func selectSongs(_ sender: UIButton) {
let controller = MPMediaPickerController(mediaTypes: .music)
controller.allowsPickingMultipleItems = true
controller.popoverPresentationController?.sourceView = sender
controller.delegate = self
present(controller, animated: true, completion: nil)
}
func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
musicPlayer.setQueue(with: mediaItemCollection)
mediaPicker.dismiss(animated: true, completion: nil)
musicPlayer.play()
}

Game Center match is invalid always

I'm trying to learn how to build a Game-Center based app. its a turn based extremely simple game that just basically logs what button you press and sends it to he other player. I am having a very hard time implementing all the game center features since Apple's documentation has not been updated for Swift. I've been guessing at everything and reading off Objective-C examples and hoping for the best (somehow I've managed to get a few things going, altough I'm not sure if they are correct)
Anyways, I made a new iCloud account for my Simulator and ran the app on my phone and the simulator simultaneously, trying to get them to match up in a game. However I always get a "match request is invalid" error:
EDIT I have registered the app in iTunesConnect, I've implemented leaderboards and tested them and they work (So I assume the iTunesConnect thing is properly working and set-up)
#IBAction func startTapped(_ sender: Any) {
let request = GKMatchRequest()
request.maxPlayers = 2
request.minPlayers = 1
let mmvc = GKMatchmakerViewController(matchRequest: request)
mmvc?.matchmakerDelegate = self
present(mmvc!, animated: true, completion: nil)
}
extension HomeVC: GKGameCenterControllerDelegate
{
func authenticatePlayer()
{
//CALLED ON VIEWDIDAPPEAR
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {
(view, error) in
if view != nil
{
self.present(view!, animated: true, completion: nil)
} else {
print("AUTHENTICATED!")
print(GKLocalPlayer.localPlayer().isAuthenticated)
}
}
}
func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismiss(animated: true, completion: nil)
}
}
Here's the matchmaking code. Note I can get the Game Center screen to appear and tell me "how many players" and that its missing a player and gives the choice to invite friends and all that
extension HomeVC: GKMatchmakerViewControllerDelegate {
func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) {
print("match was cancelled")
viewController.dismiss(animated: true, completion: nil)
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) {
print("didFailwithError: \(error.localizedDescription)")
}
func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) {
print("Match found, ID: \(match.description)")
let gameScreenVC = self.storyboard?.instantiateViewController(withIdentifier: "mainGame") as! ViewController
gameScreenVC.providesPresentationContextTransitionStyle = true
gameScreenVC.definesPresentationContext = true
gameScreenVC.modalPresentationStyle = UIModalPresentationStyle.fullScreen
gameScreenVC.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
match.delegate = gameScreenVC
self.present(gameScreenVC, animated: true, completion: nil)
}
}
If anyone is having this issue I fixed it by just replacing the "minelayers and max-layers" with the same amount, so the code now looks like this:
#IBAction func startTapped(_ sender: Any) {
let request = GKMatchRequest()
request.maxPlayers = 2 //min and max should be the same
request.minPlayers = 2 //apparently they need to be the same
let mmvc = GKMatchmakerViewController(matchRequest: request)
mmvc?.matchmakerDelegate = self
present(mmvc!, animated: true, completion: nil)
}
apparently Game Center does not like a min.player count of 1, its invalid. but thats how I got it to stop giving me Invalid Match Request warnings

Swift. How do I remove the toolbar in an AVPlayerViewController

I'm presenting an AVPlayerViewController with an AVPlayer and are displaying a pre-roll ad in it, but I want to remove the top and button bar, or make the user unable to dismis the ViewController before the ad is done:
How do I do that?
here's my code for my AVPlayerViewController implementation:
let preRollAdPlayer = AVPlayerViewController()
let player = AVPlayer()
preRollAdPlayer.player = player
if #available(iOS 9.0, *) {
preRollAdPlayer.delegate = self
}
preRollAdPlayer.showsPlaybackControls = false
self.view?.window?.rootViewController?.presentViewController(preRollAdPlayer, animated: true, completion: {
self.preRollAdPlayer.playPrerollAdWithCompletionHandler({ (error) -> Void in
if (error != nil) {
NSLog("Error Message: %#", error)
} else {
print("preRoll loaded")
}
self.preRollAdPlayer.dismissViewControllerAnimated(true, completion: {
if (error != nil) {
NSLog("Error Message: %#", error)
}
})
})
})
According to Apple, you should use showsPlaybackControls property:
A Boolean value that indicates whether the player view controller shows playback controls.
Set it to false:
Swift:
yourAvPlayer.showsPlaybackControls = false
Objective C:
[yourAvPlayer setShowsPlaybackControls:FALSE];

SpriteKit - Can't sign in to Gamecenter

I did everything right regarding itunes connect, but when I start the app it doesn't authenticate, and when I press my Gamecenter button it gives me a message "Gamecenter is not available, user not signed in"
Code:
ViewController:
import UIKit
import SpriteKit
import GameKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
authenticateLocalPlayer()
}
//initiate gamecenter
func authenticateLocalPlayer(){
print("Starting..1")
let localPlayer = GKLocalPlayer.localPlayer()
print("Starting..2")
localPlayer.authenticateHandler = {(viewController, error) -> Void in
print("Starting..3")
if (viewController != nil) {
print("Not signed in. Authenticating now")
var vc = self.view?.window?.rootViewController
vc?.presentViewController(viewController!, animated: true, completion: nil)
}
else {
print((GKLocalPlayer.localPlayer().authenticated))
}
}
}
GameScene:
import SpriteKit
import iAd
import GameKit
class GameScene: SKScene, SKPhysicsContactDelegate, ADBannerViewDelegate, GKGameCenterControllerDelegate {
func saveHighscore(score:Int) {
//check if user is signed in
if GKLocalPlayer.localPlayer().authenticated {
let scoreReporter = GKScore(leaderboardIdentifier: "CC_Leaderboard_1") //leaderboard id here
scoreReporter.value = Int64(score) //score variable here (same as above)
let scoreArray: [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: {(error : NSError?) -> Void in
if error != nil {
print("error") }
})
}
}
//shows leaderboard screen
func showLeader() {
var vc = self.view?.window?.rootViewController
var gc = GKGameCenterViewController()
gc.gameCenterDelegate = self
vc?.presentViewController(gc, animated: true, completion: nil)
}
//hides leaderboard screen
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!)
{
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
}
I also tried authenticating inside GameScene but that didn't work either.
Edit:
//initiate gamecenter
func authenticateLocalPlayer(){
print("Gamecenter..1")
let localPlayer = GKLocalPlayer.localPlayer()
print("Gamecenter..2")
localPlayer.authenticateHandler = {(viewController, error) -> Void in
print("Gamecenter..3")
if (viewController != nil) {
print("Not signed in. Authenticating now")
var vc = self.view?.window?.rootViewController
vc?.presentViewController(viewController!, animated: true, completion: nil)
}
else {
print((GKLocalPlayer.localPlayer().authenticated))
}
}
}
Take a look at the print statements at the function above, "Gamecenter..3" never get printed.
Edit 2:
The problem solved itself when I signed out of Gamecenter. (How stupid this kind of stuff)
One possibility: In your authentication handler, you never check the error code. You only check if the viewController is set or not. If the error code is set, though, the viewController will (usually) be nil, so you could be interpreting a login error as a successful login.
I would change the code to:
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if (error != nil)
{
//add some stuff to report the error
}
else if (viewController != nil){
print("Not signed in. Authenticating now")
var vc = self.view?.window?.rootViewController
vc?.presentViewController(viewController!, animated: true, completion: nil)
}
else {
print((GKLocalPlayer.localPlayer().authenticated))
}
}
to confirm you're not getting an error on login. If you are getting an error, that will provide a clue as to what to look at next.
Edit
Some folks are reporting authentication problems until they add at least 1 leaderboard or achievement in iTunesConnect. In those reports, they're actually getting a login error that the game isn't recognized. But, given the inherent flakiness in game center, I would add a placeholder achievement or leaderboard to rule that issue out. (iOS9 “This game is not recognized by game center.”)

Calling achievement screen in Swift

I am having some extreme difficulties calling the achievements screen in game center. I have already set up the achievements in iTunes connect and it pops up fine if I access the achievements screen through the leaderboard first. However; I would like to be able to press a specific achievement button and be directed directly to the achievements screen. Can any one help? I have searched high and low on the internet ( and read through all of the documentation). I have found many resources for implementing leaderboards, but not many resources for implementing achievements in swift. My code is below. Any suggestions for my last two functions?
override func viewDidLoad() {
super.viewDidLoad()
login()
}
func login() {
println("Game Center Login Called")
let localPlayer = GKLocalPlayer.localPlayer()
// Handle the authentication
localPlayer.authenticateHandler = {(Home: UIViewController!, error: NSError!) -> Void in
if Home != nil {
println("Authentication is being processed.")
self.presentViewController(Home, animated: true, completion: nil)
} else {
println("Player has been successfully authenticated.")
}
}
}
func showLeaderboard() {
let gkScore = GKScore(leaderboardIdentifier: "high_Score_Leader_Board")
gkScore.value = Int64(highscore)
GKScore.reportScores([gkScore], withCompletionHandler: ( { (error: NSError!) -> Void in
if (error != nil) {
// handle error
println("Error: " + error.localizedDescription);
} else {
println("Score reported: \(gkScore.value)")
}
}))
var gcViewController: GKGameCenterViewController = GKGameCenterViewController()
gcViewController.gameCenterDelegate = self
gcViewController.viewState = GKGameCenterViewControllerState.Leaderboards
gcViewController.leaderboardIdentifier = "high_Score_Leader_Board"
self.showViewController(gcViewController, sender: self)
self.presentViewController(gcViewController, animated: true, completion: nil)
}
#IBAction func gameCenterButtoPressed(sender: AnyObject) {
showLeaderboard()
}
func gameCenterViewControllerDidFinish(gcViewController: GKGameCenterViewController!)
{
self.dismissViewControllerAnimated(true, completion: nil)
}
func showAchievements() {
// show Achievements screen
}
#IBAction func achievementButtonPressed(sender: AnyObject) {
// Call show achievements function when button pressed
}
Instead of:
gcViewController.viewState = GKGameCenterViewControllerState.Leaderboards
I think what you'll want is:
gcViewController.viewState = GKGameCenterViewControllerState.Achievements
And I found this information in this related tutorial.