Game centre issues when viewing leaderboard and scores - ios

I've integrated Game Centre into my app the following way:
When the game loads
func authPlayer(){
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {
(view, error) in
if view != nil {
self.present(view!, animated: true, completion: nil)
}
else {
print(GKLocalPlayer.localPlayer().isAuthenticated)
}
}
}
And then in the gameOver scene:
func saveHighscore(_ number : Int){
if GKLocalPlayer.localPlayer().isAuthenticated {
let scoreReporter = GKScore(leaderboardIdentifier: "...")
scoreReporter.value = Int64(number)
let scoreArray : [GKScore] = [scoreReporter]
GKScore.report(scoreArray, withCompletionHandler: nil)
}
}
func showLeaderboard() {
let gameCenter = GKGameCenterViewController()
gameCenter.gameCenterDelegate = self
gameCenter.viewState = GKGameCenterViewControllerState.leaderboards
if let gameViewController = self.view?.window?.rootViewController {
gameViewController.show(gameCenter, sender: self)
gameViewController.navigationController?.pushViewController(gameCenter, animated: true)
}
}
Some devices can't find the leaderboard, other devices can find it but only have their score showing etc.
Have I missed something?
Authenticating works fine.

Related

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)
}

CXCallObserver is not working properly and App getting crash when running the app more than one (when includes contacts image data)

I am facing two major problem first one is :
1. I am trying to detect incoming call, outgoing call , dialing call for this i am using this code :
import UIKit
import CoreTelephony
import CallKit
class ViewController: UIViewController,CXCallObserverDelegate {
let callObserver = CXCallObserver()
var seconds = 0
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
callObserver.setDelegate(self, queue: nil)
}
override func viewWillAppear(_ animated: Bool) {
print("viewWillAppear \(seconds)")
}
fileprivate func runTimer(){
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true)
}
func updateTimer() {
seconds += 1
print("Seconds \(seconds)")
}
#IBAction func callButton(_ sender: UIButton) {
if let url = URL(string: "tel://\(12345879)"){
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
if call.hasEnded == true {
print("Disconnected")
seconds = 0
self.timer.invalidate()
}
if call.isOutgoing == true && call.hasConnected == false {
print("Dialing call")
self.runTimer()
}
if call.isOutgoing == false && call.hasConnected == false && call.hasEnded == false {
print("Incoming")
}
if call.hasConnected == true && call.hasEnded == false {
print("Connected")
}
}
}
It working fine when i am dialing a number it shows "Dialling" but when i cut the call then it shows "Disconnected" then again "Dialing" State.
Another problem is when i am fetching all contacts information from the device it works fine when i am not fetching imageData but when i am fetching contacts image it works fine for the very first time . Then if i run it again app become slow . then next it crash shows found nil while unwrapping a value.
i wrote my contact data fetching function in AppDelegate . it is calling when the app start . this is the code :
func fetchContactList(){
let loginInformation = LoginInformation()
var contactModelData: [ContactsModel] = []
var profileImage : UIImage?
let store = CNContactStore()
store.requestAccess(for: .contacts, completionHandler: {
granted, error in
guard granted else {
let alert = UIAlertController(title: "Can't access contact", message: "Please go to Settings -> MyApp to enable contact permission", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
return
}
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),CNContactPhoneNumbersKey, CNContactEmailAddressesKey, CNContactPostalAddressesKey, CNContactImageDataKey, CNContactImageDataAvailableKey,CNContactThumbnailImageDataKey,CNContactThumbnailImageDataKey] as [Any]
let request = CNContactFetchRequest(keysToFetch: keysToFetch as! [CNKeyDescriptor])
var cnContacts = [CNContact]()
do {
try store.enumerateContacts(with: request){
(contact, cursor) -> Void in
cnContacts.append(contact)
}
} catch let error {
NSLog("Fetch contact error: \(error)")
}
for contact in cnContacts {
let fullName = CNContactFormatter.string(from: contact, style: .fullName) ?? "No Name"
var phoneNumberUnclean : String?
var labelofContact : String?
var phoneNumberClean: String?
for phoneNumber in contact.phoneNumbers {
if let number = phoneNumber.value as? CNPhoneNumber,
let label = phoneNumber.label {
let localizedLabel = CNLabeledValue<CNPhoneNumber>.localizedString(forLabel: label)
print("fullname \(fullName), localized \(localizedLabel), number \(number.stringValue)")
phoneNumberUnclean = number.stringValue
labelofContact = localizedLabel
}
}
if let imageData = contact.imageData {
profileImage = UIImage(data: imageData)
print("image \(String(describing: UIImage(data: imageData)))")
} else {
profileImage = UIImage(named: "user")
}
self.contactModelData.append(ContactsModel(contactName: fullName, contactNumber:phoneNumberUnclean!, contactLabel: labelofContact!, contactImage: profileImage!, contactNumberClean: phoneNumberUnclean!))
}
self.loginInformation.saveContactData(allContactData: self.contactModelData)
})
}
I have solved this two problems using this :
for number one when i disconnect a call then if unfortunately it goes to "Dialling" option again i checked the "seconds" variable's value if it greater than 0 in "Dialing" then invalidate the thread.
for number two problem :
I used Dispatch.async.main background thread and take the thumbnail image

Swift GameCenter not behaving properly

I have 2 problems I am experiencing with GameCenter.
If a player is not signed into GameCenter, after if gives you the alert saying you are not signed in, I am no longer able to click any buttons I have on my view.
If a player is signed into GameCenter, after accessing the Leaderboards, pressing “done” will not dismiss the leaderboard view.
Here is my code that relates to GameCenter:
GameViewController:
func authPlayer() { // Gets called in ViewDidLoad
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {
(view, error) in
if view != nil {
self.presentViewController(view!, animated: true, completion: nil)
}
else {
print(GKLocalPlayer.localPlayer().authenticated)
}
}
}
GameScene:
if gameCenterBtn.containsPoint(location) { // In touches began for touch in touches
saveHighScore(highLevel)
let viewController = self.view!.window?.rootViewController
let gcvc = GKGameCenterViewController()
viewController?.presentViewController(gcvc, animated: true, completion: nil)
}
func saveHighScore(number : Int) {
if GKLocalPlayer.localPlayer().authenticated {
let scoreReporter = GKScore(leaderboardIdentifier: "myLeaderBoardID")
scoreReporter.value = Int64(number)
let scoreArray : [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: nil)
}
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
Any and all help would be appreciated
1) Not sure this will help, but your login code is not quite correct. If there is no login view controller doesnt necessarily mean the player is signed in. You are also not handling the optional error. Try this instead.
localPlayer.authenticateHandler = { [unowned self] (viewController, error) in // will handle login changes also
if let error = error {
print(error.localizedDescription)
return
}
if let viewController = viewController {
self.presentViewController(viewController, animated: true, completion: nil)
}
else if self.localPlayer.authenticated {
print("Player authenticated")
}
else {
print("Player not authenticated")
}
}
I am not sure why your gameViewCntroller will not respond. Are you not just loading the first SKScene in your gameViewController.
Could you describe further, maybe with some code, of what does not work afterwards.
2) Its not dismissing the screen because you did not set the delegate.
You code where you are creating the Game Center viewController should look like this
let viewController = self.view?.window?.rootViewController
let gcvc = GKGameCenterViewController()
gcvc.gameCenterDelegate = self // YOU FORGOT THIS LINE
viewController?.presentViewController(gcvc, animated: true, completion: nil)

Show Game Centre Leaderboard not working in swift

I am coding in swift and I am getting the following error message from my submit button. The leaderboard does not show up in the simulator, just gives me the error message below
<GKGameCenterViewController: 0x7a8d0800> on <Chinese_Quiz.OpeningViewController: 0x78688aa0> whose view is not in the window hierarchy!
Below is all the game centre code from my app.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Randomize()
Hide()
authenticateLocalPlayer()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func Submit(sender: AnyObject) {
saveHighscore(Score)
showLeaderboard()
}
func authenticateLocalPlayer(){
var localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if (viewController != nil) {
self.presentViewController(viewController, animated: true, completion: nil)
}
else {
println((GKLocalPlayer.localPlayer().authenticated))
}
}
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!)
{
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
func showLeaderboard() {
var vc = self.view?.window?.rootViewController
var gc = GKGameCenterViewController()
gc.gameCenterDelegate = self
vc?.presentViewController(gc, animated: true, completion: nil)
}
func saveHighscore(score:Int) {
//check if user is signed in
if GKLocalPlayer.localPlayer().authenticated {
var scoreReporter = GKScore(leaderboardIdentifier: "ChineseWeather") //leaderboard id here
scoreReporter.value = Int64(Score) //score variable here (same as above)
var scoreArray: [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: {(error : NSError!) -> Void in
if error != nil {
println("error")
}
})
}
}
Solved by turning this:
func showLeaderboard() {
var vc = self.view?.window?.rootViewController
var gc = GKGameCenterViewController()
gc.gameCenterDelegate = self
vc?.presentViewController(gc, animated: true, completion: nil)
}
Into this:
func showLeaderboard() {
var vc = self
var gc = GKGameCenterViewController()
var gameCenterDelegate: GKGameCenterControllerDelegate!
// Above text was replaced from gc.gameCenterDelegate = self
vc.presentViewController(gc, animated: true, completion: nil)
}
I don't know why, but it worked.

Game Center Leaderboard: new score doesn't show up instantly

I have the following code which reports score and also displays leaderboard. Everything seems to be okay but when I send it new updated score, it doesn't show the score on leaderboard unless I click on Challenges tab or All Time. Basically, it needs me to click on something else inside leaderboard so the leaderboard can refresh and display the most recent score.
func reportLeaderboardIdentifier(identifier: String, score: Int) {
let scoreObject = GKScore(leaderboardIdentifier: identifier)
scoreObject.value = Int64(score)
GKScore.reportScores([scoreObject]) { (error) -> Void in
if error != nil {
println("Error in reporting leaderboard scores: \(error)")
}
}
}
func displayLeaderboard () {
var gameCenterController: GKGameCenterViewController = GKGameCenterViewController()
gameCenterController.viewState = GKGameCenterViewControllerState.Leaderboards
gameCenterController.gameCenterDelegate = self
self.vc.presentViewController(gameCenterController, animated: true , completion:nil)
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!) {
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}

Resources