Swift : Delegate is nil



我尝试建立一个委托设计模式。我在WeatherManager中有一个简单的delegate,但它总是nil。我尝试在WeatherViewControlleroverride func viewDidLoad()中添加weatherManager.delegate = self。然而,我有另一个协议和委托,它工作得很好。

我正在使用API从城市名称中获取地理坐标,并且创建并执行了API的URL以获取有关天气的信息。

WeatherManager

import Foundation
// delegate design parttern
protocol WeatherManagerDelegate:AnyObject {
func didUpdateWeather(inputWeatherModel: WeatherModel)
}
class WeatherManager {

// delegate
weak var delegate: WeatherManagerDelegate?

// fetchCoordinate
func fetchWeather(urlString: String) {
performRequest(inputURLString: urlString)
}

// performRequest
func performRequest(inputURLString: String) {
// 1. Create URL
if let url = URL(string: inputURLString) {
// 2. Create URLSession
let session = URLSession(configuration: .default)
// 3. Give the URLSession a task
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
if let safeData = data {
if let weather = self.parseJSON(JSONobject: safeData) {
// ---------------- problem here  -------------------
self.delegate?.didUpdateWeather(inputWeatherModel: weather)
}
}
}
// 4. Start the task
task.resume()
}
}

// parse JSON object
func parseJSON(JSONobject: Data) -> WeatherModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(WeatherData.self, from: JSONobject)

let id = decodedData.weather[0].id
let temp = decodedData.main.temp
let name = decodedData.name

let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
return weather

} catch {
print(error)
return nil
}
}
}

WeatherModel

import Foundation
struct WeatherModel {
let conditionId: Int
let cityName: String
let temperature: Double
var temperatureString: String {
return String(format: "%.1f", temperature)
}

var conditionName: String {
switch conditionId {
case 200...232:
return "cloud.bolt"
case 300...321:
return "cloud.drizzle"
case 500...531:
return "cloud.rain"
case 600...622:
return "cloud.snow"
case 701...781:
return "cloud.fog"
case 800:
return "sun.max"
case 801...804:
return "cloud.bolt"
default:
return "cloud"
}
}
}

WeatherViewController

import UIKit
class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {

@IBOutlet weak var conditionImageView: UIImageView!
@IBOutlet weak var temperatureLabel: UILabel!
@IBOutlet weak var searchTextField: UITextField!
@IBOutlet weak var cityLabel: UILabel!

let weatherManager = WeatherManager()
var coordinateManager = CoordinateManager()

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
searchTextField.delegate = self
searchTextField.keyboardType = .asciiCapable
weatherManager.delegate = self
}
@IBAction func searchPressed(_ sender: UIButton) {
searchTextField.endEditing(true)
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchTextField.endEditing(true)
return true
}

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
textField.placeholder = "Type Something"
return false
}
}

func textFieldDidEndEditing(_ textField: UITextField) {

if let city = searchTextField.text {
coordinateManager.fetchCoordinate(cityName: city)
}
searchTextField.text = ""
}

func didUpdateWeather(inputWeatherModel: WeatherModel) {
print(inputWeatherModel.temperature)
}
}

CoordinateManager

import Foundation
struct CoordinateManager {
// WeatherManager
let weathetManager = WeatherManager()
// geographical coordinates(lat, lon) 地理座標
let coordinateURL = "https://api.openweathermap.org/geo/1.0/direct?limit=1&appid=c8e60c6317c653a1789294c00f54ae19"

// fetchCoordinate
func fetchCoordinate(cityName: String) {
let urlString = "(coordinateURL)&q=(cityName)"
performRequest(inputURLString: urlString)
}

// transformURLString
func transformURLString(_ string: String) -> URLComponents? {
guard let urlPath = string.components(separatedBy: "?").first else {
return nil
}
var components = URLComponents(string: urlPath)
if let queryString = string.components(separatedBy: "?").last {
components?.queryItems = []
let queryItems = queryString.components(separatedBy: "&")
for queryItem in queryItems {
guard let itemName = queryItem.components(separatedBy: "=").first,
let itemValue = queryItem.components(separatedBy: "=").last else {
continue
}
components?.queryItems?.append(URLQueryItem(name: itemName, value: itemValue))
}
}
return components!
}

// performRequest
func performRequest(inputURLString: String) {
// 1. Create URL
let components = transformURLString(inputURLString)
if let url = components?.url {
// 2. Create URLSession
let session = URLSession(configuration: .default)
// 3. Give the URLSession a task
let task = session.dataTask(with: url) {(data, response, error) in
if error != nil {
print(error!)
return
}
if let safeData = data {
self.parseJSON(JSONobject: safeData)
}
}
// 4. Start the task
task.resume()
}
}
// parseJSON
func parseJSON(JSONobject: Data) {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode([CoordinateData].self, from: JSONobject)
let name = decodedData[0].name
let lat = decodedData[0].lat
let lon = decodedData[0].lon

let coordinateModel = CoordinateModel(name: name, lat: lat, lon: lon)
let weatherURL = coordinateModel.urlString
// ------- WeatherManager fetchWeather ---------
weathetManager.fetchWeather(urlString: weatherURL)
} catch {
print(error)
}
}
}

CoordinateModel

import Foundation
struct CoordinateModel {
let name: String
let lat: Double
let lon: Double

// wheather 地理座標を元に検索した天気の情報
let weatherstr = "https://api.openweathermap.org/data/2.5/weather?"
let str = "units=metric&appid=c8e60c6317c653a1789294c00f54ae19#"
// computedproperty
var urlString: String {
return "(weatherstr)&lat=(lat)&lon=(lon)&(str)"
}
}

您在WeatherViewController中创建了一个weatherManager,并且在viewDidLoad方法中您将WeatherViewController设置为您在这里创建的weatherManager实例的委托。在CoordinateManager类中,你创建了一个新的weatherManager,但没有为它设置委托。

两种解决方案,使用最方便的:

  1. 为CoordinateManager创建一个初始化器,向它传递您已创建并正在收听
  2. 的WeatherManager。
// CoordinateManager class
let weatherManager: WeatherManager
init(weatherManager: WeatherManager) {
self.weatherManager = weatherManager
}
// WeatherViewController class
let weatherManager = WeatherManager()
var coordinateManager = CoordinateManager(weatherManager: weatherManager)
  1. 您可以直接将WeatherViewController设置为所需weatherManager实例的委托。
// WeatherViewController class
override func viewDidLoad() {
super.viewDidLoad()
searchTextField.delegate = self
searchTextField.keyboardType = .asciiCapable
// Instead weatherManager.delegate = self
coordinateManager.weatherManager.delegate = self
}

最新更新