I am working on an example project (Written in an earlier version of Swift) in my efforts to learn Swift 2, and have run into a problem
I am getting a compile error with this -
class func loadMembersFromFile(path:String) -> [Member]
{
var members:[Member] = []
var error:NSError? = nil
if let data = NSData(contentsOfFile: path, options:[]),
json = NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary,
team = json["team"] as? [NSDictionary] {
for memberDictionary in team {
let member = Member(dictionary: memberDictionary)
members.append(member)
}
}
return members
}
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
My Swift programming experience is quite limited, so I have not been able to find a way to correct these errors. Any suggestions would be appreciated. Thanks.
Initializer for conditional binding must have Optional type, not
'NSData'
Means that you don't need conditional binding when result is not optional. But this is not true issue here. Because NSData initialiser can throw an error (which is stated in second error) and you may convert it to optional. Here is how your code will look:
class func loadMembersFromFile(path:String) -> [Member]
{
var members:[Member] = []
var error:NSError? = nil
if let data = try? NSData(contentsOfFile: path, options:[]),
json = (try? NSJSONSerialization.JSONObjectWithData(data, options: [])) as? NSDictionary,
team = json["team"] as? [NSDictionary] {
for memberDictionary in team {
let member = Member(dictionary: memberDictionary)
members.append(member)
}
}
return members
}
Related
I have a JSON file and I'm trying to access the array in it.
The JSON file looks like:
{
"cars": [{
"name": "BMW",
"icons": [["front.png", "back.png", "B3"],
["front_red", "back_red", "C4"]
]
}]
}
//cars is an array of dictionaries, I just mentioned one in the snippet.
I get the JSON data as:
func loadJSONData(){
if let path = Bundle.main.path(forResource: "testJSON", ofType: "json")
{
if let jsonData = NSData(contentsOfFile : path)
{
do {
if let jsonResult = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String:Any]
{
self.testJSONData = (jsonResult["cars"] as? Array)!
//self.testJSONData = (jsonResult["cars"] as? Array<Dictionary<String, Any>>)! //also tried this
}
}
catch let error as NSError {
print(error.localizedDescription)
}
}
}
}
testJSONData is declared as an array:
var testJSONData = [] as [Dictionary<String, Any>]
and the error occurs when trying to get the "icons" array from the JSON.
let namePredicate = NSPredicate(format: "name like BMW")
let filteredArray :Array = testJSONData.filter() { namePredicate.evaluate(with: $0) }
let carData: Dictionary = filteredArray[0] as Dictionary<String, Any>
let carIcons: Array = carData["icons"] as! Array //error at this line
Cannot convert value of type 'Array<_>' to specified type 'Array'
Can someone please show me where I'm doing wrong ? Thanks!
Array is a generic type in Swift, so when you want to declare an array variable, you always need to specific what type of elements the Array is holding. There's no such type as Array without specifying its Element type.
Also, there's no need for type annotations in Swift, the compiler can infer the types for you and you are explicitly telling the compiler the type by casting anyways.
carIcons should be of type Array<Array<String>> or as a shorthand [[String]]
let carIcons = carData["icons"] as! [[String]]
Some general comments about your code: don't use old Foundation types, such as NSData in Swift when they have native Swift equivalents. Also don't do force unwrapping of safe casted types, that makes no sense. Either handle the casting and unwrapping safely or simply force cast if you know the cast will succeed for sure. .mutableContainers have no effect in Swift, so don't use it. There's no need to cast error to NSError in a catch block, Swift has its own Error type.
func loadJSONData(){
if let fileURL = Bundle.main.url(forResource: "testJSON", withExtension: "json") {
do {
let jsonData = try Data(contentsOfFile: fileURL)
if let jsonResult = try JSONSerialization.jsonObject(with: jsonData) as? [String:Any], let cars = jsonResult["cars"] as? [[String:Any]] {
self.testJSONData = cars
} else {
print("Unexpected JSON format")
}
}
catch {
print(error)
}
}
}
However, if you are using Swift 4, the best solution would be using the type safe Codable protocol and JSONDecoder instead of JSONSerialization.
I got this Errors after I converting to swift 2.3.
guard let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary else {
throw JSONError.ConversionFailed
}
guard
let loadedWeather = json["weather"]![0]["description"] as? String,
let loadedTemperatur = json["main"]!["temp"] as? Float,
let loadedWindSpeed = json["wind"]!["speed"] as? Float
else {
print("Weather JSON-Parsing failed")
return
}
The Ambiguous use of subscript error comes by declaring "loadedWeather, loadedTemperatur and loadedWindSpeed".
Already tried to change NSDictionary to Dictionary and other things, helped on another position in code, but here....
thanks guys
This happens because compiler doesn't know what are the intermediary object is in each of your line ... so may be
if let weather = json["weather"] as? [[String:String]], firstObject = weather.first as? [String:String]{
let loadedWeather = firstObject["description"]
}
// same for other objects i.e. `json["main"]` and `json["wind"]` with its return type
I think that the issue is that the compiler cannot work out what json["weather"] is, You may need to be more specific in your code.
Try
let loadedWeather = (json["weather"] as! [[String:AnyObject]])[0]["description"] as? String
I have converted to Swift 3 and I have received the following errors when assigning to AnyObject the JSONSerialization.jsonObject. Has anyone come across this issue and know the fix?
Since the last Swift 3 update most of the return types changed from AnyObject to Any and downcast is not allowed, so in such situation you are forced to use explicit cast. That means you should make a couple of guard statements or use optional chaining if let defining each necessary field. Consider using map, filter, reduce if possible to make your code more elegant. Example:
guard way:
guard let object = try JSONSerialization.jsonObject(with: data) as? [[String: Any]] else { return nil }
guard let field1 = object[0]["field1_token"] as? [Any] else { return nil }
//do your thing
if let way:
if let object = try JSONSerialization.jsonObject(with: data) as? [[String: Any]],
let field1 = object[0]["field1_token"] as? [Any] {
//do your thing
}
You may want to check Apple's article Working with JSON in Swift
Also you can use some of the json parsing/mapping libriaries like these:
SwiftyJSON
Gloss
Please replace let object : AnyObject with let object : Any.
Error showing because of wrong casting.
I'm hoping someone an help me figure out a problem that has me scratching my brain! When I attempt this function using a NSData(contentsOfUrl... structure, this all works fine. However, I am attempting to use a NSURLSession for use on an Apple Watch app, and keep hitting an error;
...
class func fetchData() -> [Complication] {
var task: NSURLSessionDataTask?
let myURL = "http://www.myurl.com/sample.json"
let dataURL = NSURL(string: myURL)
let conf = NSURLSessionConfiguration.defaultSessionConfiguration()
conf.requestCachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let session = NSURLSession(configuration: conf)
task = session.dataTaskWithURL(dataURL!) { (data, res, error) -> Void in
if let e = error {
print("dataTaskWithURL fail: \(e.debugDescription)")
return
}
var dataSet = [Complication]()
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
for item in json {
let name: String? = item["name"] as? String
let percent: Int? = item["percent"] as? Int
let timeFromNow: Int? = item["timeFromNow"] as? Int
let myData = Complication(
name: name!,
percent: percent!,
timeFromNow: timeFromNow!
)
dataSet.append(myData)
}
} catch {
print(error)
}
}
return dataSet
//THIS LINE THROWS THE ERROR
}
...
When attempting to return my dataSet array, I receive the error Instance member 'dataSet' cannot be used on type 'Complication'. As mentioned, however, this does seem to work if I were to use a NSData(contentsOfUrl... instead of a NSURLSession, which is where I am stuck!
The data task is a closure that is executed asynchronously. Its return statements returns from the closure, not from the outer function.
Since the closure is executed asynchronously it makes no sense to return data from it: the return type is Void.
You should organize your code differently, e.g. using a completion handler.
Hint: search for "swift return closure" in SO. You will find plenty of questions similar to yours and a number of good answers and suggestions.
I've started a Swift 2 project and I'm attempting to wrap my head around do, guard and throws. There are several other questions on Stack concerning this, but the issue I'm having is slightly different.
Here is my code:
enum JSONParsingError: String, ErrorType {
case URLCreationFailed = "Error: URL creation failed"
case SerializationFailed = "Error: JSON Parsing failed"
case DataDownloadingFailed = "Error: downloading data failed"
case DictionaryError = "Error: dictionary creation from JSON failed."
}
func fetchUserRepositories(urlString: String) throws {
do {
guard let reposURL = NSURL(string: urlString) else { throw JSONParsingError.URLCreationFailed }
guard let jsonData = NSData(contentsOfURL: reposURL) else { throw JSONParsingError.DataDownloadingFailed }
guard let jsonDictionary: NSDictionary = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .MutableContainers) as? NSDictionary else { throw JSONParsingError.SerializationFailed }
guard let reposArray = jsonDictionary["repos"] as? NSDictionary else { throw JSONParsingError.DictionaryError }
for repo in reposArray {
repositories.append(TestRepo(json: repo))
}
}
}
Regardless of how I cast jsonDictionary["repos"] I keep getting the same error in my for loop:
Cannot convert value of type 'Element' (aka '(key: AnyObject, value: AnyObject)') to expected argument type 'NSDictionary' (TestRepo is just a simple class that is initialized with a dictionary. Not the most ideal way, I know).
What am I missing?
reposArray is a dictionary, that is a collection of couples (key, value). In your code:
for repo in reposArray {
repositories.append(TestRepo(json: repo))
}
repo is bound to each element in turn. So, if TestRepo expects as parameter a dictionary, it receives instead an element (a couple (key, value)), and this is the cause of the error.