Send two Arrays of Strings and Ints as matchData - ios

I am working on a turn based game with Game Center. I want to send an Array of Strings and an Array of Ints as matchData. I know how to create both, but I only know how to send one of them...
This is how i create the String Array:
var strings = [String]()
let data = NSKeyedArchiver.archivedDataWithRootObject(strings)
This is how i create the Int Array:
var array : [Int] = []
let data = NSData(bytes: array, length: array.count * sizeof(Int))
This is how I send the data i create
currentMatch?.endTurnWithNextParticipants([nextParticipant], turnTimeout: 20, matchData: data, completionHandler: { (error) in
if error != nil {
print(error)
} else {
//Data sent
}
}
})

The easiest way is probably to wrap both in a dictionary and then serialize the dictionary:
let data = NSKeyedArchiver.archivedDataWithRootObject([
"strings":strings,
"numbers":array
])
Then to recover the original data, you can use:
guard let recovered = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? [String:AnyObject],
let strings = recovered["strings"] as? [String],
let array = recovered["numbers"] as? [Int] else {
// recovery failed... deal with it
}

Get your matchData using GKTurnBasedMatch.loadMatchDataWithCompletionHandler: and then use that match data if it exists in the completion block.

Related

iOS Swift iterate over an Array of Dictionary Items from Firestore

I am trying to iterate over the following dictionary:
Dictionary in Firebase
This is my code:
Global.sharedInstance.db.collection("usuarios").getDocuments { (snapshot, error) in
if error != nil {
print("error de lectura usuarios...")
} else {
if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
let txtIdentificador = data["identificador"] as? String ?? ""
let txtBio = data["bio"] as? String ?? ""
let txtNombre = data["nombre_usuario"] as? String ?? ""
let txtFotoPerfil = data["foto_perfil"] as? String ?? ""
var arrFotos = data["fotos"] as? [String: [String:String]]
}
}
}
}
I am able to retrieve the first few lines, like the id, the biography, name, etc.
But when I try to access the array of dictionary I have no idea.
This is the main idea:
I have a set of users, which I iterate over with the first loop 'for document in documents...", then each user has a set of photos. I want to iterate over the 3 photos, and in each iteration I want to retrieve the fields, so I can create a object called Image and associate the user with the 'hasUpload(Image)'.
I would like to know how to iterate over X photos an in each iteration retrieve the fields.
Something like this:
var arrFotos = data["fotos"] as? [String: [String:String]]
for foto in arrFotos {
for (key,value) in foto {
}
}
I get the error: For-in loop requires '[String : [String : String]]?' to conform to 'Sequence'; did you mean to unwrap optional?
A similar StackOverflow case can be found here and this is how they resolved it:
You can either do this, where x is the index and token is the element:
for (x, token) in transcriptCandidate.enumerated() {
}
Or this if you don't need the index:
for token in transcriptCandidate {
}

CoreData - check if object exists in database

I have viewController with table view and FetchResultsController. ViewController downloads data from web and then save it to CoreData. Downloading happens every launch. I need someway to compare info that I downloaded from web and only the save it to CoreData
How can I do this ?
I had an idea of fetching all objects by fetchedResultsController.performFetch() and then assigning them to array, but I dont understand how to iterate over that array (it us [AnyObject])
Maybe there are more easy ways ?
I figured that out
I need to perform several steps in order to make comparison of content from core with array of custom objects
1) create empty array
var arrayOfReposOnDisk : [RepoObject] = []
2) fetch objects from CoreData
let fetchedData = self.fetchedResultsController.fetchedObjects
3) iterate over fetchedData and convert each value for key-value to my custom object
for i in 0 ..< fetchedData!.count {
let object = fetchedData![i]
let name = object.valueForKey("name") as! String
let description = object.valueForKey("description") as! String
let authorName = object.valueForKey("avatarURL") as! String
let avatarURL = object.valueForKey("authorName") as! String
let forks = object.valueForKey("forks") as! Int
let watches = object.valueForKey("watches") as! Int
let repoObject = RepoObject(name: name, description: description, authorName: authorName, avatarURL: avatarURL, forks: forks, watches: watches)
arrayOfItemsOnDisk.append(repoObject)
}
4) finally, make a comparison
if arrayOfReposOnDisk.contains ({ $0.name == name }) {
print("There is the same name in json as in CoreData, doing nothing")
} else {
self.insertNewObject(name, descriptionText: description, avatarURL: avatarURL, authorName: authorName, forks: forks, watches: watches)
}
You can used fetchedObjects on the fetched results controller. It is an array of [AnyObject]?, but you can cast it to your object type with something like
if let myObjects = fetchedResultsController.fetchedObjects as? [MyObjectType] {
for object in myObjects {
// do some comparison to see if the object is in your downloaded data
}
}
Another way to get the objects is to create a NSFetchRequest and use executeFetchRequest on your managed object context.

Swift--Reading in JSON file

Using Xcode 6.4 and programming in swift.
I am typing a program that should read in JSON from a URL. A sample of the JSON can be found at this URL (https://itunes.apple.com/us/rss/topmovies/limit=2/json) and Ive been using this site to parse the JSON in order to read it better (http://json.parser.online.fr/).
Now I need to work through the levels of the JSON in order to get to
the actual movie names and image URL's but I am lost at what kind of variable entryDictionary should be. I was thinking it should be an array of dictionaries, and this compiles, but the output of entryDictionary in the console is sloppy looking, starting with Optionl( and not entry{ as it should. And when I go to loop through entryDictionary, I get an error saying entry does not have a subscript of type AnyObject.
So I am asking how I retrieve the im:name fields and im:image from the JSON.
func downloadDataFromURLString(urlString: String) {
//Downloaded data and is now stored in data. Took code out because
//irrelevant to my problem at this point. Data variable has correct
//JSON, now I am trying to parse it.
} else { //download suceeded, time to parse
var error: NSError? = nil
var names = [String]()
if let rootDictionary = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? [String: AnyObject] {
let feedDictionary = rootDictionary["feed"] as! [String: AnyObject]
let entryDictionary: AnyObject? = feedDictionary["entry"]
println(entryDictionary) //For debugging
//for entry in entryDictionary as! NSArray {
// let name = entryDictionary["name"]
// let image = entryDictionary["image"]
// let movie = Movie(name: name!, image: image!)
// weakSelf!.movies.append(movie)
//}
here is a blueprint of the JSON
"feed":{
"author":{},
"entry":[
{
"im:name":{
"label":"Deadpool"
},
"im:image":[],
"summary":{},
"im:price":{},
"im:contentType":{},
"rights":{},
"title":{},
"link":[],
"id":{},
"im:artist":{},
"category":{},
"im:releaseDate":{}
AnyObject is indeed not subscriptable (you're trying to subscript a variable whose type is AnyObject? with ["feed"]). You should also avoid casting to Cocoa container types like NSArray and NSDictionary whenever you can. Here's an example of how you might get the labels out of the entries array's names array:
import Foundation
func labels(feedDictionary:[String:AnyObject]) -> [String] {
guard let entries = feedDictionary["entry"] as? [String:AnyObject] else {
return []
}
return entries.flatMap { (key:String, value:AnyObject) -> String? in
guard key == "im:name" else {
return nil
}
guard let name = value as? [String:String] else {
return nil
}
return name["label"]
}
}
I'd however advise against using NSJSONSerialization on its own in Swift for anything but the simplest case, as you end up casting and wrapping optionals until the cows come home.
There are good 3rd party libraries such as Freddy and SwiftyJSON which apply Swift language features to accomplish a very convenient JSON (de)serialization experience.
For instance with Freddy you could express your problem in the following style:
let json = try JSON(data: data)
json.decode("feed", type:Feed.self)
struct Feed: JSONDecodable {
let entries:[Entry]
init(json: JSON) throws {
self.entries = try json.arrayOf("entry", type:Entry.self)
}
}
struct Entry:JSONDecodable {
let name:IMName
init(json: JSON) throws {
self.name = try json.decode("im:name", type:IMName.self)
}
}
struct IMName:JSONDecodable {
let label:String
init(json: JSON) throws {
self.label = try json.string("label")
}
}

Extract value from dictionary of annoying format

I apologise for the title of this question. I have no idea what else to call it.
So... When calling the following:
let testData: [NSObject : AnyObject] = getTestData()
print(testData)
I get this output:
[data: {"TypeId":7,"DataList":null,"TypeName":"This is a test"}]
How would I be able to access the value 7 for the key "TypeId"?
EDIT:
Please note that it's holding { } brackets, not only [ ], thus a cast to NSDictionary is not possible as far as I have tried.
Kind regards,
Anders
You can achieve plist-like nested structures using Any type for dictionary values which is Swift's somewhat counterpart to Objective-C's id type but can also hold value types.
var response = Dictionary()
response["user"] = ["Login": "Power Ranger", "Password": "Mighty Morfin'"]
response["status"] = 200
Any seems to be better than AnyObject because in the above code response["status"] is of type Swift.Int, while using value type of AnyObject it is __NSCFNumber.
The way most people do it is to parse annoying JSON data as custom objects. That should be done as soon as you get the JSON. Ideally, data as JSON should not be used outside your communication code, example:
First, let's define a class to hold your server data:
class MyServerObject {
let typeId: Int
let typeName: String
let dataList: [AnyObject]?
init(dictionary: Dictionary<String, AnyObject>) {
let dataDictionary = dictionary["data"] as! Dictionary<String, AnyObject>
self.typeId = dataDictionary["TypeId"] as! Int
self.typeName = dataDictionary["TypeName"] as! String
self.dataList = dataDictionary["DataList"] as? [AnyObject]
}
}
Note that init method is already parsing the JSON. This doesn't have to be done in init, you could also create a static parse method that will return a new instance.
Usage:
// demo data
let jsonString = "{\"data\": {\"TypeId\":7,\"DataList\":null,\"TypeName\":\"This is a test\"}}"
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
let json = try! NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
// parsing
let myServerObject = MyServerObject(dictionary: json as! Dictionary<String, AnyObject>)
// now we can simply read data as properties
print(myServerObject.typeId)
print(myServerObject.typeName)
One of the good thing about this solution is that we can check the JSON format and all the properties are parsed with the correct types.
Parsing can be hierarchical, for example, if your dataList contains complex objects, let's call them DataListItem, your parsing method can parse each item separately and put them into a [DataListItem], e.g.
if let dataListJSON = dataDictionary["DataList"] as? [Dictionary<String, AnyObject>] {
self.dataList = dataListJSON.map({ DataListItem($0) })
}
Also note that when parsing as! will crash the app when the format is invalid. as? will return nil if the types don't match. as? is very useful for types that can be nil because they are parsed as NSNull instances.
taking in account your data ...
print(testData)
/*
[data: {
DataList = null;
TypeId = 7;
TypeName = "This is a test";
}]
*/
// DataList type should be declared somewhere
class DataList {}
// parse data or set default value, if 'key' doesn't exist
if let data = testData["data"] as? [String:AnyObject] {
let dataList = data["DataList"] as? DataList // nil
let typeId = data["TypeId"] as? Int ?? 0 // 7
let typeName = data["TypeName"] as? String ?? "" // This is test
}

Issue displaying array of image from url

Through Firebase I'm calling many url to convert it to UIImage but aren't displayed in a corrected order. The print func stamp to the consolle the current index:
0 2 1 3 4
The JSON is like:
{
"evento_1" = "http://.altervista.org/evento1.jpeg";
"evento_2" = "http://.altervista.org/evento2.jpeg";
"evento_3" = "http://.altervista.org/evento3.jpeg";
"evento_4" = "http://.altervista.org/evento4.jpeg";
"evento_5" = "http://.altervista.org/evento5.jpeg";
}
Function to get the data:
ref.observeEventType(.Value, withBlock: { snapshot in
let nsDictionary = snapshot.value as? NSDictionary
for (key, value) in nsDictionary! {
dict[key as! String] = value
}
var index = 0
for (_, url ) in dict {
self.loadImage(url as! String, i: index)
index++
}
}, withCancelBlock: { error in
print(error.description)
})
Image Loader func:
func loadImage(urlString:String,i:Int)
{
let url = NSURL(string: urlString)
let data = NSData(contentsOfURL: url!)
self.eventi[i].image = UIImage(data: data!)
}
Put your Firebase snapshot keys into an array and the key:value pairs into a dictionary. Then sort the key array:
arrayOfKeys = Array(dict.keys) //swift
arrayOfKeys.sort {
return $0 < $1
}
then you can iterate over the array to get the event name (the key), which corresponds to the objects in your dictionary (access it by key) which returns it's value.
or (and I like this better)
Just take every .value dictionary and put each into an array then sort the array by the eventName (assuming that's the key)
eventArray.sort({ $0.eventName > $1.eventName })
As Jay and Eric mention: dictionaries have no defined order. The FDataSnapshot does have a defined order, but once you convert it to a dictionary, you're throwing that away.
So instead of first converting to a dictionary and then looping, you should simply loop over the children of the snapshot with:
for child in snapshot.children {
let key = child.key as String
print(key);
}
Where I print the key, you can do whatever you need with the child snapshot.

Resources