I'm currently making a 2-player strategy board game in Swift and need to connect two iPads over local WiFi or Bluetooth. No matter what I've tried today, I can't get them to detect each other (I've tried over local WiFi and Bluetooth).
Here is my authorization code which runs in the UIViewController when my app first launches (which always returns "Self local player is authenticated." along with the ID:
private func authenticateLocalPlayer() {
var localPlayer = getLocalPlayer()
// If Apple were doing their job right, this is what the proper code should look like:
// var localPlayer = GKLocalPlayer.localPlayer()
if ( !localPlayer.authenticated ) {
localPlayer.authenticateHandler = { (viewController : UIViewController!, error : NSError!) -> Void in
NSLog("Error: \(error)")
if viewController != nil {
// Authenticated?
self.presentViewController(viewController, animated: true, completion: nil)
NSLog("viewController is not nil")
} else if (localPlayer.authenticated == true) {
NSLog("Self local player is authenticated.")
NSLog("My name is \(localPlayer.playerID)")
} else {
NSLog("Not authenticated")
NSLog("Player is \(localPlayer.playerID)")
}
}
} else {
NSLog("Player is already authenticated!")
}
}
and here is my code to detect nearby devices in a separate UIViewController:
override func viewDidLoad() {
devicesLabel.text = "Waiting for devices..."
searchForDevices()
NSLog("Ran searchForDevices()")
}
private func searchForDevices() {
GKMatchmaker.sharedMatchmaker().startBrowsingForNearbyPlayersWithHandler() {
var status = $1 ? "true" : "false"
self.devicesLabel.text = "Reachability changed for player \($0) with status: \(status)"
}
}
No matter what I do with my two iPads (both are model iPad 3), neither one ever sees the other. Am I calling startBrowsingForNearbyPlayersWithHandler correctly?
Also notice that in the authorization code above, I'm using the Objective-C workaround recommended by this post: Game Center not authenticating using Swift, since the "Swift way" of doing that didn't work for me either.
I also ran Spelltower across both devices over local WiFi, so it looks like the hardware is functioning properly. Any idea what could be going wrong here?
I decided to abandon developing this through Game Center and to use Multipeer Connectivity instead.
You are not registering a class to receive invitation updates. You need to register a class and implement methods conforming to the protocol for GKLocalPlayerListener. See my response in this post (it is in Objective-C, but the same concept applies):
Some startBrowsingForNearbyPlayersWithReachableHandler questions
Related
I am trying to check if device's charging port is working fine.
There are two scenarios -
a. when the charging cable is already plugged in.
b. when the charging cable is plugged in after sometime.
For case a, on viewDidLoad() I checked for enable batteryStateMonitoring and checked the current state of the battery. - It always works.
For case b, I tried using NSNotifications UIDeviceBatteryStateDidChangeNotification
override func viewDidLoad() {
UIDevice.current.isBatteryMonitoringEnabled = true
self.checkForCharging()
}
func checkForCharging() {
if UIDevice.current.batteryState == .full || UIDevice.current.batteryState == .charging {
self.updateUIForChargingTest()
} else {
NotificationCenter.default.addObserver(self,selector: #selector(ViewController.monitorBatteryStatus), name: NSNotification.Name.UIDeviceBatteryStateDidChange, object: nil)
}
}
func monitorBatteryStatus() {
if self.MonitorBatteryStatus(state: UIDevice.current.batteryState) == true {
self.updateUIForChargingTest()
}
else {
print("Device Battery State Unknown")
}
}
func MonitorBatteryStatus(state : UIDeviceBatteryState) -> Bool {
switch state {
case UIDeviceBatteryState.charging:
return true
case UIDeviceBatteryState.full:
return true
default:
return false
}
}
The NSNotification UIDeviceBatteryStateDidChangeNotification is not triggered everytime. It doesn't call the selector method everytime. Out of 10 times I plug-in the cable, it successfully responds only 2 times. Even when device begins charging.
Some people have marked it as duplicate. So for you kind information, this isnt an exact duplicate question because here I have applied certain approach to a problem which isnot working for me..AT ALL!! Solutions provided in the questions similar to my question are nt working for me, Since i have already tried them.
You should listen on Object device
NotificationCenter.default.addObserver(self,selector: #selector(ViewController.monitorBatteryStatus), name: NSNotification.Name.UIDeviceBatteryStateDidChange, object: UIDevice.current)
I'm using CoreMIDI to receive messages from a MIDI-Keyboard via Camera Connection Kit on iOS-Devices. My App is about pitch recognition. I want the following functionality to be automatic:
By default use the microphone (already implemented), if a MIDI-Keyboard is connected use that instead.
It's could find out how to tell if it is a USB-Keyboard using the default driver. Just ask for the device called "USB-MIDI":
private func getUSBDeviceReference() -> MIDIDeviceRef? {
for index in 0..<MIDIGetNumberOfDevices() {
let device = MIDIGetDevice(index)
var name : Unmanaged<CFString>?
MIDIObjectGetStringProperty(device, kMIDIPropertyName, &name)
if name!.takeRetainedValue() as String == "USB-MIDI" {
return device
}
}
return nil
}
But unfortunately there are USB-Keyboards that use a custom driver. How can I tell if I'm looking at one of these? Standard Bluetooth- and Network-Devices seem to be always online. Even if Wifi and Bluetooth are turned of on the device (strange?).
I ended up using the USBLocationID. It worked with any device I tested so far and none of the users complained.But I don't expect many users to use the MIDI-Features of my app.
/// Filters all `MIDIDeviceRef`'s for USB-Devices
private func getUSBDeviceReferences() -> [MIDIDeviceRef] {
var devices = [MIDIDeviceRef]()
for index in 0..<MIDIGetNumberOfDevices() {
let device = MIDIGetDevice(index)
var list: Unmanaged<CFPropertyList>?
MIDIObjectGetProperties(device, &list, true)
if let list = list {
let dict = list.takeRetainedValue() as! NSDictionary
if dict["USBLocationID"] != nil {
devices.append(device)
}
}
}
return devices
}
I am having some problems trying to implement game center achievements into an iOS game. The game already has the player authentication and leaderboards setup and they are working fine. The first error I get is when using this method:
GKAchievementDescription.loadAchievementDescriptionsWithCompletionHandler
It gives me an error which states two things:
"The requested operation could not be completed due to an error communicating with the server."
"App does not support achievements."
Five achievements have been added to iTunes connect so I don't no why it says it doesn't support them. The other issues are when I use this method:
GKAchievement.reportAchievements
When this is called the error in the completion handler is nil, but it returns "no bundle for bundleID: (null)". The achievement banner doesn't show and there is no achievements tab in the game centre view.
The app was recently transferred to another developer but he then wanted some extra features added to it, so I'm using a provisioning profile and developer certificate provided by him so I can test game center and in app purchases properly. It seems like the problem I'm having is relating to the transfer?
So my question is what could be the problem causing the game to 'not support achievements'?
Any help would be much appreciated,
thank you.
Your question is a bit vague, how are those two methods looking exactly in your code. Its hard to help with two lines of a function
You load code should look something like this
/// Load achievements
func loadAchievements(){
print("Loading submitted achievements")
GKAchievement.loadAchievementsWithCompletionHandler( { (achievements, error:NSError?) -> Void in
guard error == nil else {
print("Error loading achievements progress: \(error)")
return
}
guard let validAchievements = achievements else { return }
for achievement in validAchievements {
print("Name: \(achievement.identifier)")
print("Percentage: \(achievement.percentComplete)")
print("Date: \(achievement.lastReportedDate)")
self.achievementsDict.updateValue(achievement, forKey: achievement.identifier!)
}
})
}
You report code should look something like this
/// Save achievement progress
func reportAchievementProgress(percent: Double, achievementID: String) {
guard self.localPlayer.authenticated else { return }
let achievement = self.checkAchievement(achievementID) as GKAchievement? //
if achievement != nil {
achievement!.percentComplete = percent
achievement!.showsCompletionBanner = true
GKAchievement.reportAchievements([achievement!], withCompletionHandler: { (error:NSError?) -> Void in
guard error == nil else {
print(error)
return
}
print("Reported achievement: \(achievementID)) to: \(percent) %")
})
}
}
/// Check achievement
private func checkAchievement(achievementID: String) -> GKAchievement {
var achievement = self.achievementsDict[achievementID]
if achievement == nil {
print("Achievement with no previous progress, saving...")
achievement = GKAchievement(identifier: achievementID)
self.achievementsDict.updateValue(achievement!, forKey: achievement!.identifier!)
}
return achievement!
}
achievementsDict is a dictionary where you cache your achievements.
var achievementsDict = [String: GKAchievement]()
Does this help?
When switching to GKMatchmaker from MatchmakerViewController my two sandbox game center accounts running on separate devices (both iPhone 5s) no longer connect into the same match. Both show expectedPlayerCount still above 0.
I followed the RayWenderlich tutorial on creating a multiplayer game with game center. It is supposed to be real time and was working as such. Using a GKMatchmakerViewController it all worked completely fine (both auto-matching and invites). However, as soon as I switched one block of code it no longer works. I've been fiddling with this for awhile now and the apple docs don't seem to help me at all. Anyone know what I might need to do extra?
func findMatchWithMinPlayers(minPlayers: Int, maxPlayers: Int, viewController: UIViewController, delegate: GameKitHelperDelegate) {
if enableGameCenter == false {
return
}
matchStarted = false
gkMatch = nil
gkDelegate = delegate
viewController.dismissViewControllerAnimated(false, completion: nil)
var request = GKMatchRequest()
request.minPlayers = minPlayers
request.maxPlayers = maxPlayers
if pendingRecipients != nil {
request.recipients = pendingRecipients!
}
GKMatchmaker.sharedMatchmaker().findMatchForRequest(request, withCompletionHandler: { (match: GKMatch!, error: NSError!) -> Void in
self.gkMatch = match
self.gkMatch?.delegate = self
println(match)
if self.matchStarted == false && match.expectedPlayerCount == 0 {
self.lookupPlayers()
}
})
}
This only difference between this and what was working is that this has the GKMatchmaker instead of the GKMatchmakerViewController and Delegate.
I am trying to set up Game Center in a swift sprite kit game. The following is done in my App Delegate
func authenticateLocalPlayer(){
let localPlayer: GKLocalPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController : UIViewController!, error : NSError!) -> Void in
//handle authentication
if (viewController != nil){
self.window?.rootViewController?.presentViewController(viewController, animated: true, completion: nil)
}
else{
if (localPlayer.authenticated){
gameCenterEnabled = true
//Get the default leaderboard identifier.
localPlayer.loadDefaultLeaderboardIdentifierWithCompletionHandler({ (leaderboardIdentifierr: String!, error: NSError!) -> Void in
if (error != nil){
NSLog("%#", [error.localizedDescription])
}
else{
leaderboardIdentifier = leaderboardIdentifierr
}
})
}
else{
gameCenterEnabled = false
}
}
}
}
The problem I am having is that the localPlayer.autheniticateHandler always returns a nil viewController even though my local player is not authenticated. Please let me know what I am doing wrong and how to fix this.
I was having the same problem.
Try deleting the app from the device then go to the device's settings under game centre and enable sandbox.
Then run your game again on the device and it should display the game centre login window.
I tried running my app in simulator and the authentication window came up fine. I am not sure why it is not working on my device however.
Exactly the same happens for me. Tried on multiple devices with the same result. Deleting app/changing Game Centre setting won't help.
Your code looks fine though.
It looks like GC for Swift is totally broken..