How do I pass Json Data from one class to another? - ios

Can someone tell what could I do?
I have a class named RequestManager where i have the URLSession and inside of it, a constant where I store the Data from the server.
import Foundation
class RequestManager {
func fetchWeather(cityName: String,completionHandler: #escaping ([String]) -> Void){
let weatherURL = "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&appid=55dbb1b10e9f55181ad910227f1460ae&units=metric"
let objectURL = URL(string: weatherURL)
let task = URLSession.shared.dataTask(with: objectURL!, completionHandler: {(data,reponse,error)in
if error != nil{
print(error!)
} else{
do{
let json = try JSONDecoder().decode(CityWeatherInfo.self, from: data!)
print(json)
let saveData: WeatherModel = WeatherModel(name: json.name,
temp: json.main.temp, feelsLike: json.main.feelsLike,
tempMin: json.main.tempMin, tempMax: json.main.tempMax,
main: json.weathers[0].main, description: json.weathers[0].main)
print(saveData)
} catch{
print("error")
}
}
})
task.resume()
}
}
I run the app and get this in console when I ask for the server data:
CityWeatherInfo(name: "Paris", main: Evaluación_Autofin_México.Main(temp: 20.3, feelsLike: 18.41,
tempMin: 18.0, tempMax: 21.67), weathers: [Evaluación_Autofin_México.Weather(main: "Clouds",
description: "broken clouds")])
WeatherModel(name: "Paris", temp: 20.3, feelsLike: 18.41,
tempMin: 18.0, tempMax: 21.67, main: "Clouds", description: "Clouds")
I need to paste this info into a "WeatherViewDetailController" labels, but I can't enter to the "let saveData" from RequestManager.
class WeatherDetailViewController: UIViewController {
var weatherPresenter: WeatherDetailPresenter?
public var cityName: String?
let request = RequestManager()
#IBOutlet weak var closeButton: UIButton!
#IBOutlet weak var cityImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var mainLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var tempLabel: UILabel!
#IBOutlet weak var feelsLikeLabel: UILabel!
#IBOutlet weak var tempMinLabel: UILabel!
#IBOutlet weak var tempMaxLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
weatherPresenter = WeatherDetailPresenter(view: self)
weatherPresenter?.retriveWeatherByCity(cityName: self.cityName ?? "")
self.view.backgroundColor = UIColor(red: 56/255.0, green: 110/255.0, blue: 185/255.0, alpha: 1.0)
self.closeButton.tintColor = UIColor(red: 0/255.0, green: 20/255.0, blue: 20/255.0, alpha: 1)
}
#IBAction func closeAction(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
}
extension WeatherDetailViewController: WeatherDetailView {
func showWeatherSuccessful() {
print("Success")
}
func showWeatherFailure() {
print("Failure")
}
}

Update your fetch data with completion
func fetchWeather(cityName: String,completionHandler: #escaping (WeatherModel?) -> Void){
let weatherURL = "https://api.openweathermap.org/data/2.5/weather?q=\(cityName)&appid=55dbb1b10e9f55181ad910227f1460ae&units=metric"
let objectURL = URL(string: weatherURL)
let task = URLSession.shared.dataTask(with: objectURL!, completionHandler: {(data,reponse,error)in
if error != nil{
print(error!)
} else{
do{
let json = try JSONDecoder().decode(CityWeatherInfo.self, from: data!)
print(json)
let saveData: WeatherModel = WeatherModel(name: json.name,
temp: json.main.temp, feelsLike: json.main.feelsLike,
tempMin: json.main.tempMin, tempMax: json.main.tempMax,
main: json.weathers[0].main, description: json.weathers[0].main)
print(saveData)
completionHandler(saveData)
} catch{
completionHandler(nil)
print("error")
}
}
})
task.resume()
}
How to use
class WeatherDetailViewController: UIViewController {
var weatherPresenter: WeatherDetailPresenter?
public var cityName: String?
let request = RequestManager()
override func viewDidLoad() {
super.viewDidLoad()
request.fetchWeather(cityName: "", completionHandler: { [weak self] weatherModel in
// here you get object and you can set labels
self?.tempMinLabel.text = weatherModel.tempMin
self?. tempMaxLabel.text = weatherModel.tempMax
//....
})
}
}

Related

Swift I can't move data with delegate

I'm trying to make a quiz application using the API, I can import the
data into the quizManager file, but I can't transfer the data to the
viewController, When I try to print in quizManger, I can print, but in viewController does not.
how do I move data to viewController?
QuizManager
import Foundation
protocol quizManagerDelegate {
func didUpdateQuiz(_ Quizmanager: QuizManager ,quiz: QuizModel)
}
struct QuizManager {
var delegate: quizManagerDelegate?
func performRequest(){
let urlString = "https://opentdb.com/api.php?amount=1&type=multiple"
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
}
if let safeData = data{
if let quiz = self.parseJSON(quizdata: safeData){
delegate?.didUpdateQuiz(self, quiz: quiz)
}
}
}
task.resume()
}
}
func handle(data: Data?, response: URLResponse?, error: Error?) -> Void {
}
func parseJSON(quizdata: Data) -> QuizModel? {
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(Welcome.self, from: quizdata)
let correct = decodedData.results?[0].correct_answer ?? "error"
let quest = decodedData.results?[0].question ?? "error"
let incorrect = decodedData.results?[0].incorrect_answers ?? ["error"]
let question = QuizModel(correctAnswer: correct, question: quest, falseAnswer: incorrect)
// print(question.correctAnswer)
// print(question.question)
// print(question.falseAnswer)
return question
} catch {
print(error)
return nil
}
}
}
QuizData
import Foundation
// MARK: - Welcome
struct Welcome: Codable {
let results: [Result]?
}
// MARK: - Result
struct Result: Codable {
let category: String?
let question, correct_answer: String?
let incorrect_answers: [String]?
}
QuizModel
import Foundation
struct QuizModel {
let correctAnswer : String
let question : String
let falseAnswer : [String]
}
ViewController
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var ChoiceButton4: UIButton!
#IBOutlet weak var ChoiceButton3: UIButton!
#IBOutlet weak var ChoiceButton2: UIButton!
#IBOutlet weak var ChoiceButton1: UIButton!
#IBOutlet weak var QuestionTextView: UITextView!
var quizMangager = QuizManager()
override func viewDidLoad() {
super.viewDidLoad()
QuestionTextView.layer.cornerRadius = 15
quizMangager.performRequest()
self.quizMangager.delegate = self
}
}
extension ViewController : quizManagerDelegate{
func didUpdateQuiz(_ Quizmanager: QuizManager, quiz: QuizModel) {
DispatchQueue.main.async {
print("***")
print(quiz.correctAnswer)
}
}
}
The problem is in viewDidLoad that lines
quizMangager.performRequest()
self.quizMangager.delegate = self
You call performRequest before to delegate it so when performRequest is called var delegate: quizManagerDelegate? is nil . Just call delegate before call function
override func viewDidLoad() {
super.viewDidLoad()
...
quizMangager.delegate = self
quizMangager.performRequest()
...
}
You have a slight mistake in your code. You just need to confirm the delegate first and then call its function .

How to send data in extension to Button function

I'm trying to make a quiz application using the API, I can take api data and show on app but I cannot transfer data from extension in viewController to button pressed.right now, when click the button, I want the clicked option to be green or red, how can I do it?
QuizManager
import Foundation
protocol quizManagerDelegate {
func didUpdateQuiz(_ Quizmanager: QuizManager ,quiz: QuizModel)
}
struct QuizManager {
var delegate: quizManagerDelegate?
func performRequest(){
let urlString = "https://opentdb.com/api.php?amount=1&type=multiple"
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
}
if let safeData = data{
if let quiz = self.parseJSON(quizdata: safeData){
delegate?.didUpdateQuiz(self, quiz: quiz)
}
}
}
task.resume()
}
}
func handle(data: Data?, response: URLResponse?, error: Error?) -> Void {
}
func parseJSON(quizdata: Data) -> QuizModel? {
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(Welcome.self, from: quizdata)
let correct = decodedData.results?[0].correct_answer ?? "error"
let quest = decodedData.results?[0].question ?? "error"
let incorrect = decodedData.results?[0].incorrect_answers ?? ["error"]
let question = QuizModel(correctAnswer: correct, question: quest, falseAnswer: incorrect)
return question
} catch {
print(error)
return nil
}
}
}
QuizData
import Foundation
// MARK: - Welcome
struct Welcome: Codable {
let results: [Result]?
}
// MARK: - Result
struct Result: Codable {
let category: String?
let question, correct_answer: String?
let incorrect_answers: [String]?
}
QuizModel
import Foundation
struct QuizModel {
let correctAnswer : String
let question : String
let falseAnswer : [String]
}
ViewController
import UIKit
import GameKit
class ViewController: UIViewController {
#IBOutlet weak var ChoiceButton4: UIButton!
#IBOutlet weak var ChoiceButton3: UIButton!
#IBOutlet weak var ChoiceButton2: UIButton!
#IBOutlet weak var ChoiceButton1: UIButton!
#IBOutlet weak var QuestionTextView: UITextView!
var quizMangager = QuizManager()
#IBAction func OptionsButtonPressed(_ sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
QuestionTextView.layer.cornerRadius = 15
quizMangager.delegate = self
quizMangager.performRequest()
}
}
extension ViewController : quizManagerDelegate{
func didUpdateQuiz(_ Quizmanager: QuizManager, quiz: QuizModel) {
DispatchQueue.main.async { [self] in
self.QuestionTextView.text = quiz.question
var allOptions = []
allOptions.append(quiz.falseAnswer[0])
allOptions.append(quiz.falseAnswer[1])
allOptions.append(quiz.falseAnswer[2])
allOptions.append(quiz.correctAnswer)
let generatedValue = Array(allOptions.shuffled().prefix(4))
print(generatedValue)
print(quiz.correctAnswer)
ChoiceButton1.setTitle(generatedValue[0] as? String, for: .normal)
ChoiceButton2.setTitle(generatedValue[1] as? String, for: .normal)
ChoiceButton3.setTitle(generatedValue[2] as? String, for: .normal)
ChoiceButton4.setTitle(generatedValue[3] as? String, for: .normal)
}
}
}
ViewController
import UIKit
import GameKit
class ViewController: UIViewController {
#IBOutlet weak var ChoiceButton4: UIButton!
#IBOutlet weak var ChoiceButton3: UIButton!
#IBOutlet weak var ChoiceButton2: UIButton!
#IBOutlet weak var ChoiceButton1: UIButton!
#IBOutlet weak var QuestionTextView: UITextView!
var quizMangager = QuizManager()
#IBAction func OptionsButtonPressed(_ sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
QuestionTextView.layer.cornerRadius = 15
quizMangager.delegate = self
quizMangager.performRequest()
}
}
extension ViewController : quizManagerDelegate{
func didUpdateQuiz(_ Quizmanager: QuizManager, quiz: QuizModel) {
DispatchQueue.main.async { [self] in
self.QuestionTextView.text = quiz.question
var allOptions = []
allOptions.append(quiz.falseAnswer[0])
allOptions.append(quiz.falseAnswer[1])
allOptions.append(quiz.falseAnswer[2])
allOptions.append(quiz.correctAnswer)
let generatedValue = Array(allOptions.shuffled().prefix(4))
print(generatedValue)
print(quiz.correctAnswer)
ChoiceButton1.setTitle(generatedValue[0] as? String, for: .normal)
ChoiceButton2.setTitle(generatedValue[1] as? String, for: .normal)
ChoiceButton3.setTitle(generatedValue[2] as? String, for: .normal)
ChoiceButton4.setTitle(generatedValue[3] as? String, for: .normal)
}
}
}
You need to "hold onto the quiz data."
So, add a var property to your view controller:
var quizMangager = QuizManager()
// add this
var theQuiz: QuizModel?
then, in your didUpdateQuiz func, set that property:
func didUpdateQuiz(_ Quizmanager: QuizManager, quiz: QuizModel) {
DispatchQueue.main.async { [self] in
// add this
self.theQuiz = quiz
self.QuestionTextView.text = quiz.question
// ... no other changes
Now, connect all your buttons to #IBAction func OptionsButtonPressed and try this:
#IBAction func OptionsButtonPressed(_ sender: UIButton) {
guard let thisQuiz = theQuiz,
let btnTitle = sender.currentTitle
else { return }
if btnTitle == thisQuiz.correctAnswer {
sender.setTitleColor(.systemGreen, for: [])
} else {
sender.setTitleColor(.systemRed, for: [])
}
}

API Call Data Issues with Passing to New View Controller - Swift

I am building a swift app and hit an issue where the data I get back from an API call is not being passed to the view controller. I have uilabels all over the page and they do not update after the query is made like I intended. I am trying to present the aviation weather data from the user on the view controller ReportViewController.
I can get the data to fill labels on the WxViewController, but don't understand why I can't mirror the same code to have it display on the ReportViewController instead. I have tried copy/paste and removing the same code from the WxViewController as well as having it in both places, but the only place that updates the label is WxViewController (testTextLabel). Any help getting out of this endless cycle would be appreciated!
WxViewController
//
// WxViewController.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/21/20.
//
import UIKit
class WxViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
var weatherManager = WeatherManager()
#IBOutlet weak var testTextField: UILabel!
#IBOutlet weak var stationSearch: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
stationSearch.delegate = self
}
#IBAction func searchPressed(_ sender: Any) {
print(stationSearch.text!)
stationSearch.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
print(stationSearch.text!)
stationSearch.endEditing(true)
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let station = stationSearch.text {
weatherManager.fetchWeather(stationICAO: station)
}
stationSearch.text = ""
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if stationSearch.text != "" {
return true
} else {
stationSearch.placeholder = "Type an ICAO"
return false
}
}
func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) {
DispatchQueue.main.async {
self.testTextField.text = weather.flightConditions
}
print(weather.flightConditions)
}
func didFailWithError(error: Error) {
print(error)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
}
ReportViewController
//
// ReportViewController.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/22/20.
//
import UIKit
class ReportViewController: UIViewController, WeatherManagerDelegate {
var weatherManager = WeatherManager()
#IBOutlet weak var flightRulesTitleLabel: UILabel!
#IBOutlet weak var flightRulesValueLabel: UILabel!
#IBOutlet weak var visibilityValueLabel: UILabel!
#IBOutlet weak var altimeterValueLabel: UILabel!
#IBOutlet weak var cloudsTitleLabel: UILabel!
#IBOutlet weak var cloudsType1Label: UILabel!
#IBOutlet weak var cloudsAltitude1Label: UILabel!
#IBOutlet weak var cloudsType2Label: UILabel!
#IBOutlet weak var cloudsAltitude2Label: UILabel!
#IBOutlet weak var cloudsType3Label: UILabel!
#IBOutlet weak var cloudsAltitude3Label: UILabel!
#IBOutlet weak var windGTextLabel: UILabel!
#IBOutlet weak var windSpeedValueLabel: UILabel!
#IBOutlet weak var windGustValueLabel: UILabel!
#IBOutlet weak var windFromTextLabel: UILabel!
#IBOutlet weak var windDirectionValueLabel: UILabel!
#IBOutlet weak var remarksValueLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
}
func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) {
DispatchQueue.main.async {
self.flightRulesValueLabel.text = weather.flightConditions
print(weather.flightConditions)
}
}
func didFailWithError(error: Error) {
print(error)
}
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
}
WeatherManager
//
// WeatherManager.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/21/20.
//
import Foundation
protocol WeatherManagerDelegate : class {
func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel)
func didFailWithError(error: Error)
}
struct WeatherManager {
let weatherURL = "https://avwx.rest/api/metar/"
weak var delegate : WeatherManagerDelegate?
func fetchWeather (stationICAO: String) {
let urlString = "\(weatherURL)\(stationICAO)?token=OVi45FiTDo1LmyodShfOfoizNe5m9wyuO6Mkc95AN-c"
performRequest(with: urlString)
}
func performRequest (with urlString: String) {
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
self.delegate?.didFailWithError(error: error!)
return
}
if let safeData = data {
if let weather = self.parseJSON(safeData) {
self.delegate?.didUpdateWeather(self, weather: weather)
}
}
}
task.resume()
print(urlString)
}
}
func parseJSON(_ weatherData: Data) -> WeatherModel? {
do {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let clouds = decodedData.clouds
let lowCloudsType = (clouds.count > 0 ? clouds[0]?.type : nil) ?? "N/A"
let midCloudsType = (clouds.count > 1 ? clouds[1]?.type : nil) ?? "N/A"
let highCloudsType = (clouds.count > 2 ? clouds[2]?.type : nil) ?? "N/A"
let lowCloudsAlt = (clouds.count > 0 ? clouds[0]?.altitude : nil) ?? 0
let midCloudsAlt = (clouds.count > 1 ? clouds[1]?.altitude : nil) ?? 0
let highCloudsAlt = (clouds.count > 2 ? clouds[2]?.altitude : nil) ?? 0
let reportingStationVar = decodedData.station ?? "N/A"
let windGustValue = decodedData.wind_gust?.value ?? 0
let windSpeedValue = decodedData.wind_speed?.value ?? 0
let windDirectionValue = decodedData.wind_direction?.value ?? 999
let visibilityValue = decodedData.visibility?.value ?? 0
let flightRulesValue = decodedData.flight_rules ?? "N/A"
let weather = WeatherModel(lowestCloudsType: lowCloudsType , lowestCloudsAlt: lowCloudsAlt, middleCloudsType: midCloudsType , middleCloudsAlt: midCloudsAlt, highestCloudsType: highCloudsType , highestCloudsAlt: highCloudsAlt, reportingStation: reportingStationVar, windGust: windGustValue, windSpeed: windSpeedValue, windDirection: windDirectionValue, visibility: visibilityValue, flightRules: flightRulesValue)
delegate?.didUpdateWeather(self, weather: weather)
return weather
} catch {
delegate?.didFailWithError(error: error)
return nil
}
}
}
WeatherData
//
// WeatherData.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/21/20.
//
import Foundation
struct WeatherData: Codable {
let clouds: [Clouds?]
let flight_rules: String?
let remarks: String?
let wind_speed: WindSpeed?
let wind_gust: WindGust?
let wind_direction: WindDirection?
let visibility: Visibility?
let station: String?
}
struct Clouds: Codable {
let type: String
let altitude: Int
}
struct WindSpeed: Codable {
let value: Int
}
struct WindGust: Codable {
let value: Int
}
struct WindDirection: Codable {
let value: Int
}
struct Visibility: Codable {
let value: Int
}
Again, I really appreciate the help! There are no errors when I run my code.

Question about passing data from an API into a view controller after user query - Swift

I am working tirelessly on an app and I'm pretty new to Swift. I am trying to take data from an API call after user entry on the landing screen and then present the results of the API call on the modal popover that comes up to display the data that the user wanted. This is an aviation weather app; The user enters something like "KDAB" and the API returns the current aviation weather at that airport. I have a bunch of labels set up for different values and can print them to console after a search but can't seem to get them printed in the VC. I have tried some destination.segue fixes but don't see why I would use them - the data is coming directly from the decoded JSON. Help would be appreciated!
I am trying to display on the ReportViewController in each of the value labels. I.E. Wind speed in the windSpeedValueLabel by utilizing the .text property.
Right now, nothing changes from the default labels on the actual ReportViewController.
Thanks again.
My code:
//
// ReportViewController.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/22/20.
//
import UIKit
class ReportViewController: UIViewController, WeatherManagerDelegate {
var weatherManager = WeatherManager()
#IBOutlet weak var flightRulesTitleLabel: UILabel!
#IBOutlet weak var flightRulesValueLabel: UILabel!
#IBOutlet weak var visibilityValueLabel: UILabel!
#IBOutlet weak var altimeterValueLabel: UILabel!
#IBOutlet weak var cloudsTitleLabel: UILabel!
#IBOutlet weak var cloudsType1Label: UILabel!
#IBOutlet weak var cloudsAltitude1Label: UILabel!
#IBOutlet weak var cloudsType2Label: UILabel!
#IBOutlet weak var cloudsAltitude2Label: UILabel!
#IBOutlet weak var cloudsType3Label: UILabel!
#IBOutlet weak var cloudsAltitude3Label: UILabel!
#IBOutlet weak var windGTextLabel: UILabel!
#IBOutlet weak var windSpeedValueLabel: UILabel!
#IBOutlet weak var windGustValueLabel: UILabel!
#IBOutlet weak var windFromTextLabel: UILabel!
#IBOutlet weak var windDirectionValueLabel: UILabel!
#IBOutlet weak var remarksValueLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) {
DispatchQueue.main.async {
self.flightRulesValueLabel.text = weather.flightRules
self.cloudsType1Label.text = weather.lowestCloudsType
}
}
func didFailWithError(error: Error) {
print(error)
}
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
}
//
// ViewController.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/21/20.
//
import UIKit
class WxViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
var weatherManager = WeatherManager()
#IBOutlet weak var stationSearch: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
stationSearch.delegate = self
}
#IBAction func searchPressed(_ sender: Any) {
print(stationSearch.text!)
stationSearch.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
print(stationSearch.text!)
stationSearch.endEditing(true)
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let station = stationSearch.text {
weatherManager.fetchWeather(stationICAO: station)
}
stationSearch.text = ""
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if stationSearch.text != "" {
return true
} else {
stationSearch.placeholder = "Type an ICAO"
return false
}
}
func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) {
print(weather.flightConditions)
}
func didFailWithError(error: Error) {
print(error)
}
}
//
// WeatherManager.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/21/20.
//
import Foundation
protocol WeatherManagerDelegate {
func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel)
func didFailWithError(error: Error)
}
struct WeatherManager {
let weatherURL = "https://avwx.rest/api/metar/"
var delegate : WeatherManagerDelegate?
func fetchWeather (stationICAO: String) {
let urlString = "\(weatherURL)\(stationICAO)?token=OVi45FiTDo1LmyodShfOfoizNe5m9wyuO6Mkc95AN-c"
performRequest(with: urlString)
}
func performRequest (with urlString: String) {
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
self.delegate?.didFailWithError(error: error!)
return
}
if let safeData = data {
if let weather = self.parseJSON(safeData) {
self.delegate?.didUpdateWeather(self, weather: weather)
}
}
}
task.resume()
print(urlString)
}
}
func parseJSON(_ weatherData: Data) -> WeatherModel? {
do {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let clouds = decodedData.clouds
let lowCloudsType = (clouds.count > 0 ? clouds[0]?.type : nil) ?? "N/A"
let midCloudsType = (clouds.count > 1 ? clouds[1]?.type : nil) ?? "N/A"
let highCloudsType = (clouds.count > 2 ? clouds[2]?.type : nil) ?? "N/A"
let lowCloudsAlt = (clouds.count > 0 ? clouds[0]?.altitude : nil) ?? 0
let midCloudsAlt = (clouds.count > 1 ? clouds[1]?.altitude : nil) ?? 0
let highCloudsAlt = (clouds.count > 2 ? clouds[2]?.altitude : nil) ?? 0
let reportingStationVar = decodedData.station ?? "N/A"
let windGustValue = decodedData.wind_gust?.value ?? 0
let windSpeedValue = decodedData.wind_speed?.value ?? 0
let windDirectionValue = decodedData.wind_direction?.value ?? 999
let visibilityValue = decodedData.visibility?.value ?? 0
let flightRulesValue = decodedData.flight_rules ?? "N/A"
let weather = WeatherModel(lowestCloudsType: lowCloudsType , lowestCloudsAlt: lowCloudsAlt, middleCloudsType: midCloudsType , middleCloudsAlt: midCloudsAlt, highestCloudsType: highCloudsType , highestCloudsAlt: highCloudsAlt, reportingStation: reportingStationVar, windGust: windGustValue, windSpeed: windSpeedValue, windDirection: windDirectionValue, visibility: visibilityValue, flightRules: flightRulesValue)
return weather
} catch {
delegate?.didFailWithError(error: error)
return nil
}
}
}
//
// WeatherData.swift
// AvWx Pro
//
// Created by Grayson Bertaina on 9/21/20.
//
import Foundation
struct WeatherData: Codable {
let clouds: [Clouds?]
let flight_rules: String?
let remarks: String?
let wind_speed: WindSpeed?
let wind_gust: WindGust?
let wind_direction: WindDirection?
let visibility: Visibility?
let station: String?
}
struct Clouds: Codable {
let type: String
let altitude: Int
}
struct WindSpeed: Codable {
let value: Int
}
struct WindGust: Codable {
let value: Int
}
struct WindDirection: Codable {
let value: Int
}
struct Visibility: Codable {
let value: Int
}
Thanks again.
First, on WeatherManager, declare your delegate as weak to avoid reference cycle
weak var delegate : WeatherManagerDelegate?
Second, after you decode the data, you have to call the delegate and pass the data, in this case,the WeatherModel you just created using one of your delegate methods
do {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let clouds = decodedData.clouds
let lowCloudsType = (clouds.count > 0 ? clouds[0]?.type : nil) ?? "N/A"
let midCloudsType = (clouds.count > 1 ? clouds[1]?.type : nil) ?? "N/A"
let highCloudsType = (clouds.count > 2 ? clouds[2]?.type : nil) ?? "N/A"
let lowCloudsAlt = (clouds.count > 0 ? clouds[0]?.altitude : nil) ?? 0
let midCloudsAlt = (clouds.count > 1 ? clouds[1]?.altitude : nil) ?? 0
let highCloudsAlt = (clouds.count > 2 ? clouds[2]?.altitude : nil) ?? 0
let reportingStationVar = decodedData.station ?? "N/A"
let windGustValue = decodedData.wind_gust?.value ?? 0
let windSpeedValue = decodedData.wind_speed?.value ?? 0
let windDirectionValue = decodedData.wind_direction?.value ?? 999
let visibilityValue = decodedData.visibility?.value ?? 0
let flightRulesValue = decodedData.flight_rules ?? "N/A"
let weather = WeatherModel(lowestCloudsType: lowCloudsType , lowestCloudsAlt: lowCloudsAlt, middleCloudsType: midCloudsType , middleCloudsAlt: midCloudsAlt, highestCloudsType: highCloudsType , highestCloudsAlt: highCloudsAlt, reportingStation: reportingStationVar, windGust: windGustValue, windSpeed: windSpeedValue, windDirection: windDirectionValue, visibility: visibilityValue, flightRules: flightRulesValue)
delegate?.didUpdateWeather(self, weather: weather)
return weather
} catch {
delegate?.didFailWithError(error: error)
return nil
}
On ReportViewController, assign self as your WeatherManagerDelegate on viewDidLoad(), then you have to implement the delegate function didUpdateWeather by updating your labels with corresponding values -- which you already started
class ReportViewController: UIViewController, WeatherManagerDelegate {
var weatherManager = WeatherManager()
#IBOutlet weak var flightRulesTitleLabel: UILabel!
#IBOutlet weak var flightRulesValueLabel: UILabel!
#IBOutlet weak var visibilityValueLabel: UILabel!
#IBOutlet weak var altimeterValueLabel: UILabel!
#IBOutlet weak var cloudsTitleLabel: UILabel!
#IBOutlet weak var cloudsType1Label: UILabel!
#IBOutlet weak var cloudsAltitude1Label: UILabel!
#IBOutlet weak var cloudsType2Label: UILabel!
#IBOutlet weak var cloudsAltitude2Label: UILabel!
#IBOutlet weak var cloudsType3Label: UILabel!
#IBOutlet weak var cloudsAltitude3Label: UILabel!
#IBOutlet weak var windGTextLabel: UILabel!
#IBOutlet weak var windSpeedValueLabel: UILabel!
#IBOutlet weak var windGustValueLabel: UILabel!
#IBOutlet weak var windFromTextLabel: UILabel!
#IBOutlet weak var windDirectionValueLabel: UILabel!
#IBOutlet weak var remarksValueLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
}
func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) {
DispatchQueue.main.async {
self.flightRulesValueLabel.text = weather.flightRules
self.cloudsType1Label.text = weather.lowestCloudsType
}
}

Am i using CallKit framwork of Apple for Voip call and how ever it work not work in background state when user accept call using callkit button

when the user gets from CallKit at that time I am switching root on accept button click of Call but somehow root controller object always found nil and application crashed
Case : this is happing when the application running in the background and phone state is locked.
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
endCallTimer()
guard let call = callManager.callWithUUID(uuid: action.callUUID) else {
action.fail()
return
}
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let incomingCall = mainStoryboard.instantiateViewController(withIdentifier: "CallConnectedVC") as! IncomingController
incomingCall.connectToCalling(duration:self.callDict["duration"] as! Int, consumerId: "\(self.callDict["consumerId"] as! Int)", categoryName: self.callDict["categoryTopic"] as! String, categoryImage: "", callId: self.callDict["callId"] as! String)
let nav = UINavigationController(rootViewController: incomingCall)
UIApplication.shared.keyWindow?.makeKeyAndVisible()
UIApplication.shared.keyWindow?.rootViewController = nav
configureAudioSession()
call.answer()
action.fulfill()
}
IncomingController code -
import UIKit
import TwilioVideo
import AVFoundation
import SDWebImage
import FirebaseAnalytics
class IncomingController: UIViewController {
var camera: TVICameraCapturer?
#IBOutlet weak var img_user: UIImageView!
#IBOutlet weak var lblName: UILabel!
#IBOutlet weak var lbltopic: UILabel!
#IBOutlet weak var lblTimer: UILabel!
#IBOutlet weak var btn_speaker : UIButton!
#IBOutlet weak var btn_video: UIButton!
#IBOutlet weak var btn_mute: UIButton!
#IBOutlet weak var btn_extendcall: UIButton!
#IBOutlet weak var btn_endcall: UIButton!
//Video
#IBOutlet weak var btn_toogleMic : UIButton!
#IBOutlet weak var btnConnectAudio : UIButton!
#IBOutlet weak var btnFlipCamera : UIButton!
#IBOutlet weak var view_video: UIView!
#IBOutlet weak var lblviedoName: UILabel!
#IBOutlet weak var lblvideoTimer: UILabel!
#IBOutlet weak var lblvideoTopic: UILabel!
#IBOutlet weak var provider_previewView: TVIVideoView!
#IBOutlet weak var provider_remoteView: TVIVideoView!
#IBOutlet weak var provider_previewViewShadow: UIView!
#IBOutlet weak var btnView : UIView!
#IBOutlet weak var constrain_btnView: NSLayoutConstraint!
//UpdatedVideo
#IBOutlet weak var viewDisableVideo: UIView!
#IBOutlet weak var lblDisableViedoName: UILabel!
#IBOutlet weak var lblDisableVideoTimer: UILabel!
#IBOutlet weak var lblDisableTopic: UILabel!
#IBOutlet weak var lblDisableText: UILabel!
#IBOutlet weak var imgUserDisable: UIImageView!
var NotificationDict = NSMutableDictionary()
var Calltimer: Timer? = Timer()
var sec = 60
var min = 6
var audioDevice: TVIDefaultAudioDevice = TVIDefaultAudioDevice(block: {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, mode: AVAudioSessionModeVoiceChat, options: .mixWithOthers)
try AVAudioSession.sharedInstance().setPreferredSampleRate(48000)
try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(0.01)
} catch {
print(error)
}
})
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
TwilioVideo.audioDevice = self.audioDevice
self.prepareLocalMedia()
connectToCalling(duration:NotificationDict["duration"] as! Int, consumerId: "\(NotificationDict["consumerId"] as! Int)", categoryName: NotificationDict["categoryTopic"] as! String, categoryImage: "", callId: NotificationDict["callId"] as! String)
}
//MARK:- Speaker Method
func setAudioOutputSpeaker(_ enabled: Bool) {
let session = AVAudioSession.sharedInstance()
try? session.setCategory(AVAudioSessionCategoryPlayAndRecord)
try? session.setMode(AVAudioSessionModeVoiceChat)
if enabled {
try? session.overrideOutputAudioPort(AVAudioSessionPortOverride.speaker)
} else {
try? session.overrideOutputAudioPort(AVAudioSessionPortOverride.none)
}
try? session.setActive(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK:- Custom Method
func connectToCalling(duration:Int, consumerId:String, categoryName:String, categoryImage:String, callId:String)
{
let Url = "\(Constant.tokenUrl)"
let Param = ["duration":duration, "consumerId":consumerId, "categoryName":categoryName, "categoryImage":categoryImage, "callId":callId] as [String:Any]
ApiResponse.onResponseKeyPost(url: Url, parms: Param as NSDictionary, completion: { (dict, errr) in
print("dict responce",dict)
if(errr == ""){
OperationQueue.main.addOperation {
accessToken = dict["providerToken"] as! String
let connectOptions = TVIConnectOptions.init(token: accessToken) { (builder) in
if let videoTrack = ProvideCallObject.localVideoTrack {
builder.videoTracks = [videoTrack]
}
// We will share a local audio track only if ExampleAVAudioEngineDevice is selected.
if let audioTrack = ProvideCallObject.localAudioTrack {
builder.audioTracks = [audioTrack]
}
userDef.set("\(dict["roomId"]!)", forKey: "roomId")
builder.roomName = "\(dict["roomId"]!)"
}
ProvideCallObject.Provideroom = TwilioVideo.connect(with: connectOptions, delegate: self)
}
}
})
}
#objc func updateCountDown() {
if btn_endcall.isUserInteractionEnabled == false
{
btn_endcall.isUserInteractionEnabled = true
}
if sec == 0 {
if min == 0
{
sec = 0
min = 0
goToFeedback()
Calltimer?.invalidate()
}else
{
sec = 59
min = min - 1
if min == 0 {
SystemSoundID.playFileNamed(fileName: "60 Seconds v2", withExtenstion: "m4a")
}
}
}else
{
var timeString = ""
sec = sec - 1
if min < 10
{
timeString = timeString + "0" + String(min)
if min == 2 && sec == 0{
SystemSoundID.playFileNamed(fileName: "2 Mins v2", withExtenstion: "m4a")
}
}
else
{
timeString = timeString + String(min)
}
if sec < 10
{
timeString = timeString + ":0" + String(sec)
}
else
{
timeString = timeString + ":" + String(sec)
}
lblTimer.text = "\(timeString) Free Minutes"
lblvideoTimer.text = "\(timeString) Free Minutes"
lblDisableVideoTimer.text = "\(timeString) remaining"
}
}
func prepareLocalMedia() {
if (ProvideCallObject.localAudioTrack == nil) {
ProvideCallObject.localAudioTrack = TVILocalAudioTrack.init(options: nil, enabled: true, name: "Microphone")
if (ProvideCallObject.localAudioTrack == nil) {
print("Failed to create audio track")
}
}
if (ProvideCallObject.localVideoTrack == nil) {
self.startPreview()
}
changeButtonImage(isVideoEnable: true)
}
//Video
func setupRemoteVideoView() {
self.provider_previewViewShadow.frame = CGRect(x: self.view_video.bounds.width - 132, y: self.view_video.bounds.height - 239, width: 112, height: 149)
self.provider_previewView.frame = self.provider_previewViewShadow.bounds
self.provider_remoteView.bringSubview(toFront: self.provider_previewViewShadow)
self.provider_remoteView.isHidden = false
}
// MARK: Private
func startPreview() {
if PlatformUtils.isSimulator {
return
}
camera = TVICameraCapturer(source: .frontCamera, delegate: self)
ProvideCallObject.localVideoTrack = TVILocalVideoTrack.init(capturer: camera!, enabled: true, constraints: nil, name: "Camera")
if (ProvideCallObject.localVideoTrack == nil) {
print("Failed to create video track")
} else {
ProvideCallObject.localVideoTrack!.addRenderer(self.provider_previewView)
let tap = UITapGestureRecognizer(target: self, action: #selector(IncomingController.flipCamera))
self.provider_previewView.addGestureRecognizer(tap)
}
}
#objc func flipCamera() {
if (self.camera?.source == .frontCamera) {
self.camera?.selectSource(.backCameraWide)
} else {
self.camera?.selectSource(.frontCamera)
}
}
func cleanupRemoteParticipant() {
if ((ProvideCallObject.remoteParticipant) != nil) {
if ((ProvideCallObject.remoteParticipant?.videoTracks.count)! > 0) {
let remoteVideoTrack = ProvideCallObject.remoteParticipant?.remoteVideoTracks[0].remoteTrack
remoteVideoTrack?.removeRenderer(self.provider_remoteView!)
self.provider_remoteView?.isHidden = true
}
}
ProvideCallObject.remoteParticipant = nil
}
}
// MARK:- TVIRoomDelegate
extension IncomingController : TVIRoomDelegate {
func callDetails(room_sid:String,callID:String) {
let params = ["room_sid":room_sid,"callId": callID,"isCallEnd":0] as [String : Any]
ApiResponse.onResponsePost(url: Constant.callDetailsTwilio, parms: params as NSDictionary) { (response, error) in
}
}
func didConnect(to room: TVIRoom) {
ProvideCallObject.localParticipant = ProvideCallObject.Provideroom?.localParticipant
ProvideCallObject.localParticipant?.delegate = self
Calltimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCountDown), userInfo: nil, repeats: true)
NotificationDict["roomsid"] = room.sid
btn_video.isUserInteractionEnabled = true
-----------------------------------------
if (room.remoteParticipants.count > 0) {
ProvideCallObject.remoteParticipant = room.remoteParticipants[0]
ProvideCallObject.remoteParticipant?.delegate = self
self.callDetails(room_sid: room.sid, callID: NotificationDict["callId"] as! String)
}
if !isHeadPhoneAvailabel(){
self.setAudioOutputSpeaker(true)
}
}
func room(_ room: TVIRoom, didDisconnectWithError error: Error?) {
self.cleanupRemoteParticipant()
}
func room(_ room: TVIRoom, didFailToConnectWithError error: Error) {
}
func room(_ room: TVIRoom, participantDidConnect participant: TVIRemoteParticipant) {
if (ProvideCallObject.remoteParticipant == nil) {
ProvideCallObject.remoteParticipant = participant
ProvideCallObject.remoteParticipant?.delegate = self
}
// print("Participant \(participant.identity) connected with \(participant.remoteAudioTracks.count) audio and \(participant.remoteVideoTracks.count) video tracks")
}
func room(_ room: TVIRoom, participantDidDisconnect participant: TVIRemoteParticipant) {
if (ProvideCallObject.remoteParticipant == participant) {
cleanupRemoteParticipant()
}
goToFeedback()
}
}
// MARK: TVIRemoteParticipantDelegate
extension IncomingController : TVILocalParticipantDelegate {
func localParticipant(_ participant: TVILocalParticipant, publishedVideoTrack: TVILocalVideoTrackPublication) {
ProvideCallObject.localVideoTrack = publishedVideoTrack.videoTrack as? TVILocalVideoTrack
}
}
// MARK: TVIRemoteParticipantDelegate
extension IncomingController : TVIRemoteParticipantDelegate {
func subscribed(to videoTrack: TVIRemoteVideoTrack,
publication: TVIRemoteVideoTrackPublication,
for participant: TVIRemoteParticipant) {
if (ProvideCallObject.remoteParticipant == participant) {
setupRemoteVideoView()
videoTrack.addRenderer(self.provider_remoteView!)
}
}
func remoteParticipant(_ participant: TVIRemoteParticipant,
enabledVideoTrack publication: TVIRemoteVideoTrackPublication) {
// print("Participant \(participant.identity) enabled \(publication.trackName) video track")
self.viewDisableVideo.isHidden = true
changeButtonImage(isVideoEnable: true)
}
func remoteParticipant(_ participant: TVIRemoteParticipant,
disabledVideoTrack publication: TVIRemoteVideoTrackPublication) {
self.viewDisableVideo.isHidden = false
changeButtonImage(isVideoEnable: false)
// print("Participant \(participant.identity) disabled \(publication.trackName) video track")
}
}
extension IncomingController : TVICameraCapturerDelegate {
func cameraCapturer(_ capturer: TVICameraCapturer, didStartWith source: TVICameraCaptureSource) {
// Layout the camera preview with dimensions appropriate for our orientation.
self.provider_previewView.shouldMirror = (source == .frontCamera)
}
}
// MARK: TVIVideoViewDelegate
extension IncomingController : TVIVideoViewDelegate {
func videoView(_ view: TVIVideoView, videoDimensionsDidChange dimensions: CMVideoDimensions) {
self.view.setNeedsLayout()
}
}
so it is possible to switch or change root controller in define case?
Thanks

Resources