Im following the Ray Wenderlich tutorial and converted most of the code to Swift. Im on part two but this function never gets called. I followed the tutoral but im not sure where to call it. I dont think its a delegate function also so I know that im supposed to call it somewhere but not sure where. Has anyone ever tried this tutorial and got it to work on Swift? Thanks!
Heres the link: http://www.raywenderlich.com/60998/game-center-tutorial-how-to-make-a-simple-multiplayer-game-with-sprite-kit-part-2
func match(theMatch: GKMatch, didReceiveData data: NSData, fromPlayer playerID: String) {
let message = UnsafePointer<Message>(data.bytes).memory
if(message.messageType == MessageType.kMessageTypeRandomNumber) {
let messageRandomNumber = UnsafePointer<MessageRandomNumber>(data.bytes).memory
println("Received random number: \(messageRandomNumber.randomNumber)")
var tie = false
if(messageRandomNumber.randomNumber == _ourRandomNumber) {
println("Tie")
tie = true
_ourRandomNumber = arc4random()
self.sendRandomNumber()
}
else {
var dictionary = ["\(playerIDKey)":"\(playerID)", "\(randomNumberKey)":"\(messageRandomNumber.randomNumber)"]
self.processReceivedRandomNumber(dictionary)
}
if(_receivedAllRandomNumbers) {
_isPlayer1 = self.isLocalPlayerPlayer1()
}
if(!tie && _receivedAllRandomNumbers) {
if(_gameState == GameState.kGameStateWaitingForRandomNumber) {
_gameState = GameState.kGameStateWaitingForStart
}
self.tryStartGame()
}
}
else if(message.messageType == MessageType.kMessageTypeGameBegin) {
println("Begin game message received")
_gameState = GameState.kGameStateActive
self.delegate?.setCurrentPlayerIndex(self.indexForLocalPlayer())
}
else if(message.messageType == MessageType.kMessageTypeMove) {
println("Move message received")
let messageMove = UnsafePointer<MessageMove>(data.bytes).memory
self.delegate?.movePlayerAtIndex(self.indexForPlayerWithId(playerIDKey))
}
else if(message.messageType == MessageType.kMessageTypeGameOver) {
println("Game over message received")
let messageGameOver = UnsafePointer<MessageGameOver>(data.bytes).memory
self.delegate?.gameOver(messageGameOver.player1Won)
}
}
The delegate method match:didReceiveData:fromPlayer: has been deprecated. Use match:didReceiveData:fromRemotePlayer: instead.
Make sure you set the delegate, doing something like:
func matchmakerViewController(viewController: GKMatchmakerViewController, didFindMatch match: GKMatch) {
theMatch = match
match.delegate = self
...
}
That is the viewController:didFindMatch delegate for GKMatchmakerViewControllerDelegate.
You need to delegate from both GKMatchmakerViewControllerDelegate and GKMatchDelegate.
Yeah, I know this is an old thread, but the OP hasn't had any success yet.
match:didReceiveData:fromPlayer: is a GKMatchDelegate optional method which will trigger when the match received data sent from the player.
So please don't try to call it directly. It will be called by GameKit when data is received from a player.
#protocol GKMatchDelegate <NSObject>
#optional
// The match received data sent from the player.
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromRemotePlayer:(GKPlayer *)player NS_AVAILABLE(10_10, 8_0);
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID NS_DEPRECATED(10_8, 10_10, 4_1, 8_0, "use match:didReceiveData:fromRemotePlayer:");
#end;
This also is declared in GameKitHelper.h class as a GameKitHelperDelegate
#protocol GameKitHelperDelegate
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data
fromPlayer:(NSString *)playerID;
#end
This delegate method is calling from the below method when the match received data sent from the player
#pragma mark GKMatchDelegate
// The match received data sent from the player.
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
if (_match != match) return;
[_delegate match:match didReceiveData:data fromPlayer:playerID];
}
Related
I'm new to swift coding, so most of my code is "this is how I found it on the internet" status.
I'm trying to send a HTTP GET request and then work with it. I'm using NSURLConnection for this. I have 2 swift files in my Xcode project (it is a swift console project, not playground), one is main one and 2nd contains my class I would like to use for delegation:
import Foundation
class Remote: NSObject, NSURLConnectionDelegate {
var data = NSMutableData()
func connect() {
var url = NSURL(string: "https://www.google.com")
var request = NSURLRequest(URL: url!)
NSLog("Is%# main thread", NSThread.isMainThread() ? "" : " NOT");
var conn = NSURLConnection(request: request, delegate: self, startImmediately: false)
conn?.scheduleInRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode)
conn?.start()
sleep(2)
}
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
println("didReceiveResponse")
}
func connection(connection: NSURLConnection!, didReceiveData conData: NSData!) {
println("didReceiveData")
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
println("DidFinishLoading")
}
deinit {
println("deiniting")
}
}
Here is my main swift code:
import Foundation
var xxx = Remote()
xxx.connect()
sleep(10)
sleep(2)
When I set breakpoints on each println in delegate functions, they are never hit. Execution is done from main thread according to NSLog output.
In debugger I can see that data are sent and received over the network, but I never get any output. I've seen many similar questions here but nothing helped me.
What am I doing wrong?
As Martin R suggested I added correct NSRunLoop into my code right after starting NSURLConnection:
self.shouldKeepRunning = true
let theRL = NSRunLoop.currentRunLoop()
while self.shouldKeepRunning && theRL.runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeInterval: 1.0, sinceDate: NSDate())) { }
shouldKeepRunning is bool variable defined in my class and it set to false in delegate function connectionDidFinishLoading, so
I have a function, shown below, that I would like to continuously update. It is taking data from a webpage, and every so often that webpage is updated to reflect current information. Is there a way that I can catch this update and reflect that in my application? I'm pretty new to Swift and iOS programming. Some of the code made seem very bizarre, but it currently works for whatever song is playing when you first open the app (that is, it updates the text to show that song playing but doesn't update later).
let url = NSURL(string: "http://api.vicradio.org/songs/current")!
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) in
if error != nil {
return
}
let name = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
var songName = ""
var artistName = "by "
var quoteNumber = 0
for character in name.characters {
if character == "\"" {
quoteNumber++
}
if quoteNumber == 3 && character != "\"" {
songName += String(character)
} else if quoteNumber == 7 && character != "\"" {
artistName += String(character)
}
}
if (songName != "no song metadata provided") {
self.SongNowText.text = songName
self.ArtistNowText.text = artistName
self.SongNowText.setNeedsDisplay()
self.ArtistNowText.setNeedsDisplay()
} else if (songName == "no song metadata provided") {
self.SongNowText.text = "The Best of What's Next!"
self.ArtistNowText.text = "only on VIC Radio"
}
}
task!.resume()
It looks like the URL you're accessing there is an API endpoint putting out JSON. I highly recommend using NSJSONSerialization.JSONObjectWithData to parse the response body into a dictionary and use that instead of rolling your own solution by counting quote marks.
The callback to dataTaskWithURL is executed on a background thread. Avoid updating the UI on anything besides the main thread because it can cause problems. Use dispatch_async to execute your UI update function on the main thread as in the example.
All you can do with this API is send it requests and read the responses. You can poll the endpoint at a regular interval while the app is open and get decent results from that. NSTimer is one way to do that, and it requires you put the method you want to execute repeatedly in a class inheriting from NSObject because it depends on Objective-C style message sending.
Throw this in a playground and try it:
import Cocoa
import XCPlayground
XCPSetExecutionShouldContinueIndefinitely()
class RadioDataAccessor : NSObject {
private let callback: [String : AnyObject] -> Void
init(callback: [String : AnyObject] -> Void) {
self.callback = callback
super.init()
NSTimer.scheduledTimerWithTimeInterval(5.0, target: self,
selector: "updateData", userInfo: nil, repeats: true)
// just so it happens quickly the first time
updateData()
}
func updateData() {
let session = NSURLSession.sharedSession()
let url = NSURL(string: "http://api.vicradio.org/songs/current")!
session.dataTaskWithURL(url) { data, response, error in
if error != nil {
return
}
var jsonError = NSErrorPointer()
let json = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.allZeros,
error: jsonError) as? [String : AnyObject]
if jsonError != nil {
return
}
dispatch_async(dispatch_get_main_queue()) { self.callback(json!) }
}.resume()
}
}
RadioDataAccessor() { data in
println(data)
}
You may want to save the timer to a variable and expose a function that lets you invalidate it.
So I have a entity, which I am treating as a simple Database Table in iOS8 XCODE and SWIFT. I want to delete every entry in the table. Permanently. So when I start up the app again they do not reload.This is my code.
func deleteAllItems(){
println("All Items are being DELETED")
var count:Int = 0
while (HBCContactList.count > 0){
let AppDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let MOContext: NSManagedObjectContext = AppDel.managedObjectContext!
MOContext.deleteObject(HBCContactList[0] as NSManagedObject)
HBCContactList.removeAtIndex(0)
var error:NSError? = nil
if !MOContext.save(&error){
abort()
}
tableView.reloadData()
}
It looks like it loads. When its finished on the screen there is nothing in UITableView and all is good. However If I write code to return me the entity via a fetch statement and do a count on the number of records. It still says there is over 150 results.
Thoughts? Am I even in the right ball park?
One solution is to fetch the objects and delete them.
Here is an example (make sure you specify your own entity) :
// If you'll be using the Managed Object Contexte often,
// you might want to make it a lazy property :
lazy var managedObjectContext : NSManagedObjectContext? = {
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
if let managedObjectContext = appDelegate.managedObjectContext {
return managedObjectContext
}
else {
return nil
}
}()
func deleteData() {
let context = self.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName: "yourEntity")
fetchRequest.includesPropertyValues = false // Only fetch the managedObjectID (not the full object structure)
if let fetchResults = context.executeFetchRequest(fetchRequest, error: nil) as? [yourEntity] {
for result in fetchResults {
context.deleteObject(result)
}
}
var err: NSError?
if !context.save(&err) {
println("deleteData - Error : \(err!.localizedDescription)")
abort()
} else {
println("deleteData - Success")
}
}
Core data newbie here. I'm trying to change the default NSManagedObjectContext of an NSPersistentDocument, in order to initialise and use it with NSMainQueueConcurrencyType.
Currently I'm doing it in -windowControllerDidLoadNib: like this:
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
NSManagedObjectContext *newMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[newMOC setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]];
[self setManagedObjectContext:newMOC];
}
This seemingly works fine. But I'm wondering if initialisation of the MOC in -windowControllerDidLoadNib: is the best thing to do, or whether it should be placed somewhere else and/or initialised in a different way.
Thanks for any help.
I'm experimenting with the Xcode template for a document-based CoreData app. The template creates an init() override which just calls super.init(). I want to run a large import in the background, so I added this to the document class:
class Document: NSPersistentDocument {
private var importQueue = DispatchQueue(label: "Importer")
override init() {
super.init()
let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
moc.mergePolicy = self.managedObjectContext!.mergePolicy
moc.persistentStoreCoordinator = self.managedObjectContext!.persistentStoreCoordinator
self.managedObjectContext = moc
}
func importStuff(url: URL) {
let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
moc.parent = self.managedObjectContext
var count = 0
moc.performAndWait {
...
count += 1
if count % 10000 == 0 {
do {
try moc.save()
moc.reset()
}
catch {
Swift.print("save failed at record #\(count): \(error.localizedDescription)")
}
}
return true
}
do {
try moc.save()
}
catch {
Swift.print("save failed at records #\(count): \(error.localizedDescription)")
}
}
Swift.print("imported \(count) records.")
}
#IBAction func import(_ sender: Any) {
...
importQueue.async {
self.importStuff(url: url)
}
}
}
This seems to work OK in my initial tests. I think initializing a new MOC in -windowControllerDidLoadNib: is OK, but if you have object controllers bound to the document MOC, they might perform a second fetch when the MOC is changed. Initializing it in the init will initialize it sooner, before the UI is loaded.
The preferred pattern is the "pass-the-baton" approach where you pass the managed object context down to the child view controllers. Give your controllers a managed object context attribute and simply pass it on when you present them.
I want to customize text for the same information but when I am sharing it on Facebook I don't want to use the twitter hash tags or #username scheme...
How can I diversify text for sharing based on which sharing service would be used?
Ofcourse I'm using UIActivityViewController:
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:#[shareText, shareURL] applicationActivities:nil];
I took this answer and made a simple class for it. The default message will be seen by sharing outlets other than Twitter, and for Twitter words within the hashWords array will appear with hashes if they are present in the default message. I thought I would share it for anyone else who needs it. Thanks Christopher!
Usage:
TwitterHashActivityItemProvider *twit = [[TwitterHashActivityItemProvider alloc] initWithDefaultText:#"I really like stackoverflow and code"
hashWords:#[#"stackoverflow", #"code"]];
NSArray *items = #[twit];
UIActivityViewController *act = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
Header:
#interface TwitterHashActivityItemProvider : UIActivityItemProvider
- (id)initWithDefaultText:(NSString*)text hashWords:(NSArray*)hashItems;
#property (nonatomic,strong) NSArray *hashItems;
#end
Implementation:
#import "TwitterHashActivityItemProvider.h"
#implementation TwitterHashActivityItemProvider
- (id)initWithDefaultText:(NSString*)text hashWords:(NSArray*)hashItems;
{
self = [super initWithPlaceholderItem:text];
if ( self )
{
self.hashItems = hashItems;
}
return self;
}
- (id)item
{
if ( [self.placeholderItem isKindOfClass:[NSString class]] )
{
NSString *outputString = [self.placeholderItem copy];
// twitter gets some hash tags!
if ( self.activityType == UIActivityTypePostToTwitter )
{
// go through each potential hash item and augment the main string
for ( NSString *hashItem in self.hashItems)
{
NSString *hashed = [#"#" stringByAppendingString:hashItem];
outputString = [outputString stringByReplacingOccurrencesOfString:hashItem withString:hashed];
}
}
return outputString;
}
// else we didn't actually provide a string...oops...just return the placeholder
return self.placeholderItem;
}
#end
Instead of passing the text strings into the initWithActivityItems call, pass in your own sub-class of the UIActivityItemProvider class and when you implement the itemForActivityType method it will provide the sharing service as the 'activityType' parameter.
You can then return the customized content from this method.
Swift implementation example of an UIActivityItemProvider subclass. Copy option will use only the password, other activity types will use the full share text. Should be easy to customize for different use cases. Credit to Cristopher & NickNack for their answers.
class PasswordShareItemsProvider: UIActivityItemProvider {
private let password: String
private var shareText: String {
return "This is my password: " + password
}
init(password: String) {
self.password = password
// the type of the placeholder item is used to
// display correct activity types by UIActivityControler
super.init(placeholderItem: password)
}
override var item: Any {
get {
guard let activityType = activityType else {
return shareText
}
// return desired item depending on activityType
switch activityType {
case .copyToPasteboard: return password
default: return shareText
}
}
}
}
Usage:
let itemProvider = PasswordShareItemsProvider(password: password)
let activityViewController = UIActivityViewController(activityItems: [itemProvider], applicationActivities: nil)