I have this func:
typealias KeyValue = Dictionary<String,AnyObject>
func response( response: NSHTTPURLResponse!, data: AnyObject!, error: NSError! ) {
var resData = data as NSDictionary
if( resData["status"] as Int == 1 ){
var content = resData["content"] as Array<KeyValue>
for player in content {
let id : Int = player["id"]
let score : Int = player["score"]
let name : String = player["name"]
players[ id ]!.setName( name )
players[ id ]!.setScore( score )
}
} else {
println( resData["error"] )
}
self.playersList.reloadData()
}
and i'm getting this error:
'(String, AnyObject)' is not convertible to 'Int'
on this lines
let id : Int = player["id"]
let score : Int = player["score"]
let name : String = player["name"]
I can't figure out why
content is Array
-> player is KeyValue
-> player is Dictionary
-> player["id"] is AnyObject
so why he think that player["id"] is '(String, AnyObject)'??
Thanks
UPDATE
Change to this fix that error:
let id = player["id"]! as Int
let score = player["score"]! as Int
let name = player["name"]! as String
But now i'm getting this in run-time:
Thread 5: EXC_BREAKPOINT (code=EXC_ARM_BREAKPOINT,subcode=0xdefe)
player in your loop is an instance of Dictionary<String, AnyObject>. So values stored in that dictionary are instances of AnyObject.
In this line:
let id : Int = player["id"]
you are trying to make an implicit cast from AnyObject to Int, which is not possible. The correct way to handle that is to:
Unwrap the value extracted from the dictionary (it can be nil if the key doesn't exist)
explicitly cast to Int
So that line should be fixed as follows:
let id : Int = player["id"]! as Int
which can also be shortened as:
let id = player["id"]! as Int
Similar rules apply for score and name
Addendum - as per your last error, probably it is caused by the fact that a key is not in the dictionary. Try using optionals like this:
let id = (player["id"] as AnyObject?) as? Int
let score = (player["score"] as AnyObject?) as? Int
let name = (player["name"] as AnyObject?) as? String
You have 3 optional variables, which get nil if the corresponding expression at the right side evaluates to nil. If that works, use the debugger to check which key is not in the dictionary
Related
There is an issue that i can't understand and can not find an answer to:
i have this method in a tableViewCOntroller that is calling another viewCOntroller with TableView inside it
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowBook" {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
let books = categoryStore.allCategories[selectedIndexPath.row].books
let destinationVC = segue.destinationViewController as! BookViewController
destinationVC.books = books
}
}
}
Then in the BookViewController i have this just for testing:
var books = [Book]()
override func viewDidLoad() {
super.viewDidLoad()
print(books[0].name)
}
I can see that the books var is an array that is holding books:
class Book {
var name: String = ""
var id: Int = -1
var categoryId: Int = -1
var bookAuthor: String = ""
var level1Name: String = ""
var level2Name: String = ""
var level3Name: String = ""
var level4Name: String = ""
var levels: Int = -1
var orderKey: Int = -1
var viewCount: Int = -1
var viewLevel: Int = -1
var chapters: [AnyObject] = []
}
so i am getting an array of books with 13 key/value pairs dictionary in the books var
when i'm trying to print any thing, lets say:
print(books[0].name)
I get the error:
fatal error: NSArray element failed to match the Swift Array Element type
and i can't understand why...
p.s The transition is working and i can see the next table but then getting the fatal error
Ok, let's start with your error.
fatal error: NSArray element failed to match the Swift Array Element
type
A swift array is complaining that one of it's elements is not what it's expecting. The NSArray element does not match the swift array element.
Now we know that a NSArray can store different types of elements, for example, NSNumber, NSDictionary, NSString... you get the picture. So clearly our issue here is type related.
Now looking at your segue code we can see that we do not actually state what type books is, we let swift's type inference work it out for us. This is where our first sign of the issue occurs. You are expecting an array of [Book] but for some reason you are getting a type of NSArray.
If you make the change from:
let books = categoryStore.allCategories[selectedIndexPath.row].books
let books : [Book] = categoryStore.allCategories[selectedIndexPath.row].books
You will now crash at this line, because you are now stating the type expected.
Full Method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowBook" {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
let books : [Book] = categoryStore.allCategories[selectedIndexPath.row].books
let destinationVC = segue.destinationViewController as! BookViewController
destinationVC.books = books
}
}
}
This is backed up by your debugger console output, it says that you have an array of 13 NSDictionary types.
This means you need to investigate where you populate the [Book] array - i.e. categoryStore.allCategories
OK, This was a beginner problem that might help others.
The scenario: I have a complex JSON: root is object, for each key in the object there is a numeric value (deferent key but the value has the same structure) and the value is the Category Object!
For each category there are many key/Value pairs and one of them is the the Books KEY and the value is array or objects, each one is a book.
The problem: when i deserialised the JSON, i parsed the categories with an array of books, but the books as mentioned in the comments weren't really book instances more array of NSDictionary objects.
So after deserialising the Category i have to add an init method in The Book class to get an NSDictionary and parse it into a Book Object.
After that for each category i have to go over the the NSArray of books and for each one to create an Object go Book and return an Array of Books.
That did the trick and now every thing is working:
API:
// Getting NSArray of NSDictionary that represnets books and convert it into Books array
static func getBooksFromArr(booksArr: NSArray) -> [Book] {
var books = [Book]()
for book in booksArr {
let thisBook = Book(bookDict: book as! NSDictionary)
books.append(thisBook)
print(book)
}
return books
}
// Parsing the category object
private static func CategoryFromJSONObject(json: [String: AnyObject]) -> Category? {
guard let
id = json["Id"] as? Int,
name = json["Name"] as? String,
booksArr = json["Books"] as? NSArray else {
return nil
}
let books = getBooksFromArr(booksArr)
return Category(name: name, id: id, books: books)
}
Book Class:
init(bookDict: NSDictionary){
self.name = (bookDict["Name"] ?? "") as! String
self.id = (bookDict["Id"] ?? -1) as! Int
self.categoryId = (bookDict["CategoryId"] ?? -1) as! Int
self.bookAuthor = (bookDict["BookAuthor"] ?? "") as! String
self.level1Name = (bookDict["Level1Name"] ?? "") as! String
self.level2Name = (bookDict["Level2Name"] ?? "") as! String
self.level3Name = (bookDict["Level3Name"] ?? "") as! String
self.level4Name = (bookDict["Level4Name"] ?? "") as! String
self.levels = (bookDict["Levels"] ?? -1) as! Int
self.orderKey = (bookDict["OrderKey"] ?? -1) as! Int
self.viewCount = (bookDict["ViewCount"] ?? -1) as! Int
self.viewLevel = (bookDict["ViewLevel"] ?? -1) as! Int
self.chapters = (bookDict["Chapters"] ?? []) as! [AnyObject]
}
Now i will have to do the same for the chapters if i want to compose books to hold the chapters.
I didn't write the REST API so don't ask me why they did it as so, but now every thing is working and that is the point of it.
In my case, it was just casting to the wrong array type in the code above the problem...
I'm stuck on a weird problem.
I'm parsing a JSON response via Alamofire, and want to fetch a variable that just keeps throwing errors regardless whether I cast it to a String or Number, whilisting changing the error message every time I do :/
If I cast it to a String like this:
let kolicina = jsonCategory["kolicina"] as! String
if Int(kolicina) > 0 {
I get the error:
"Could not cast value of type '__NSCFNumber' (0x10f051368) to 'NSString' (0x10e627b48)."
When I try to cast it to a NSNumber:
let kolicina = jsonCategory["kolicina"] as! NSNumber
if kolicina.integerValue > 0 {
I get:
"Could not cast value of type 'NSTaggedPointerString' (0x10effcae8) to 'NSNumber' (0x10e5d32a0)."
This is what I get for that specific variable when I print the Dictionary:
kolicina = 0;
Can someone point me in the right direction?
It seems you have a combination of strings and integers in there. I would recommend you to fix the JSON and keep the data either as a string or a number, not both.
If you want to parse such a format, you will have to try to parse it as a NSNumber or as a NSString, e.g.:
let kolicina = jsonCategory["kolicina"]
if let kolicinaString = kolicina as? String {
...
} else if let kolicinaInt = kolicina as? Int {
...
}
with the > 0 checks you could do something like this:
if let kolicinaString = kolicina as? String,
kolicinaInt = Int(kolicinaString) where kolicinaInt > 0 {
print("String: \(kolicinaInt)")
} else if let kolicinaInt = kolicina as? Int where kolicinaInt > 0 {
print("Int: \(kolicinaInt)")
}
or using a ternary operator:
let kolicinaInt = (kolicina is String) ? Int(kolicina as! String) : kolicina as? Int
if kolicinaInt > 0 {
print("Kolicina: \(kolicinaInt)")
}
Try this .. don't make force unwrap
if let kolicina = jsonCategory["kolicina"] as? Int{
// Success: your kolicina is Int
print(kolicina)
}
else{
// Not an Int
}
please, ask me, where is my mistake? I have Xcode error:
Cannot subscript a value of type '[Int : [String]]' with an index of
type 'String!'
in let keyExists = myDict[tmp.Hour] != nil, myDict[tmp.Hour] = Int and myDict[tmp.Hour].append(tmp.Minutes) of that part of code:
func array() -> Dictionary <Int,[String]>
{
let timeInfos = getTimeForEachBusStop()
var myDict: Dictionary = [Int:[String]]()
for tmp in timeInfos {
let keyExists = myDict[tmp.Hour] != nil
if (!keyExists) {
myDict[tmp.Hour] = [Int]()
}
myDict[tmp.Hour].append(tmp.Minutes)
}
return myDict
}
I understand, that problem is in optional type, but where is problem I don't understand
upd
func getTimeForEachBusStop() -> NSMutableArray {
sharedInstance.database!.open()
let lineId = getIdRoute
let position = getSelectedBusStop.row + 1
let getTimeBusStop: FMResultSet! = sharedInstance.database!.executeQuery("SELECT one.hour, one.minute FROM shedule AS one JOIN routetobusstop AS two ON one.busStop_id = (SELECT two.busStop_id WHERE two.line_id = ? AND two.position = ?) AND one.day = 1 AND one.line_id = ? ORDER BY one.position ASC ", withArgumentsInArray: [lineId, position, lineId])
let getBusStopInfo : NSMutableArray = NSMutableArray()
while getTimeBusStop.next() {
let stopInfo: TimeInfo = TimeInfo()
stopInfo.Hour = getTimeBusStop.stringForColumnIndex(0)
stopInfo.Minutes = getTimeBusStop.stringForColumnIndex(1)
getBusStopInfo.addObject(stopInfo)
}
sharedInstance.database!.close()
return getBusStopInfo
}
You are declaring your dictionary as a dictionary with keys of type Int and values of type [String]:
var myDict: Dictionary = [Int:[String]]()
(better written as: var myDict: [Int: [String]] = [:] because by casting it to Dictionary you are removing the types).
However, in
myDict[tmp.Hour] = [Int]()
You are using a value which is of [Int] type and tmp.Hour is probably a String.
So, your problem is a type mismatch.
The error states that you cannot subscribe your [Int:[String]] dictionary with a String key.
Therefore the type of tmp.Hour is obviously String rather than the expected Int
If tmp.Hour is guaranteed to be an integer string you can convert the value
let hour = Int(tmp.Hour)!
myDict[hour] = [Int]()
On the other hand since myDict is [Int:[String]] you might mean
let hour = Int(tmp.Hour)!
myDict[hour] = [String]()
Hour and Minutes are of type string (I guess - stringForColumnIndex) so your dictionary is wrong type. Should be:
func array() -> Dictionary <String,[String]>
{
let timeInfos = getTimeForEachBusStop()
var myDict: Dictionary = [String:[String]]()
for tmp in timeInfos {
let keyExists = myDict[tmp.Hour] != nil
if (!keyExists) {
myDict[tmp.Hour] = [String]()
}
myDict[tmp.Hour].append(tmp.Minutes)
}
return myDict
}
I have a function that loops through an array that contains either Strings or Dictionaries and sets values for a new Dictionary of type [String:AnyObject], using values from the original array as keys. sections is an array of custom Section objects and its method getValue always returns a String.
func getSectionVariables() -> [String:AnyObject] {
var variables = [String:AnyObject]()
for section in sections {
if let name = section.name as? String {
variables[name] = section.getValue()
} else if let name = section.name as? [String:String] {
for (k, v) in name {
if variables[k] == nil {
variables[k] = [String:String]()
}
if var dict = variables[k] as? [String:String] {
dict[v] = section.getValue() //This works, but of course copies variables by value and doesn't set variables[k]
}
(variables[k] as? [String:String])![v] = section.getValue() //Can't get this line to compile
}
}
}
return variables
}
If I try to cast variables[k] as a [String:String], the compiler insists that String is not convertible to DictionaryIndex<String, AnyObject>. I'm not sure why downcasting isn't working and why the compiler thinks I'm trying to use the alternate dictionary subscript syntax.
Note that this project is in Swift 1.1.
The general problem is that a "cast expression" does not yield an "l-value" and therefore cannot be assigned to. For example, consider the following simplified version of the code:
var x = "initial"
var y : AnyObject? = x
(y as? String) = "change"
You will get an error on the last line, because you cannot assign to a "cast expression".
I have parsed a JSON array and extracted an NSDictionary and from this have extracted a String variable and successfully passed this via a segue to my next view. When I try to do the same with an integer the app crashes...
This is my code:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "genreSegue" {
println("genreSegue")
let indexPath = self.tableView.indexPathForSelectedRow()
println("row: \(indexPath.row)")
var bookView:BookViewController = segue.destinationViewController as BookViewController
if let genres = self.genreList as? [NSDictionary] {
var item = genres[indexPath.row]
println("segueItem: \(item)")
let title:String = item["title"] as String
let genreId:Int = item["id"] as Int // this line crashes the app
let genreId:NSNumber = item["id"] as NSNumber // this version also crashes
println("title: \(title)")
bookView.genreTitle = item["title"] as String
//bookView.genreId = item["id"] as Int // so I can't test this line
}
}
}
Here is the console output:
genreSegue
row: 1
segueItem: {
id = 2;
records = 1;
selfLink = "http://creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/id/2";
title = Biography;
}
title: Biography
Here is the error on thread 1:
libswift_stdlib_core.dylib`swift_dynamicCastObjCClassUnconditional:
Here is the original json data:
http://creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/list
Here is my app source code:
https://github.com/marktyers/ios_bookshop_client
I don't see how the string variable works but not the int. Any ideas?
The problem is that the number you are looking for is not a number in the JSON, it's a String. So try this:
let genreId = (item["id"] as String).toInt() // This casts the String to an Integer
// Note that this is not Int but Int?
Here you see thats a string: