I trying implement Rewarded Ad - Rewarded Ads New APIs (Beta). Video is load and isReady property is changing to true in a couple of seconds.
I have a button on which user press and Rewarded Video appear
This is function which is fire when user press on button
func presentRewardAd(from viewController: UIViewController) {
if rewardedAd.isReady {
rewardedAd.present(fromRootViewController: viewController, delegate: self)
}
}
The problem is
I want to hide button until video isReady == true, and when it's ready show button. So i want to get notify when rewardedAd.isReady is changing.
What i try so far:
class CustomRewardAd: GADRewardedAd {
private var observation: NSKeyValueObservation?
override init(adUnitID: String) {
super.init(adUnitID: adUnitID)
observation = observe(\.isReady, options: [.old, .new]) { object, change in
print("isReady changed from: \(change.oldValue!), updated to: \(change.newValue!)")
}
}
}
Also i tried this Using Key-Value Observing in Swift but same result.
But changeHandler never gets called. Am i doing something wrong?
Thanks!
I found solution, not ideal but it's works! Maybe this is can help someone in future.
When new rewarded request finishes, isReady property is set to true or false depends what response is.
private func createAndLoadRewardedAd() -> GADRewardedAd {
let rewardedAd = GADRewardedAd(adUnitID: "ca-app-pub-3940256099942544/1712485313")
rewardedAd.load(GADRequest()) { [weak self] error in
guard let self = self else { return }
self.videoIsReady?(rewardedAd.isReady) // already set
}
return rewardedAd
}
You are welcome!
Related
I am pretty new to the swift, I am trying to implement the feature that when the user logged in, they will get directly to the home page rather than the login page every time they reopen the app.
I took the reference to the tutorial: https://www.youtube.com/watch?v=gjYAIXjpIS8&t=146s. and I implemented the is logged in boolean checking as he did, but I somehow encounter the trouble reopen the homepage while logged in. I have an error message:[Presentation] Attempt to present <UITabBarController: 0x7fa68102ea00> on <IFTTT.ViewController: 0x7fa67fe0c150> (from <IFTTT.ViewController: 0x7fa67fe0c150>) whose view is not in the window hierarchy.
This is how my login page controller class:(which is the entry point when opening the app) I tried present as the tutorial and performsegue and both shows up the same error message above
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if isLoggedIn() {
performSegue(withIdentifier: "logInJump", sender: nil)
}
}
fileprivate func isLoggedIn() -> Bool {
print("logged in status: \(UserDefaults.standard.bool(forKey: "isLoggedIn"))")
return UserDefaults.standard.bool(forKey: "isLoggedIn")
}
#IBAction func signInButton(_ sender: Any) {
print("sign in tapped")
if let appURL = URL(string: "http://vocation.cs.umd.edu/flask/register") {
UIApplication.shared.open(appURL) { success in
if success {
print("The URL was delivered successfully.")
} else {
print("The URL failed to open.")
}
}
} else {
print("Invalid URL specified.")
}
}
}
// button broder width
#IBDesignable extension UIButton {
#IBInspectable var borderWidth: CGFloat {
set {
layer.borderWidth = newValue
}
get {
return layer.borderWidth
}
}
#IBInspectable var cornerRadius: CGFloat {
set {
layer.cornerRadius = newValue
}
get {
return layer.cornerRadius
}
}
#IBInspectable var borderColor: UIColor? {
set {
guard let uiColor = newValue else { return }
layer.borderColor = uiColor.cgColor
}
get {
guard let color = layer.borderColor else { return nil }
return UIColor(cgColor: color)
}
}
}
and this is my messy story board and segues :
story board
I tried adding a navigation controller that the app entry point gets in there and it performs the isLoggedIn the same as the view controller class did, but it also has the same error.
Can someone walk me through how to fix it or any other better techniques? I felt like I am blind since I just get into the study of swift. Thank you!
You need to thread your request to perform the segue. Because your calling performSegue in the viewDidLoad() what happens is that your call is being called before everything is loaded, so you need to introduce some lag.
Threads are sometimes called lightweight processes because they have
their own stack but can access shared data. Because threads share the
same address space as the process and other threads within the
process, the operational cost of communication between the threads is
low, which is an advantage
An asynchronous function will await the execution of a promise, and an
asynchronous function will always return a promise. The promise
returned by an asynchronous function will resolve with whatever value
is returned by the function
Long story short, you need to wait for everything to be loaded into memory and if you're calling functions from the main stack/thread e.g. viewDidLoad() then there is a good chance that it hasn't been loaded into memory yet. Meaning, logInJump segue doesn't exist at that point in that view controller, thus your error.
The other possibility is you don't have the right view/segue ID but that should've thrown a different error.
Also, change the sender from nil to self. Actually this isn't necessary but I've always used self over nil
// change to desired number of seconds should be higher then 0
let when = DispatchTime.now() + 0.2
if isLoggedIn() {
DispatchQueue.main.asyncAfter(deadline: when) {
self.dismiss(animated: true, completion: nil)
self.performSegue(withIdentifier: "logInJump", sender: nil)
}
}
This is pretty long but it's probably a really easy fix.
Most of this is code, just skip to the bottom and there'll be a tl;dr
Hey, so I've followed about 10 tutorials on getting Admob interstitials to work in my Swift Spritekit game. Except all the tutorials I've seen use an IBAction with a button.
Currently I would like the ads to pop up every once in a while on the menu. (just to begin with, I might change it later).
This all works perfectly, but once I hit the play button (going to a different scene) and then return to the menu scene. (GameScene) The interstitial function no longer happens. There is no error, it just doesn't do it any more until the app is completely closed and re-opened.
Here's the code.
In my GameViewController class I have:
var interstital: GADInterstitial!
And in the GameViewController did load I have:
if let scene = GameScene(fileNamed:"GameScene") {
let skView = self.view as? SKView
skView!.ignoresSiblingOrder = false
scene.scaleMode = .AspectFill
scene.viewController = self
skView!.presentScene(scene)
interstital = GADInterstitial(adUnitID: "ca-app-pub-9776687746813194/9687097060")
let request = GADRequest()
interstital.loadRequest(request)
Also in the GameViewController class I have these functions:
func ShowAd() {
print("doing the showadfunc now")
if (interstital.isReady) {
print("there is an inter ready")
interstital.presentFromRootViewController(self)
interstital = CreateAd()
}
else{
print("The interstitial wasn't ready.")
}
}
func CreateAd() -> GADInterstitial {
let interstital = GADInterstitial(adUnitID: "ca-app-pub-9776687748683194/9687247060")
interstital.loadRequest(GADRequest())
return interstital
}
In my GameScene's (The menu) class I have:
var viewController: GameViewController!
And in my GameScene's class I have this function for displaying an interstitial ad.
func adThing(){
//viewController?.ShowAd()
print("starting the ad function")
let waitForInterstitial = SKAction.waitForDuration(15.0)
let waitForInterstitialShort = SKAction.waitForDuration(3.0)
let showInterstitialAd = SKAction.runBlock({self.viewController?.ShowAd(); print("the function has been called..")})
let waitThenShowInterstitial = SKAction.sequence([showInterstitialAd,waitForInterstitial])
let waitThenShowInterStitialForever = SKAction.repeatActionForever(waitThenShowInterstitial)
//self.runAction(waitThenShowInterStitialForever)
self.runAction(waitForInterstitialShort, completion: {
print("its waited 3 seconds and will now start the ad thingo")
self.runAction(waitThenShowInterStitialForever)
})
print("done")
}
So, I call the ShowAd function (which is located in gameviewcontroller) in my GameScene every 20 seconds, (I have confirmed it at least thinks it is calling the function.) It works perfectly when the app is opened to begin with. But when I change scene and then return to the menu scene (GameScene), the Prints in my GameScene function keep happening, and my code thinks that it is calling the ShowAd function, but it seems that nothing is happening, no interstitials pop up, not even the prints that are in the ShowAd function in the GameViewController.
So it seems like after I change scenes and return, my game forgets what the GameViewController is. If I refer to the GameViewController without a '?' it will crash my game with the error "unexpectedly found nil while unwrapping an optional." So after changing scenes, maybe GameViewController becomes nil? How can I fix this.
Please help me!!! (I'm new to programming in case you can't tell. I know it must be tempting to make a snide comment about my code, it is really bare bones and not that good at the moment.)
Thank you so much if you can help me, and wow.. thanks for reading all this.. ;)
You problem most likely is that once you transition to a new scene the viewController property is set to nil hence a crash if you use !.
In general its not the best practice to reference the viewController in your SKScenes, you should use Delegation or NSNotification Center.
1) Notification Center (easiest way)
In your ViewController where you have the function to show ads add a NSNotificationCenter observer in ViewDidLoad
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(showAd), name: "ShowInterAdKey", object: nil)
(Selector is the function to call and name is the key to reference this observer)
Than in your SKScenes when you want to show the ad you simply post the notification
NSNotificationCenter.defaultCenter().postNotificationName("ShowInterAdKey", object: nil)
2) For delegation examples check out these articles.
http://stephenradford.me/creating-a-delegate-in-swift/
https://www.natashatherobot.com/ios-weak-delegates-swift/
3) I also noticed that you are not preloading the ads before you show them, which is not very efficient and can cause delays showing ads.
You should call
interstital = CreateAd()
in viewDidLoad so it will load the first ad ASAP. Than you should use the adMob delegate method
func interstitialDidDismissScreen(ad: GADInterstitial!) {
// load next ad
interstital = CreateAd()
to immediately load the next ad once the current one is closed.
To use the delegates you can create an extension in ViewController
extension MyViewController: GADInterstitialDelegate {
func interstitialDidReceiveAd(ad: GADInterstitial!) {
print("AdMob interstitial did receive ad from: \(ad.adNetworkClassName)")
}
func interstitialWillPresentScreen(ad: GADInterstitial!) {
print("AdMob interstitial will present")
// pause game/app
}
func interstitialWillDismissScreen(ad: GADInterstitial!) {
print("AdMob interstitial about to be closed")
}
func interstitialDidDismissScreen(ad: GADInterstitial!) {
print("AdMob interstitial closed, reloading...")
interstitial = createAd()
}
func interstitialWillLeaveApplication(ad: GADInterstitial!) {
print("AdMob interstitial will leave application")
// pause game/app
}
func interstitialDidFailToPresentScreen(ad: GADInterstitial!) {
print("AdMob interstitial did fail to present")
}
func interstitial(ad: GADInterstitial!, didFailToReceiveAdWithError error: GADRequestError!) {
print(error.localizedDescription)
}
}
To make sure these get called go to your createAd function and add this line before returning the ad
let interstitial = ...
interstitial.delegate = self
Finally your showAd method should look like this
func showAd() {
print("doing the showadfunc now")
if (interstital.isReady) {
print("there is an inter ready")
interstital.presentFromRootViewController(self)
} else{
print("The interstitial wasn't ready, reloading again.")
interstital = CreateAd()
}
}
4) You can also check out my helper on gitHub if you are looking for a more reusable solution.
https://github.com/crashoverride777/Swift-AdMob-AppLovinTVOS-CustomAds-Helpers
As a general tip you should also follow the swift naming conventions, your func and properties should start with small letters. Only classes, structs, enums and protocols should start with capital letters.
It makes your code harder to read for yourself and on stackoverflow because it is marked blue but shouldn't. You are sometimes doing and sometimes not.
Hope this helps
I'd bet this is your issue:
if (interstital.isReady) {
print("there is an inter ready")
interstital.presentFromRootViewController(self)
interstital = CreateAd()
You're presenting your interstitial and then immediately overwriting your interstitial. You should be implementing the GADInterstitial's delegate methods and calling your func CreateAd() -> GADInterstitial once func interstitialDidDismissScreen(ad: GADInterstitial!) is called.
if your device keeps crashing because of:
interstitial.delegate = self
then put it in your CreatAndLoadInterstitial(), not your viewDidLoad()
I'm using SpriteKit for my iOS app, and need some full-screen (or sometimes called "interstitial" iAds to pop up every so often. I can figure out the code for when it happens, but I need to know how to add iAds without necessarily changing the View Controller.
I tried a [tutorial][1] by Techotopia on Interstitial iAds, but to do it I need to transition between actual View Controllers. Well, I tried this, but whenever transitioning back from the iAd view controller to the GameViewController, it messed it all up. The code in the GameViewController.m says that it is initially set to the main menu of my game (an SKScene). So when I try transitioning back from the iAd, instead of keeping the same SKScene up, it goes strait to my main menu scene.
I need these answers:
Is there some way other than using two View Controllers to show Interstitial iAds?
If not, how can I get around this problem?
Would this problem also occur if I were to change my "Interstitial iAds" to "Pre-roll Video iAds?"
-
EDIT! EDIT! EDIT! EDIT! EDIT! EDIT! EDIT! EDIT! EDIT! EDIT! EDIT! EDIT! EDIT! EDIT!
-
I ended up deciding to use crashoverride777's answer, and creating an Objective-C bridging header to convert his Swift code to Objective-C. But when referencing the Ads.swift's variable presentingViewController, it ends up being nil. Here is my code in GameViewController.m:
// Set up interstitial iAds.
Ads *adsClass = [[Ads alloc] init];
Ads *adsInstance = [Ads sharedInstance];
adsInstance.presentingViewController = self;
This, however, doesn't do anything. The function showInterAd gets called from my GameScene.m like it's supposed to, but no ad appears. In my Ads.swift, I added a log to see if presentingViewController is indeed nil.
print("iAd inter showing")
if (self.presentingViewController != nil) {
print("presentingViewController != nil")
iAdInterAdView.frame = presentingViewController.view.bounds
presentingViewController.view.addSubview(iAdInterAdView)
iAdInterAd!.presentInView(iAdInterAdView)
UIViewController.prepareInterstitialAds()
iAdInterAdView.addSubview(iAdInterAdCloseButton)
} else {
print("presentingViewController == nil")
}
The log comes out like this every time: "presentingViewController == nil". I'm not sure what's going wrong, here.
You can make use of my helper I posted on github. Its was made specifically for spritekit and you can call ads from anywhere without delegates etc or changing view controllers.
https://github.com/crashoverride777/Swift-2-iAds-and-AdMob-Helper
I think having a look at that helper should give you a good idea of where to start. Here is a cut down example of how it could look just for iAds Inter ads.
This is in swift so I am not sure if it is still helpful for you.
import iAd
class Ads: NSObject {
// MARK: - Static Properties
/// Shared instance
static let sharedInstance = Ads()
// MARK: - Properties
/// Presenting view controller
var presentingViewController: UIViewController!
/// iAd inter ad
private var iAdInterAd: ADInterstitialAd?
/// iAd inter ad view
private var iAdInterAdView = UIView()
/// iAd inter ad close button
private var iAdInterAdCloseButton = UIButton(type: UIButtonType.System)
// MARK: - Init
private override init() {
super.init()
print("Ads helper init")
iAdInterAd = iAdLoadInterAd()
}
// MARK: - User Methods
/// Show inter ad
func showInterAd() {
iAdShowInterAd()
}
/// Show inter ad randomly (33% chance)
func showInterAdRandomly() {
let randomInterAd = Int(arc4random() % 3)
print("randomInterAd = \(randomInterAd)")
if randomInterAd == 1 {
iAdShowInterAd()
}
}
/// Remove all ads
func removeAllAds() {
print("Removed all ads")
if iAdInterAd != nil {
iAdInterAd!.delegate = nil
iAdInterAdCloseButton.removeFromSuperview()
iAdInterAdView.removeFromSuperview()
}
}
// MARK: - Internal Methods
/// iAd load inter ad
private func iAdLoadInterAd() -> ADInterstitialAd {
print("iAd inter ad loading...")
let iAdInterAd = ADInterstitialAd()
iAdInterAd.delegate = self
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
iAdInterAdCloseButton.frame = CGRectMake(18, 18, 27, 27)
} else {
iAdInterAdCloseButton.frame = CGRectMake(13, 13, 22, 22)
}
iAdInterAdCloseButton.layer.cornerRadius = 11
iAdInterAdCloseButton.setTitle("X", forState: .Normal)
iAdInterAdCloseButton.setTitleColor(UIColor.grayColor(), forState: .Normal)
iAdInterAdCloseButton.backgroundColor = UIColor.whiteColor()
iAdInterAdCloseButton.layer.borderColor = UIColor.grayColor().CGColor
iAdInterAdCloseButton.layer.borderWidth = 2
iAdInterAdCloseButton.addTarget(self, action: "iAdPressedInterAdCloseButton:", forControlEvents: UIControlEvents.TouchDown)
return iAdInterAd
}
/// iAd show inter ad
private func iAdShowInterAd() {
guard iAdInterAd != nil else {
print("iAd inter is nil, reloading")
iAdInterAd = iAdLoadInterAd()
return
}
if iAdInterAd!.loaded {
print("iAd inter showing")
iAdInterAdView.frame = presentingViewController.view.bounds
presentingViewController.view.addSubview(iAdInterAdView)
iAdInterAd!.presentInView(iAdInterAdView)
UIViewController.prepareInterstitialAds()
iAdInterAdView.addSubview(iAdInterAdCloseButton)
//pauseTasks() // not really needed for inter as you tend to show them when not playing.
} else {
print("iAd inter not ready, reloading again...")
iAdInterAd = iAdLoadInterAd()
}
}
/// iAd inter ad pressed close button
func iAdPressedInterAdCloseButton(sender: UIButton) { // dont make private as its called with a selector
print("iAd inter closed")
iAdInterAd!.delegate = nil
iAdInterAdCloseButton.removeFromSuperview()
iAdInterAdView.removeFromSuperview()
iAdInterAd = iAdLoadInterAd()
//resumeTasks() // not really needed for inter as you tend to not show them during gameplay
}
/// Pause tasks in the app/game
private func pauseTasks() {
// Pause app/game, music etc here.
// you could use NSNotifactionCenter or Delegates to call methods in other SKScenes / ViewControllers
}
/// Resume tasks in the app/game
private func resumeTasks() {
// Resume app/game, music etc here.
// you could use NSNotifactionCenter or Delegates to call methods in other SKScenes / ViewControllers
}
}
// MARK: - Delegates iAd Inter
extension Ads: ADInterstitialAdDelegate {
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
print("iAd inter did load")
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
print("iAd inter did unload")
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
print("iAd inter error \(error)")
iAdInterAd!.delegate = nil
iAdInterAdCloseButton.removeFromSuperview()
iAdInterAdView.removeFromSuperview()
}
}
Now you simply call
Ads.sharedInstance.presentingViewController = self
in your GameViewController before doing anything else. This will init the helper and preload the first inter ad.
Than you simply call these anywhere you would like inter ads to show
GameData.sharedInstance.showInterAd()
or
GameData.sharedInstance.showInterAdRandomly() // 33% chance for ad
Presenting interstitial in sprite kit is almost identical to in other parts of the app. The only difference is you present the add by calling all the iAd methods with 'self.view'. This gets the view the SKScene is presented in and present the iAd over it
also this can be used to present interstitial without switching view controllers
currentView.requestInterstitialAdPresentation()
to request from in the scene:
self.view?.requestInterstitialAdPresentation()
Notice how in the function call it says request. That is why it only shows up sometimes. When you 'request' an ad the device sends a request to apple asking for an ad. If you get a response back in a reasonable amount of time then the ad will present. If you do not get a response back in a reasonable amount of time (most likely due to poor internet) there the ad won't present because theres nothing to present. However if you request once and it doesn't present, the next time you ask it will definitely present because you already have an ad loaded from the last time you requested but it never got presented.
I'm new to programming with iOS and Swift. I have a label that should automatically be updated when the status of the StreamingKit Framework changes.
Currently the label only changes when I press a different button (it calls the self.updateView() function again), but I want it to happen automatically when the status has changed, not by pressing a different button.
Here is the code:
func updateView() {
dispatch_async(dispatch_get_main_queue()) {
if let label = self.bufferingLabel, let button = self.playerButton{
if let audioPlayer = self.player{
if(audioPlayer.state == STKAudioPlayerStateBuffering)
{
self.playerState = "Loading"
button.setImage(self.image1, forState: UIControlState.Normal)
}
else(audioPlayer.state == STKAudioPlayerStatePlaying)
{
self.playerState = self.defaultPlayerState
self.playerState = "Playing"
button.setImage(self.image1, forState: UIControlState.Normal)
}
label.text = self.playerState!
}
}
So when I press this button, it gives the first state (which is loading), but after that is done (status changed), the label should change to play, but it doesn't.
#IBAction func pressStart(sender: AnyObject) {
//declare path to streaming
let filePath = "http://mp3.streampower.be/mnm-high.mp3"
if player == nil {
self.player = STKAudioPlayer()
self.player?.delegate = self
}
if let audioPlayer = player{
if (audioPlayer.state == STKAudioPlayerStateReady) ||
(audioPlayer.state == STKAudioPlayerStateStopped){
audioPlayer.play(filePath)
}else{
audioPlayer.stop()
}
}
self.updateView()
}
Some of the delegates I tried as well
func audioPlayer(audioPlayer: STKAudioPlayer!, didStartPlayingQueueItemId queueItemId: NSObject!) {
self.updateView()
}
func audioPlayer(audioPlayer: STKAudioPlayer!, didFinishBufferingSourceWithQueueItemId queueItemId: NSObject!) {
self.updateView()
}
func audioPlayer(audioPlayer: STKAudioPlayer!, stateChanged state: STKAudioPlayerState, previousState: STKAudioPlayerState) {
self.updateView()
}
You should look and use the delegate property of the STKAudioPlayer.
I'm not familiar with it, but generally in the iOS world, that's how you receive events from an object (the object delegates some stuff to the object of your choice).
You can use NSNotifications to send a signal whenever the streaming kit changes. Then you can add an observer to your UI view controller that will trigger a function whenever it receives a signal.
If you want to try out functional reactive programming you can also use The Reactive Cocoa framework to do a similar signal observer pattern. And finally, there is plain old key-value observation. One of these options should be able to help you.
I'm facing a little issue here and I hope someone will help me figure out what is wrong.
*The test project presented below can be find here : http://goo.gl/wz84aA (FR) or https://goo.gl/0m8LrZ (Mega.NZ) *
I'm trying to present to the user the authentification view controller proposed by apple for the GameCenter feature. More precisely, re-present it if he canceled it on the first time.
I have a game with a storyboard like that :
GameNavigationController :
class GameNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("showAuthenticationViewController"), name: PresentAuthenticationViewController, object: nil)
GameKitHelper.sharedInstance.authenticateLocalPlayer()
}
func showAuthenticationViewController() {
let gameKitHelper = GameKitHelper.sharedInstance
if let authenticationViewController = gameKitHelper.authenticationViewController {
self.topViewController.presentViewController(authenticationViewController, animated: true, completion: nil)
}
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
MenuViewController :
class MenuViewController: UIViewController {
#IBAction func didTapLeaderboardBTN() {
// TRY 2
//if ( !GameKitHelper.sharedInstance.gameCenterEnabled) {
GameKitHelper.sharedInstance.authenticateLocalPlayer()
//} else {
GameKitHelper.sharedInstance.showGKGameCenterViewController(self)
//}
}
}
GameKitHelper :
import GameKit
import Foundation
let PresentAuthenticationViewController = "PresentAuthenticationViewController"
let singleton = GameKitHelper()
class GameKitHelper: NSObject, GKGameCenterControllerDelegate {
var authenticationViewController: UIViewController?
var lastError: NSError?
var gameCenterEnabled: Bool
class var sharedInstance: GameKitHelper {
return singleton
}
override init() {
gameCenterEnabled = true
super.init()
}
func authenticateLocalPlayer () {
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = { (viewController, error) in
self.lastError = error
if viewController != nil {
self.authenticationViewController = viewController
NSNotificationCenter.defaultCenter().postNotificationName(PresentAuthenticationViewController, object: self)
} else if localPlayer.authenticated {
self.gameCenterEnabled = true
} else {
self.gameCenterEnabled = false
}
}
}
func showGKGameCenterViewController(viewController: UIViewController!) {
if ( !self.gameCenterEnabled ) {
println("Local player is not authenticated")
// TRY 1
//self.authenticateLocalPlayer()
return
}
let gameCenterViewController = GKGameCenterViewController()
gameCenterViewController.gameCenterDelegate = self
gameCenterViewController.viewState = .Leaderboards
viewController.presentViewController(gameCenterViewController, animated: true, completion: nil)
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!) {
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
}
What is currently working :
if the user is previously logged-in (within the GameCenter app) then he's able to open the leaderboard view ;
if the user wasn't logged-in then he's prompted to log-in when the game navigation controller is loaded (and then open the leaderboard).
What is NOT currently working :
if he cancel three time the authentification, then the authentification won't appear anymore (even at launch) ; // Apparently a known "problem", not "fixable"
if the user cancel his authentification, when he tries to load the leaderboard the authentification won't appear again.
I tried 2-3 things as you can see in the commented code above, but none of them is working ; I can't make the authentification view appear again.
PS : My code is written in Swift, but help in Objective-C is welcomed as well.
As you have found out, if the Game Center authentication dialog is canceled 3 times, then you can't bring it back without resetting the device.
There is another "security feature" built into Game Center which does not allow an app to re-authenticate if the user has already canceled the dialog without leaving the app. So for your authentication dialog to show, the user must leave and then re-enter your app.
There is really no way around it. What I've done in a couple of projects is to display a message to the user:
Game Center not available. Please make sure you are signed in through the Game Center app
I will show that message after trying to authenticate and if Game Center isn't available or the user is not signed in.
If you want to be able to re-present this to your user then go to settings -> General -> Reset -> -> Reset Location & Privacy.
This will force iOS to forget preferences for apps for example whether they can use location services, send you push notifications and also game centre preferences. Keep in mind this will reset privacy settings for all apps.