iOS 12: ReplayKit is broken - ios

I have been using ReplayKit for all past updates, but now with iOS 12 my recordings sometimes work, sometimes don't... but usually they don't. Most of the time when I stop the recording this is what I get:
a completely black screen.
This hasn't happened to me before and it is extremely frustrating. This is how I use ReplayKit to record the screen:
import ReplayKit
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, RPPreviewViewControllerDelegate {
func startRecording() {
func start() {
guard RPScreenRecorder.shared().isAvailable else {
print("Recording is not available at this time.")
return
}
RPScreenRecorder.shared().isMicrophoneEnabled = micToggle
RPScreenRecorder.shared().startRecording { [unowned self] (error) in
guard error == nil else {
print("There was an error starting the recording.")
return
}
print("Started Recording Successfully")
isRecording = true
}
}
DispatchQueue.main.async {
start()
}
}
func stopRecording() {
func stop() {
RPScreenRecorder.shared().stopRecording { [unowned self] (preview, error) in
print("Stopped recording")
guard preview != nil else {
print("Preview controller is not available.")
return
}
onGoingScene = true
preview?.previewControllerDelegate = self
self.present(preview!, animated: true, completion: nil)
print("presented")
isRecording = false
}
}
DispatchQueue.main.async {
stop()
}
}
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
previewController.dismiss(animated: true, completion: nil)
RPScreenRecorder.shared().discardRecording {
print("discarded")
}
}
When it works, all the print statements are printed, but when the black screen appears the last print statement is "presented".
I am absolutely desperate for some help because I have no idea how to get around this. ANY help would be much appreciated.
THANKS
Edit:
I just realised that I am using an 'AVCaptureVideoPreviewLayer` if that may be the issue. If so, what's the fix?

Related

UIPrinterPickerController not showing on iOS 13

I've been trying to get a UIPrinterPicker to show up but for some reason it just never does. The completion handler gets called immediately. The UIPrintInteractionController shows up just fine but this one refuses to show for some reason. This is the code I am using currently
let picker = UIPrinterPickerController(initiallySelectedPrinter: nil)
picker.present(animated: true) { (controller, complete, error) in
print("done")
}
Implement UIPrinterPickerControllerDelegate so it works for iOS 13
https://developer.apple.com/documentation/uikit/uiprinterpickercontroller/1620514-present
class ViewController: UIViewController {
#IBAction func btnTapped(_ sender: Any) {
let picker = UIPrinterPickerController(initiallySelectedPrinter: nil)
picker.delegate = self
picker.present(animated: true) { (controller, complete, error) in
print("done")
}
}
}
// MARK:- UIPrinterPickerControllerDelegate
extension ViewController: UIPrinterPickerControllerDelegate {
func printerPickerControllerParentViewController(_ printerPickerController: UIPrinterPickerController) -> UIViewController? {
return self
}
}

AVFoundation- .audioDeviceInUseByAnotherClient runs after CXCallObserver call.hasEnded (call disconnection) is called

I'm using AVFoundation for video recording. I also use the CXCallObserverDelegate to listen to when a phone call is disconnected.
I go to the background
I make a phone call
While the phone call is active I bring the app back to the foreground and press the button to modally present the vc that contains AVFoundation
Once the vc is on scene and because I'm currently on a phone call the .audioDeviceInUseByAnotherClient gets called and I stop the capture session
Once the phone call is disconnected then CXCallObserver call.hasEnded is called and I restart the capture session. The .sessionInterruptionEnded also gets called but this isn't causing the issue.
This is where the problem occurs. Once call.hasEnded is called then .audioDeviceInUseByAnotherClient gets called again. Since the code to stop the capture session is in there this results in capture session stopping again
In step 6 why does .audioDeviceInUseByAnotherClient get called again after the call has been disconnected?
func sessionWasInterrupted(notification: NSNotification) {
let reasonIntegerValue = userInfoValue.integerValue,
let reason = AVCaptureSession.InterruptionReason(rawValue: reasonIntegerValue) {
case .audioDeviceInUseByAnotherClient:
stopCaptureSession()
}
}
}
func sessionInterruptionEnded(notification: NSNotification) {
print("-----Capture session interruption ended")
restartCaptureSession()
}
func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
if call.hasEnded == true {
print("\nCXCallState :Disconnected")
restartCaptureSession()
}
if call.hasConnected == true && call.hasEnded == false {
print("\nCXCallState : Connected")
// *** THIS NEVER GETS CALLED IN THIS SCENARIO ***
}
}
fileprivate func stopCaptureSession() {
if captureSession.isRunning {
DispatchQueue.global(qos: .background).async { [weak self] in
DispatchQueue.main.sync {
self?.captureSession.stopRunning()
}
DispatchQueue.main.async {
self?.previewLayer?.removeFromSuperlayer()
self?.previewLayer = nil
}
}
}
}
func restartCaptureSession() {
if !captureSession.isRunning {
DispatchQueue.global(qos: .background).async { [weak self] in
DispatchQueue.main.sync {
self?.captureSession.startRunning()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
if let safeSelf = self {
if safeSelf.previewLayer == nil {
self?.previewLayer = AVCaptureVideoPreviewLayer(session: self!.captureSession)
self?.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
guard let previewLayer = self?.previewLayer else { return }
previewLayer.frame = self!.containerViewForPreviewLayer.bounds
self?.containerViewForPreviewLayer.layer.insertSublayer(previewLayer, at: 0)
}
}
}
}
}
}

ReplayKit stops screen recording in background mode of the application or outside the app?

I've implemented screen recording with ReplayKit in foreground mode of the application. But when I'm going outside the app with home button app stops background record.
--> There is an app available In App Store which allows background screen record.
--> If I have to use Broadcast upload and UI extension then please provide me some programming guide. I've added both in my app but still it stops recording in background mode.
Below is my code
import UIKit
import ReplayKit
class ViewController: UIViewController {
let recorder = RPScreenRecorder.shared()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func btnStartRecord_Action(_ sender: UIButton) {
if recorder.isAvailable {
if !recorder.isRecording {
recorder.startRecording { (error) in
if let error = error {
print(error)
}
}
}
}
}
#IBAction func btnStopRecord_Action(_ sender: UIButton) {
if recorder.isAvailable {
if recorder.isRecording {
recorder.stopRecording { (previewVC, error) in
if let previewController = previewVC {
previewController.previewControllerDelegate = self
self.present(previewController, animated: true, completion: nil)
}
}
}
}
}
}
extension ViewController: RPPreviewViewControllerDelegate {
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
previewController.dismiss(animated: true) {
}
}
}

How to display Leaderboard Sets in iOS

I was trying to get this to work all last night but it wouldnt. Can anyone help?
I use the following code to display a leaderboard:
gameCenterViewController.leaderboardIdentifier = leaderboardId
This works fine for normal leaderboards, but fails to load any leaderboard sets, when I use the leaderboardSetId. Can you link directly to leaderboard sets and if so how do you do this?
Thanks.
Please do refer below code and cross verify your configuration.
Hope this would help you.
import GameKit
class YourViewController: UIViewController, GKGameCenterControllerDelegate {
var gcEnabled = Bool() // Stores if the user has Game Center enabled
var gcDefaultLeaderBoard = String() // Stores the default leaderboardID
override func viewDidLoad() {
super.viewDidLoad()
self.authenticateLocalPlayer()
}
func authenticateLocalPlayer()
{
let localPlayer: GKLocalPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(ViewController, error) -> Void in
if((ViewController) != nil) {
// 1 Show login if player is not logged in
self.presentViewController(ViewController!, animated: true, completion: nil)
} else if (localPlayer.authenticated) {
// 2 Player is already euthenticated & logged in, load game center
self.gcEnabled = true
// Get the default leaderboard ID
localPlayer.loadDefaultLeaderboardIdentifierWithCompletionHandler({ (leaderboardIdentifer: String?, error: NSError?) -> Void in
if error != nil {
print(error)
} else {
self.gcDefaultLeaderBoard = leaderboardIdentifer!
}
})
} else {
// 3 Game center is not enabled on the users device
self.gcEnabled = false
print("Local player could not be authenticated, disabling game center")
print(error)
}
}
}
#IBAction func clickToLeaderBoard(sender: UIButton) {
let gcVC: GKGameCenterViewController = GKGameCenterViewController()
gcVC.gameCenterDelegate = self
gcVC.viewState = GKGameCenterViewControllerState.Leaderboards
gcVC.leaderboardIdentifier = "YourLeaderboardId"
self.presentViewController(gcVC, animated: true, completion: nil)
}
func saveScoreOnGameCenter()
{
let leaderboardID = "YourLeaderboardId"
let sScore = GKScore(leaderboardIdentifier: leaderboardID)
sScore.value = Int64(10)
GKScore.reportScores([sScore], withCompletionHandler: { (error: NSError?) -> Void in
if error != nil {
print(error!.localizedDescription)
} else {
print("Score submitted")
}
})
}
func gameCenterViewControllerDidFinish(gcViewController: GKGameCenterViewController)
{
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Update
Also please do check your leaderboard configuration from back end.
Some good posts are here.
http://www.appcoda.com/ios-game-kit-framework/
https://developer.apple.com/game-center/
https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/GameKit_Guide/GameCenterOverview/GameCenterOverview.html
Having ensured that you have authenticated the local player and your leaderboards set in iTunes Connect, you will need to report your scores to GameCenter ensuring you use 64bit Int.
Now you can add this to display the leaderboard for the local player:
func showGKGameCenterViewController(_ viewController: UIViewController) {
if gameCenterIsDisabled {
print("Local player is not authenticated")
}
let gameCenterViewController = GKGameCenterViewController()
gameCenterViewController.delegate = self
gameCenterViewController.viewState = GKGameCenterViewControllerState.default
viewController.presentViewController(gameCenterViewController, animated: true, completion: nil)
}

is it possible to record part of screen with Replaykit?

Here is the start and stop functions.
#IBAction func startRecordingAction(sender: AnyObject) {
activityView.hidden = false
// start recording
recorder.startRecordingWithMicrophoneEnabled(true) { [unowned self] (error) in
dispatch_async(dispatch_get_main_queue()) {
[unowned self] in
self.activityView.hidden = true
}
if let error = error {
print("Failed start recording: \(error.localizedDescription)")
return
}
print("Start recording")
self.buttonEnabledControl(true)
}
}
#IBAction func stopRecordingAction(sender: AnyObject) {
activityView.hidden = false
//end recording
recorder.stopRecordingWithHandler({ [unowned self] (previewViewController, error) in
dispatch_async(dispatch_get_main_queue()) {
self.activityView.hidden = true
}
self.buttonEnabledControl(false)
if let error = error {
print("Failed stop recording: \(error.localizedDescription)")
return
}
print("Stop recording")
previewViewController?.previewControllerDelegate = self
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
// show preview vindow
self.presentViewController(previewViewController!, animated: true, completion: nil)
}
})
}
func screenRecorderDidChangeAvailability(screenRecorder: RPScreenRecorder) {
let availability = screenRecorder.available
print("Availability: \(availability)\n");
}
// MARK: - RPPreviewViewControllerDelegate
// called when preview is finished
func previewControllerDidFinish(previewController: RPPreviewViewController) {
print("Preview finish");
dispatch_async(dispatch_get_main_queue()) {
[unowned previewController] in
// close preview window
previewController.dismissViewControllerAnimated(true, completion: nil)
}
}
I want to record only a part of screen and I want to show a custom alert, not ReplayKit standard alert message. I can use 3 party pods, no problem.
Maybe you can advice me a different way, without ReplayKit.
Unfortunately, you cannot record a particular UIView for now with/Using Replay Kit.
For recording particular view here are some alternative's hope this helps you out.
https://github.com/wess/Glimpse
https://github.com/adam-roth/screen-cap-view
https://github.com/andydrizen/UIViewRecorder
Hope this helps you out.

Resources