The errors are:
Initializer for conditional binding must have Optional type, not 'NSData'
and
Call can throw, but it is not marked with 'try' and the error is not handled
class func loadMembersFromFile(path:String) -> [Member] //Function
{
var members:[Member] = []
var error:NSError? = nil
if let data = NSData(contentsOfFile: path, options:[]), //data
json = NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary, //my array (json)
team = json["team"] as? [NSDictionary] { // display json
for memberDictionary in team { //cylce for
let member = Member(dictionary: memberDictionary)
members.append(member)
}
}
return members
}
First, you need to use the do catch syntax for methods that can throw exceptions. Secondly, the NSData initializer doesn't produce an Optional value so you can't put it in an if statement.
class func loadMembersFromFile(path:String) -> [Member] //Function
{
var members:[Member] = []
var error:NSError? = nil
do {
let data = try NSData(contentsOfFile: path, options:[])
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary,
let team = json["team"] as? [NSDictionary] {
for memberDictionary in team { //cylce for
let member = Member(dictionary: memberDictionary)
members.append(member)
}
}
} catch {
//handle exceptions
}
return members
}
See documentation.
Related
Im updating my app to swift 3
I am getting a couple of errors
for (k, v): (AnyObject, AnyObject) in value {
Gets an NSDictionary.Iterator.Element is not convertable to (Anyobject, Anyobject)
Subsiquently Im getting this error
var artworks = [Artwork]()
func loadInitialData() {
// 1
let fileName = Bundle.main.path(forResource: "PublicArt", ofType: "json");
let data: Data = try! Data(contentsOf: URL(fileURLWithPath: fileName!), options: NSData.ReadingOptions(rawValue: 0))
// 2
var error: NSError?
let jsonObject: AnyObject!
do {
jsonObject = try JSONSerialization.jsonObject(with: data,
options: JSONSerialization.ReadingOptions(rawValue: 0))
} catch let error1 as NSError {
error = error1
jsonObject = nil
}
// 3
if let jsonObject = jsonObject as? [String: AnyObject], error == nil,
// 4
let jsonData = JSONValue.fromObject(jsonObject)?["data"]?.array {
for artworkJSON in jsonData {
if let artworkJSON = artworkJSON.array,
// 5
let artwork = Artwork.fromJSON(artworkJSON) {
artworks.append(artwork)
}
}
}
}
JsonObject produces 'Any' not the expected contextual result type
'AnyObject'
and
Argument type [String : AnyObject] does not conform to expected type
'AnyObject'
Im assuming this is an easy one but I havent coded in a year and would be very appreciative of the help
Thanks
Travis
UPDATE
So I just updated the code
but getting an error in the JSON.swift file
static func fromObject(_ object: AnyObject) -> JSONValue? {
switch object {
case let value as NSString:
return JSONValue.jsonString(value as String)
case let value as NSNumber:
return JSONValue.jsonNumber(value)
case _ as NSNull:
return JSONValue.jsonNull
case let value as NSDictionary:
var jsonObject: [String:JSONValue] = [:]
for (k, v): (AnyObject, AnyObject) in value {
if let k = k as? NSString {
if let v = JSONValue.fromObject(v) {
jsonObject[k as String] = v
} else {
return nil
}
}
}
return JSONValue.jsonObject(jsonObject)
case let value as NSArray:
var jsonArray: [JSONValue] = []
for v in value {
if let v = JSONValue.fromObject(v as AnyObject) {
jsonArray.append(v)
} else {
return nil
}
}
return JSONValue.jsonArray(jsonArray)
default:
return nil
}
}
}
error is:
nsdictionary.iterate.element '(aka (key: Any, value: Any)') is not
convertible to 'AnyObject, AnyObject)'
for code line
for (k, v): (AnyObject, AnyObject) in value {
Sorry for the late reply
Regards
Travis
You are using too much AnyObject aka it's-an-object-but-I-don't-know-the-type.
Since the JSON file is in your bundle you know exactly the types of all objects.
In Swift 3 a JSON dictionary is [String:Any] and a JSON array is [[String:Any]].
However I don't know the exact structure of the JSON and I don't know what JSONValue does – a third party library is actually not necessary – but this might be a starting point to get the array for key data.
func loadInitialData() {
let fileURL = Bundle.main.url(forResource: "PublicArt", withExtension: "json")!
do {
let data = try Data(contentsOf: fileURL, options: [])
let jsonObject = try JSONSerialization.jsonObject(with: data) as! [String: Any]
let jsonData = jsonObject["data"] as! [[String:Any]]
for artworkJSON in jsonData {
print(artworkJSON)
// ... create Artwork items
}
} catch {
print(error)
fatalError("This should never happen")
}
}
I am trying to check the version of my app with the iTunes lookup api. I have problems in parsing the response. Please find the code
static func needsUpdate() -> Bool
{
do {
let infoDictionary = Bundle.main.infoDictionary
let appID = infoDictionary?["CFBundleIdentifier"]
let url:URL = URL(string: "http://itunes.apple.com/lookup?bundleId=\(appID!)")!
let data = try Data(contentsOf: url)
let lookup = try JSONSerialization.jsonObject(with:data, options: []) as! [String:AnyObject]
print(lookup)
let resultCount:Int = lookup["resultCount"] as! Int
if (resultCount == 1)
{
var results = lookup["results"] as! [String:AnyObject] // ***Error***
if results.isEmpty
{
print(results)
}
}
} catch
{
}
return true
}
Please let me know how can i parse this response
The error message clearly reveals that the value for results is an array.
let results = lookup["results"] as! [[String:Any]]
And consider that a JSON dictionary is [String:Any] in Swift 3
The following code throws a message which says "Initializer for conditional binding must have Optional type, not 'AnyObject'"
func parseData2(){
var data:NSData?
if let data2 = data {
do {
let details = try NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments)
if let actualDetails = details where actualDetails.isKindOfClass(NSDictionary) {
print("Parse Data")
}
}catch {
print("Error \(error)")
}
}
}
To resolve the above error I used the following code.
func parseData2(){
var data:NSData?
if let data2 = data {
do {
let details:AnyObject = try NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments)
if let actualDetails:AnyObject = details where actualDetails.isKindOfClass(NSDictionary) {
print("Parse Data")
}
}catch {
print("Error \(error)")
}
}
}
Is there any better approach then the above or my code might crash?
There is one more code which I want to add considering nil check,type check and then type cast check. The reason behind that Swift offers great flexibility but litle bit difficult to fix issues. Let's say I have a dictionary, cityDetails and I am trying to get data for self.cityZipCode and self.cityIdentifier, which are optional, defined as var cityZipCode:Int? and var cityIdentifier:Int?
if let cityBasic = cityDetails["basicDetails"] where
cityBasic!.isKindOfClass(NSDictionary) {
self.cityZipCode = (cityBasic as! NSDictionary)["zip"].integerValue ?? 0
self.cityIdentifier = (cityBasic as! NSDictionary)["cityId"].integerValue ?? 0
}
No need to unwrap the result from try. It is not an optional. You do need to cast the result from try to an NSDictionary. Use as? to downcast it.
Best practice: full access to returned error for good error handling
func parseData2(){
var data:NSData?
if let data2 = data {
do {
let details = try NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments)
if let detailsDict = details as? NSDictionary {
print("Parse Data")
} else if let detailsArray = details as? NSArray {
print("array")
}
} catch {
print("Error \(error)")
}
}
}
Quick and dirty: error handling is not for me!
func parseData2(){
var data:NSData?
if let data2 = data {
let details = try? NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments)
if let detailsDict = details as? NSDictionary {
print("Parse Data")
} else {
print("details might be nil, or not an NSDictionary")
}
}
}
Bad Ass Mode: crashes are features
func parseData2(){
var data:NSData?
if let data2 = data {
let details = try! NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments) as! NSDictionary
}
}
Some extra info on multiple unwraps :
Drop the code below in a playground.
struct SomeStruct {
var anOptional : Int?
init() {
}
}
func unwrapWithIfLet() {
if let unWrappedStruct = myStruct, let unWrappedSomething = unWrappedStruct.anOptional {
print("multiple optional bindings succeeded")
// both unWrappedStruct and unWrappedSomething are available here
} else {
print("something is nil")
}
}
func unwrapWithGuard() {
guard let unWrappedStruct = myStruct, let unWrappedSomething = unWrappedStruct.anOptional else {
print("something is nil")
return
}
print("multiple optional bindings succeeded")
// both unWrappedStruct and unWrappedSomething are available here
}
var myStruct : SomeStruct?
//unwrapWithGuard()
//unwrapWithIfLet()
myStruct = SomeStruct()
myStruct!.anOptional = 1
unwrapWithGuard()
unwrapWithIfLet()
You are looking for as?, which attempts to convert the thing on the left to the type on the right, and returns nil if the conversion is not possible:
let details = try NSJSONSerialization.JSONObjectWithData(data2, options: .AllowFragments)
if let actualDetails = details as? NSDictionary {
print("Parse Data")
}
You rarely need to use isKindOfClass in Swift. If you find yourself using it, ask why, and consider whether as or as? will work instead.
arowmy init works fine in Swift < 2 but in Swift 2 I get a error message from Xcode Call can throw, but it is not marked with 'try' and the error is not handled at let anyObj = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]. I think in my case I can´t use a try catch block because super is not initialized at this time. "Try" need a function that throws.
here is my function:
required init(coder aDecoder : NSCoder)
{
self.name = String(stringInterpolationSegment: aDecoder.decodeObjectForKey("name") as! String!)
self.number = Int(aDecoder.decodeIntegerForKey("number"))
self.img = String(stringInterpolationSegment: aDecoder.decodeObjectForKey("image") as! String!)
self.fieldproperties = []
var tmpArray = [String]()
tmpArray = aDecoder.decodeObjectForKey("properties") as! [String]
let c : Int = tmpArray.count
for var i = 0; i < c; i++
{
let data : NSData = tmpArray[i].dataUsingEncoding(NSUTF8StringEncoding)!
// Xcode(7) give me error: 'CAll can thorw, but it is not marked with 'try' and the error is not handled'
let anyObj = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
let label = anyObj["label"] as AnyObject! as! String
let value = anyObj["value"] as AnyObject! as! Int
let uprate = anyObj["uprate"] as AnyObject! as! Int
let sufix = anyObj["sufix"] as AnyObject! as! String
let props = Fieldpropertie(label: label, value: value, uprate: uprate, sufix: sufix)
self.fieldproperties.append(props)
}
}
Xcode mean that:
let anyObj = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! [String:AnyObject]
but I have no idea to do here the right think according to this document https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html
The jsonObject can throw errors, so put it within do block, use try, and catch any errors thrown. In Swift 3:
do {
let anyObj = try JSONSerialization.jsonObject(with: data) as! [String: Any]
let label = anyObj["label"] as! String
let value = anyObj["value"] as! Int
let uprate = anyObj["uprate"] as! Int
let sufix = anyObj["sufix"] as! String
let props = Fieldpropertie(label: label, value: value, uprate: uprate, sufix: sufix)
// etc.
} catch {
print("json error: \(error.localizedDescription)")
}
Or, in Swift 4, you can simplify your code by making your struct conform to Codable:
struct Fieldpropertie: Codable {
let label: String
let value: Int
let uprate: Int
let suffix: String
}
Then
do {
let props = try JSONDecoder().decode(Fieldpropertie.self, from: data)
// use props here; no manual parsing the properties is needed
} catch {
print("json error: \(error.localizedDescription)")
}
For Swift 2, see previous revision of this answer.
JSONSerialization.JSONObject throws ErrorType and not NSError.
so the correct catch is
do {
let anyObj = try JSONSerialization.JSONObject(with: data, options: []) as! [String:AnyObject]
// use anyObj here
} catch let error {
print("json error: \(error)")
}
The type of error in catch let error is ErrorType
Don't know if it'll solve your problem, but isn't the method JSONObjectWithData:options:error:? I think you're missing the error parameter.
I am writing a class that returns JSON data. I create an array of the type NSArray. To this Array I add arrays of the type String.
I later want to append strings to the array of type String that is within the array of type NSArray.
However it does not allow this, I have no idea why because the append function does work when appending NSArrays to the main array.
This is the entire class:
import Foundation
class JSON {
struct Static {
let hostname = "http://192.168.2.115:8888/tcn/";
func getJSON(script:String, jsonMainElement:String, elements:[String]) -> [NSArray] {
var returnObject = [NSArray]()
let urlAsString = hostname + script;
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
println(error.localizedDescription);
}
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if (err != nil) {
println("JSON Error \(err!.localizedDescription)");
}
var i = 0;
let arrayFromJson: [[String:String]] = jsonResult[jsonMainElement] as [[String:String]]
dispatch_async(dispatch_get_main_queue(), {
for item in arrayFromJson {
returnObject.append([String]());
for jsonObject in elements {
returnObject[i].append(item[jsonObject]!);
// ^ THIS LINE GIVES ERROR
}
i++;
}
if (arrayFromJson.count > 0) {
//k
}
})
})
jsonQuery.resume()
return returnObject;
}
}
}
You may need to define the array as containing a mutable array as below.
var returnObject = [NSMutableArray]()
You could also use a Swift 'sub' array, and define it more like this.
var returnObject = Array<Array<String>>()
Don't forget to update your return on the method.
func getJSON() -> Array<Array<String>>