Reputation: 1
I created a custom class called Weather and declared an array of Weather objects.
import Foundation
class Weather {
var cityName:String
var temperature:Double
var temperatureMax:Double
var temperatureMin:Double
init(cityName: String, temperature: Double, temperatureMax: Double, temperatureMin: Double) {
self.cityName = cityName
self.temperature = temperature
self.temperatureMax = temperatureMax
self.temperatureMin = temperatureMin
}
}
import UIKit
import SwiftyJSON
class ViewController: UIViewController {
@IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
var weatherArrays = [Weather]()
findLocation(zipCode: "11210", weatherArrays: weatherArrays)
print(weatherArrays[0].cityName)
}
func findLocation(zipCode: String, weatherArrays: [Weather])
{
let zip = zipCode
let appID = "245360e32e91a426865d3ab8daab5bf3"
let urlString = "http://api.openweathermap.org/data/2.5/weather?zip=\(zip)&appid=\(appID)&units=imperial"
let request = URLRequest(url: URL(string: urlString)!)
URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
do
{
let json = try JSONSerialization.jsonObject(with: data!) as! NSDictionary
let main = json["main"] as! [String:Any]
let temp = main["temp"]! as! Double
let name = json["name"]! as! String
let tempMax = main["temp_max"]! as! Double
let tempMin = main["temp_min"]! as! Double
weatherArrays.append(Weather(cityName: name, temperature: temp, temperatureMax: tempMax, temperatureMin: tempMin))
}
catch
{
print("Error")
}
}.resume()
}
}
I pass the array into a function and I append the values to the weatherArrays parameter. However, when I compile I get the error, "Cannot use mutating member on immutable value: 'weatherArrays' is a 'let' constant."
The Weather class was originally a struct but I got this same error and I read up and found that struct values cannot be edited in a function because it is pass by value. I changed the struct to a class and I am still getting this same error? Why is it saying "'weatherArrays' is a 'let' constant" when I declared weatherArrays as a var?
Upvotes: 0
Views: 298
Reputation: 10012
Here is a better Approach for your code
import UIKit
import SwiftyJSON
struct Weather {
let cityName:String
let temperature:Double
let temperatureMax:Double
let temperatureMin:Double
}
class ViewController: UIViewController {
@IBOutlet weak var myLabel: UILabel!
var array = [Weather]()
let appID = "245360e32e91a426865d3ab8daab5bf3"
override func viewDidLoad() {
super.viewDidLoad()
findLocation(zipCode: "11210"){ array in
guard let array = array else {
print("Error")
return
}
print(array.first?.cityName ?? "no city name found")
}
}
func buildUrl(queryItems: [URLQueryItem]) -> URL?{
var components = URLComponents()
components.scheme = "http"
components.host = "api.openweathermap.org"
components.path = "/data/2.5/weather"
components.queryItems = queryItems
return components.url
}
func findLocation(zipCode: String, completionHandler: @escaping (_ array: [Weather]?) -> ()){
guard let url = buildUrl(queryItems: [URLQueryItem(name: "zip", value: zipCode), URLQueryItem(name: "appID", value: appID), URLQueryItem(name: "units", value: "imperial")]) else {
print("Error in building url")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
guard let data = data else {
print(error?.localizedDescription ?? "")
completionHandler(nil)
return
}
do{
var array = [Weather]()
let json = try JSON(data: data)
if json["cod"].intValue == 200{
let main = json["main"]
let temp = main["temp"].doubleValue
let name = json["name"].stringValue
let tempMax = main["temp_max"].doubleValue
let tempMin = main["temp_min"].doubleValue
array.append(Weather(cityName: name, temperature: temp, temperatureMax: tempMax, temperatureMin: tempMin))
completionHandler(array)
}else{
completionHandler(nil)
}
} catch let error{
print(error)
}
}.resume()
}
}
Use struct for models, we don't have to write init method
Use let instead of var when you know that the data is not going to be changed
Do not use NSDictionary.
You've written print statement right after calling the function where as the array would've been filled only after the call to server is completed. so I have used completion handler
I saw you have installed SwiftyJSON but you weren't actually using the benefits of it. Look at the parsing section.
About the error you were getting is because Swift is pass by value i.e. when you are passing array object you were actually passing a copy of it not the actual array. if you want the same array to be modified, you need to us inout. A great tutorial can be found for that
Edit: As suggested by @rmaddy to make the code much safer by returning a new array. Please see his comment for more information.
Upvotes: 2
Reputation: 726
Because when you pass a parameter in the function it always passes let type. Type following code maybe it's help
import Foundation
class Weather {
var cityName:String
var temperature:Double
var temperatureMax:Double
var temperatureMin:Double
init(cityName: String, temperature: Double, temperatureMax: Double, temperatureMin: Double) {
self.cityName = cityName
self.temperature = temperature
self.temperatureMax = temperatureMax
self.temperatureMin = temperatureMin
}
}
import UIKit
import SwiftyJSON
class ViewController: UIViewController {
var weatherArrays = [Weather]()
@IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
findLocation(zipCode: "11210")
}
func findLocation(zipCode: String)
{
let zip = zipCode
let appID = "245360e32e91a426865d3ab8daab5bf3"
let urlString = "http://api.openweathermap.org/data/2.5/weather?zip=\(zip)&appid=\(appID)&units=imperial"
let request = URLRequest(url: URL(string: urlString)!)
URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
do
{
let json = try JSONSerialization.jsonObject(with: data!) as! NSDictionary
let main = json["main"] as! [String:Any]
let temp = main["temp"]! as! Double
let name = json["name"]! as! String
let tempMax = main["temp_max"]! as! Double
let tempMin = main["temp_min"]! as! Double
self.weatherArrays.append(Weather(cityName: name, temperature: temp, temperatureMax: tempMax, temperatureMin: tempMin))
}
catch
{
print("Error")
}
}.resume()
}
}
Upvotes: 0
Reputation: 936
First of all as rmaddy said in comment you should not do this.But if you are still interested to know how to make passed parameter mutable, below is an example.
// function should accept parameter as inout
func test(arr: inout [String]){
arr.append("item3")
print(arr) // ["item1", "item2", "item3"]
}
// you should pass mutable array
var item = ["item1", "item2"]
test(arr: &item)
Note from Swift doc
Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
For more info refer to this link. https://docs.swift.org/swift-book/LanguageGuide/Functions.html
Upvotes: 0