My Game Center Authentication is not working. When I build and run, it won't show my username.. has signed in. Also, when I try to add my score I get a screen that says "no data availible". Heres my code.
override func viewDidLoad() {
super.viewDidLoad()
gcAuthPlayer()
}
#IBAction func GCButton(sender: AnyObject) {
saveHighScore(GameScene().highScoreNumer)
showLeaderBoard()
if GameScene().currentScore > GameScene().highScoreNumer{
saveHighScore(GameScene().currentScore)
}
}
func showLeaderBoard(){
let viewController = self.view.window?.rootViewController
let gcvc = GKGameCenterViewController()
gcvc.gameCenterDelegate = self
viewController?.presentViewController(gcvc, animated: true, completion: nil)
}
func saveHighScore(number: Int){
if GKLocalPlayer.localPlayer().authenticated{
let scoreReporter = GKScore(leaderboardIdentifier: "myleaderboard")
scoreReporter.value = Int64(number)
let scoreArray : [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: nil)
}
}
func gcAuthPlayer(){
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {
(view, error) in
if view != nil{
self.presentViewController(view!, animated: true, completion: nil)
}else{
print(GKLocalPlayer.localPlayer().authenticated)
}
}
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
This code makes no sense
saveHighScore(GameScene().highScoreNumer)
showLeaderBoard()
if GameScene().currentScore > GameScene().highScoreNumer{
saveHighScor
You are creating a new instance of GameScene everytime you try to update the score and therefore your score is nil
I would need to see some more code but for now you need to change the score property in your game scene. For example make it a static property so you can get it in other classes.
class GameScene: SKScene {
static var currentScore = 0
static var highscoreNumber = 0
}
Than in your Scenes or ViewController you can get it like so
GameScene.currentScore = 5
GameScene.highscoreNumber = 5
Just remember that you have to reset the score to 0 everytime you restart your gameScene because it a static property.
GameScene.currentScore = 0
GameScene.highscoreNumber = 0
Than your code to post the score should look like this
saveHighScore(GameScene.highScoreNumer)
showLeaderBoard()
if GameScene.currentScore > GameScene.highScoreNumer{
saveHighScor
Your score reporting code should also handle the error and actually do the completion handler. So change it to something like this.
/// Save leaderboard progress
func reportLeaderboardProgress(value: Int, leaderboardID: String) {
let scoreReporter = GKScore(leaderboardIdentifier: leaderboardID)
scoreReporter.value = Int64(value)
GKScore.reportScores([scoreReporter]) { error in // Trailing Closure syntax
if let error = error {
print(error.localizedDescription)
return
}
print("Reported leaderboard progress \(value) to leaderboardID \(leaderboardID)")
}
}
It is also a good idea to move that code into another class to keep your overall code cleaner and more reusable.
For a nice and simple example check this helper on gitHub.
https://github.com/jackcook/GCHelper
Let me know how it goes.
Related
first of all thank you for reading my lines.
For an idea I'm currently trying to dive into the Swift world (I only have very basic programming knowledge - no Objective C knowledge
).
I tried to set up the following lines to create a very basic app-to-app sample in Sinch. After my code I let you know what the issues are.
import UIKit
import Sinch
var appKey = "APP_KEY_FROM_MY_ACCOUNT"
var hostname = "clientapi.sinch.com"
var secret = "SECRET_FROM_MY_ACCOUNT"
class CViewController: UIViewController, SINCallClientDelegate, SINCallDelegate, SINClientDelegate {
var client: SINClient?
var call: SINCall?
var audio: SINAudioController?
//Text field in the main storyboard
#IBOutlet weak var userNameSepp: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.initSinchClient()
}
//initialize and start the client as a fixed "userA"
func initSinchClient() {
client = Sinch.client(withApplicationKey: appKey, applicationSecret: secret, environmentHost: hostname, userId: "userB")
client?.call().delegate = self
client?.delegate = self
client?.startListeningOnActiveConnection()
client?.setSupportCalling(true)
client?.start()
}
//Did the Client start?
func clientDidStart(_ client: SINClient!) {
print("Hello")
}
//Did the Client fail?
func clientDidFail(_ client: SINClient!, error: Error!) {
print("Good Bye")
}
//Call Button in the main.storyboard ... if call==nil do the call ... else hangup and set call to nil
//the background color changes are my "debugging" :D
#IBAction func callSepp(_ sender: Any) {
if call == nil{
call = client?.call()?.callUser(withId: userNameSepp.text)
//for testing I change to callPhoneNumber("+46000000000").
// the phone call progresses (but I hear nothing),
// the phonecall gets established (but I hear nothing)
// and the phonecall gets ended (but of course I hear nothing)
self.view.backgroundColor = UIColor.red
call?.delegate = self
audio = client?.audioController()
}
else{
call?.hangup()
self.view.backgroundColor = UIColor.blue
call = nil
}
}
func callDidProgress(_ call: SINCall?) {
self.view.backgroundColor = UIColor.green
client?.audioController().startPlayingSoundFile("/LONG_PATH/ringback.wav", loop: true)
print("Call in Progress")
}
//I know that this works but I don't hear anything
func callDidEstablish(_ call: SINCall!) {
client?.audioController().stopPlayingSoundFile()
print("Call did Establish")
}
func callDidEnd(_ call: SINCall!) {
print("Call did end")
}
// this works fine
#IBAction func hangUpSepp(_ sender: Any) {
call?.hangup()
self.view.backgroundColor = UIColor.red
call = nil
}
// i work in a "sub view controller" - so i navigate here back to the main view controller
#IBAction func goBackMain(_ sender: Any) {
call?.hangup()
dismiss(animated: true, completion: nil)
client?.stopListeningOnActiveConnection()
client?.terminateGracefully()
client = nil
}
}
So I can call my private phone number or if I change to callUser I can call another app but I don't hear anything. What do I miss? It must have to do with the SINAudioController and the client's method audioController() but I don't know what I'm doing wrong. Thank you for your help.
I have this code to pass info between viewControllers using protocols, I have my models and with the first protocol work perfectly but the second one I have some issues, the data pass nil, or do nothing, already use tabBarController & UINavigationBar... the protocols... I create one in my ViewController(RecordViewController) and have the button & action and one protocol, this is:
protocol RecordViewProtocol {
func newTrackInstrument(item: SampleCarouselsRecord)
func newInstrumentTrackCell(item: SampleTrackCellRecord)
}
extension RecordViewController: RecordViewProtocol {
func newTrackInstrument(item: SampleCarouselsRecord) {
self.sampleCarouselItemsRecord.append(item)
let indexOnPath = NSIndexPath(row: self.sampleCarouselItemsRecord.count - 1, section: 0)
self.instrumentCarousel.insertItems(at: [indexOnPath as IndexPath])
}
func newInstrumentTrackCell(item: SampleTrackCellRecord) {
self.sampleTrackRecord.append(item)
let indexOnPath = NSIndexPath(row: self.sampleTrackRecord.count - 1, section: 0)
self.trackInstrument.insertItems(at: [indexOnPath as IndexPath])
}
}
the other protocol, on another ViewController(MixerViewController):...
protocol MixerViewProtocol {
func newCarouselItem(item: SampleCarouselMixer)
}
extension MixerViewController: MixerViewProtocol {
func newCarouselItem(item: SampleCarouselMixer) {
self.sampleCarouselMixer.append(item)
let indexOnPath = NSIndexPath(row: self.sampleCarouselMixer.count - 1, section: 0)
self.mixerCarouselInstrument.insertItems(at: [indexOnPath as IndexPath])
}
}
so, when wanna go the view and put the info to my models.. I put this code in the action Button to go into the view where is the function to put the info models:
#objc func goToAddView() {
let addTrackViewController = AddNewTrackView()
let addTrackView = UINavigationController(rootViewController: addTrackViewController)
addTrackViewController.recordViewControllerProtocol = self as RecordViewProtocol
present(addTrackView, animated: true, completion: nil)
}
and work good with the first protocol.. but with to the other protocol nothing.. I don't know why or I don't know wath I have to do... look, I have the delegate protocols inside on the AddNweTrackView:
var recordViewControllerProtocol: RecordViewProtocol?
var delegateMixerView: MixerViewProtocol? <- this is I think, put into the action button like the firstone:
here on the same action where the first protocol already is
#objc func goToAddView() {
let addTrackViewController = AddNewTrackView()
let addTrackView = UINavigationController(rootViewController: addTrackViewController)
addTrackViewController.recordViewControllerProtocol = self as RecordViewProtocol
addTrackViewController.delegateMixerView = MixerViewController.self as? MixerViewProtocol
present(addTrackView, animated: true, completion: nil)
}
but return nil... and I try with diferents forms.. I try put this:
let mixerViewController = MixerViewController()
addTrackViewController.delegateMixerView = mixerViewController as? MixerViewProtocol
and try with this but the error Is obvious:
addTrackViewController.delegateMixerView = self as MixerProtocol
and the same... I think, here at this point, it's where Im failing .. someone can help?
I'm using AudioKit to set up a sounds player. My sounds engine is set up in a singleton with these functions:
override func viewDidLoad() {
super.viewDidLoad()
configureAudioPlayer()
}
func configureAudioPlayer() {
leftPanner = AKPanner(leftOscillator)
rightPanner = AKPanner(rightOscillator)
//Set up rain and rainPlayer
do {
rain = try AKAudioFile(readFileName: "rain.wav")
rainPlayer = try AKAudioPlayer(file: rain, looping: true, deferBuffering: false, completionHandler: nil)
} catch {
print(error)
}
mixer = AKMixer(leftPanner, rightPanner, rainPlayer)
envelope = AKAmplitudeEnvelope(mixer)
AudioKit.output = envelope
AudioKit.start()
}
func startPlaying() {
leftOscillator.start()
rightOscillator.start()
rainPlayer.start() //CRASHES HERE AND SAYS NIL OPTIONAL VALUE
envelope.start()
soundIsPlaying = true
}
In a separate view controller I have a play button that calls the following function when pressed:
let player = AudioPlayer.sharedPlayer
#IBAction func playSound(_ sender: Any) {
player.leftOscillator.frequency = 220
player.rightOscillator.frequency = 230
if player.soundIsPlaying == false {
player.startPlaying()
} else {
player.stopPlaying()
}
}
When I press this button the app crashes and says that the rainPlayer is nil. Did I set something up wrong? Why would the rainPlayer be nil when I configured it in the configureAudioPlayer() function?
UPDATE:
Moving the singleton into its own class that inherits from NSObject and calling configureAudioPlayer() in its init() function solves my problem. configureAudioPlayer() in the viewDidLoad() method was never being called.
I've been debugging my code and found that my manager was deinitialised (that was cause of my bug - not calling delegate methods).
What's strange, that during debugging process I've used "po" command after setting the manager's delegate (weak) and it prevented it from being deinitialised (delegate methods were called).
Why is that? Is it proper behaviour?
Xcode 8.3, swift 3.1
EDIT:
//a tap starts everything :)
#IBAction func shareButtonPressed(_ sender: Any) {
let requestManager = FacebookPostRouteRequest() //bug fixed by changing to instance variable
requestManager.delegate = self
requestManager.showShareBadgeDialog(self.badge!, onViewController: self)
}
//in FacebookPostRouteRequest
final weak var delegate: FacebookPostRouteRequestDelegate?
func showShareBadgeDialog(_ badge: Badge, onViewController viewController: UIViewController) {
let dialog = self.initDialog(onViewController: viewController)
guard let imageURL = badge.imageURL else {
self.delegate?.facebookPostRouteRequest(self, didCompleteWithResult: false)
return
}
dialog.shareContent = self.generateImageShareContent(imageURL)
self.show(dialog)
}
private func show(_ dialog: FBSDKShareDialog) {
OperationQueue.main.addOperation {
dialog.delegate = self //when printed out dialog.delegate delegate methods were called! Deinit of FacebookPostRouteRequest is not called.
let showResult = dialog.show()
...
}
}
extension FacebookPostRouteRequest: FBSDKSharingDelegate {
func sharer(_ sharer: FBSDKSharing!, didCompleteWithResults results: [AnyHashable : Any]!) {
...
}
//other delegate methods implemented as well
}
Your problem is here:
#IBAction func shareButtonPressed(_ sender: Any) {
let requestManager = FacebookPostRouteRequest()
requestManager.delegate = self
requestManager.showShareBadgeDialog(self.badge!, onViewController: self)
}
After the last line, the requestManager object will be disposed because it's no longer referenced and will not call any of the delegate methods.
Make requestManager an instance variable:
let requestManager = FacebookPostRouteRequest()
#IBAction func shareButtonPressed(_ sender: Any) {
requestManager.delegate = self
requestManager.showShareBadgeDialog(self.badge!, onViewController: self)
}
Your issues with the debugger are probably race conditions for stopping the main thread.
I made a game using SpriteKit and Xcode 7 beta. I tried to put GameCenter and Leaderboard but the problem is that the score in leaderboard won't change it stay all the time 0 (High Score of game won't save in Leaderboard) and I don't know how to fix it. I'm using 3 different files: GameScene.swift, GameViewController.swift, and PointsLabel.swift.
GameScene.swift
func addPointsLabels() {
let pointsLabel = PointsLabel(num: 0)
pointsLabel.position = CGPointMake(30.0, view!.frame.size.height - 40)
pointsLabel.name = "pointsLabel"
addChild(pointsLabel)
//High Score
let highscoreLabel = PointsLabel(num: 0)
highscoreLabel.name = "highscoreLabel"
highscoreLabel.position = CGPointMake(view!.frame.size.width - 35, view!.frame.size.height - 40)
addChild(highscoreLabel)
}
func loadHighscore() {
let defaults = NSUserDefaults.standardUserDefaults()
let highscoreLabel = childNodeWithName("highscoreLabel") as! PointsLabel
highscoreLabel.setTo(defaults.integerForKey("highscore"))
}
GameViewController.swift:
import GameKit
class GameViewController: UIViewController,UIGestureRecognizerDelegate, GKGameCenterControllerDelegate {
var scoreManager = PointsLabel(num: 0)
override func viewDidLoad() {
super.viewDidLoad()
//initiate gamecenter
func authenticateLocalPlayer(){
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(GameViewController, error) -> Void in
if (GameViewController != nil) {
self.presentViewController(GameViewController!, animated: true, completion: nil)
}
else {
print((GKLocalPlayer.localPlayer().authenticated))
}
}
}
}
#IBAction func leaderboard(sender: UIButton) {
saveHighscore(scoreManager.score)
scoreManager.increment()
showLeader()
}
//send high score to leaderboard
func saveHighscore(score:Int) {
//check if user is signed in
if GKLocalPlayer.localPlayer().authenticated {
let scoreReporter = GKScore(leaderboardIdentifier: "Leaderboard_01")
scoreReporter.value = Int64(score)
let scoreArray: [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: {error -> Void in
if error != nil {
print("error")
}
})
}
}
//shows leaderboard screen
func showLeader() {
let vc = self.view?.window?.rootViewController
let gc = GKGameCenterViewController()
gc.gameCenterDelegate = self
vc?.presentViewController(gc, animated: true, completion: nil)
}
}
//hides leaderboard screen
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController)
{
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
PointsLabel.swift:
import Foundation
import UIKit
import SpriteKit
class PointsLabel: SKLabelNode {
var score:Int = 0
init(num: Int) {
super.init()
fontColor = UIColor.blackColor()
fontSize = 30.0
score = num
text = "\(num)"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func increment() {
score++
text = "\(score)"
}
func setTo(num: Int) {
self.score = num
text = "\(self.score)"
}
}
I think the problem is in file GameViewController.swift in code:
#IBAction func leaderboard(sender: UIButton) {
saveHighscore(scoreManager.score)
scoreManager.increment() //<-- Here
showLeader()
}
Maybe I didn't put it in right place
scoreManager.increment()
Well I see a couple things that can contribute to the issue. First is this method...
func loadHighscore() {
let defaults = NSUserDefaults.standardUserDefaults()
let highscoreLabel = childNodeWithName("highscoreLabel") as! PointsLabel
highscoreLabel.setTo(defaults.integerForKey("highscore"))
}
I don't see anywhere you are setting that so pulling it out won't help much. I added the saving to defaults in the saveHighscore: function bellow.
Second is...
saveHighscore(scoreManager.score)
scoreManager.increment() //<-- Here
showLeader()
You should increment before you save your score.
I would try adding these logs to see if this helps...
func saveHighscore(score:Int) {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setInteger(score, forKey: "highscore")
defaults.synchronize()
//check if user is signed in
if GKLocalPlayer.localPlayer().authenticated {
println("authenticated")
let scoreReporter = GKScore(leaderboardIdentifier: "Leaderboard_01")
println("ScoreReporter: \(scoreReporter)")
scoreReporter.value = Int64(score)
let scoreArray: [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: {error -> Void in
if error != nil {
print("error")
}
else{
println("reported correctly")
}
})
}
}
Hopefully what does or don't print out in the logs plus actually saving your defaults will help. Good luck.
Edit
So it appears the root of the problem is that you have scoreManager (which is a PointsLabel) in your VC but you also have one that is in your Scene. The one in your scene you are updating the score and life is good. When you hit the button you are actually getting the score from the label in your VC that isn't getting updated. So what you really need is to get to that label in your scene to pull out the score.
So the easiest way I can think of getting it to work properly with as little changes to your code is this…
Remove this line completely…
var scoreManager = PointsLabel(num: 0)
and change your action to this...
#IBAction func leaderboard(sender: UIButton) {
let skView = self.view as! SKView
let scene = skView.scene
let scoreManager = scene.childNodeWithName("pointsLabel") as! PointsLabel
saveHighscore(scoreManager.score)
showLeader()
}
Hopefully that fixes everything =)
In your game, you need to increment the score AND send the score to Game Center whenever the user scores a point. I'm not sure how your game works, but whenever they should be awarded a point you need to update the score and send that score to game center and update the label. You just can't update the score when the leaderboard button is tapped. I'm not sure if I was clear so ask if you need clarification or help.
It sounds like you need to use core data, it is what you use to save data in an app. I don't know to much about it so I can only point you to a few recourses that might help:
Apple Docs
Core Data relationships (swift)
Swift - Core Data Seeding Class
Check if property is set in Core Data?
How do I access and use an entity using Core Data