{"id":18696065,"url":"https://github.com/jacobkosmart/weatherapp-ios-practice","last_synced_at":"2025-11-08T14:30:31.643Z","repository":{"id":140609274,"uuid":"439538763","full_name":"jacobkosmart/weatherApp-iOS-practice","owner":"jacobkosmart","description":"To practice URLSession to fetch  json data from open weather API","archived":false,"fork":false,"pushed_at":"2021-12-18T06:12:02.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-28T03:27:48.044Z","etag":null,"topics":["openweatherapi","urlsession"],"latest_commit_sha":null,"homepage":"https://jacobko.info/uikit/ios-08/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jacobkosmart.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-18T05:47:50.000Z","updated_at":"2021-12-18T06:13:31.000Z","dependencies_parsed_at":"2023-03-27T10:57:51.220Z","dependency_job_id":null,"html_url":"https://github.com/jacobkosmart/weatherApp-iOS-practice","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacobkosmart%2FweatherApp-iOS-practice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacobkosmart%2FweatherApp-iOS-practice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacobkosmart%2FweatherApp-iOS-practice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jacobkosmart%2FweatherApp-iOS-practice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jacobkosmart","download_url":"https://codeload.github.com/jacobkosmart/weatherApp-iOS-practice/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239558911,"owners_count":19658927,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["openweatherapi","urlsession"],"created_at":"2024-11-07T11:17:02.451Z","updated_at":"2025-11-08T14:30:31.604Z","avatar_url":"https://github.com/jacobkosmart.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ⛅️ weatherApp-iOS-practice\n\n\u003cimg width=\"350\" alt=\"스크린샷\" src=\"https://user-images.githubusercontent.com/28912774/146631143-7524bfb4-5f84-416c-a316-3d9d56ea21db.gif\"\u003e\n\n## 📌 기능 상세\n\n- 도시 이름을 입력하면 현재 날씨 정보를 가져와 화면에 표시되게 만들어야 합니다\n\n- 도시 이름을 잘못 입력하면 서버로부터 응답받은 에러 메시지가 alert으로 표시 됩니다\n\n## 🔑 Check Point !\n\n![image](https://user-images.githubusercontent.com/28912774/146631112-f5bb1378-ac2e-4dfe-bcb1-d790392ec992.png)\n\n### 🔷 Current Weather API (OpenWeather API)\n\n#### json 과 struct 구조체(model) mapping 하기\n\n![image](https://user-images.githubusercontent.com/28912774/146618816-114981b1-717a-43c3-be8d-e336b498637a.png)\n\n```swift\n// WeatherInfo.swift\n\nimport Foundation\n\n// Codable 은 자신을 변환하거나, 외부표현으로 변환 할 수 있는 (예, .json) 타입을 의미함\n// Codable 은 decodable(자신을 외부에 decoding 타입), encodable(자신을 외부에서 encoding 타입)\n// Codable protocol 을 채택 했다는 것은 Json decoding, encoding 이 모두 가능 하다는 것임, 즉 Json \u003c-\u003e WeatherInfo 객체\nstruct WeatherInfo: Codable {\n\tlet weather: [Weather]\n\tlet temp: Temp\n\tlet nameL: String\n\n\tenum CodingKeys: String, CodingKey {\n\t\tcase weather\n\t\tcase temp = \"main\"\n\t\tcase name\n\t}\n}\n\nstruct Weather: Codable {\n\tlet id: Int\n\tlet main: String\n\tlet description: String\n\tlet icon: String\n}\n\n\n// 만약 json의 property 이름과 type 의 이름이 다를 경우 type 내부에서 codingKeys 라는 String type 의 열거형을 선언하고 codingKey protocol 을 준수하게 만들어야 함\n// main property 의 json 에서 temp struct 에 mapping 시키기 위해서 property 정의함\nstruct Temp: Codable {\n\tlet temp: Double\n\tlet feelsLike: Double\n\tlet minTemp: Double\n\tlet maxTemp: Double\n\n\tenum CodingKeys: String, CodingKey {\n\t\tcase temp\n\t\tcase feelsLike = \"feels_like\"\n\t\tcase minTemp = \"temp_min\"\n\t\tcase maxTemp = \"temp_max\"\n\t}\n}\n```\n\n#### response 한 data를 UI 에 업데이트\n\n```swift\n//\n\n// UI창에 weatherInfo 가 나타나게 하는 method\nfunc configureView(weatherInfo: WeatherInfo) {\n\tself.cityNameLabel.text = weatherInfo.name\n\t// weatherInfor 안에 wather 의 첫번째 상수에 대입\n\tif let weather = weatherInfo.weather.first {\n\t\tself.weatherDescriptionLabel.text = weather.description\n\t}\n\tself.tempLabel.text = \"\\(Int(weatherInfo.temp.temp))°C\"\n\tself.minTempLabel.text = \"최저: \\(Int(weatherInfo.temp.minTemp))°C\"\n\tself.maxTempLabel.text = \"최고: \\(Int(weatherInfo.temp.maxTemp))°C\"\n}\n```\n\n- 도시의 현재 날씨 정보를 가져옵니다\n\n### 🔷 URLSession\n\n```swift\nfunc getCurrentWeather(cityName: String) {\n\tguard let url = URL(string: \"https://api.openweathermap.org/data/2.5/weather?q=\\(cityName)\u0026units=metric\u0026lang=kr\u0026appid=0fb8463dce1de96897cba0b1eff08e18\") else { return }\n\t// session 을 default session 으로 설정\n\tlet session = URLSession(configuration: .default)\n\t// compression handler 로써 closure 매개 변수에 data(서버에서 응답 받은 data), response(HTTP header 나 상태 코드의 metaData), error(error 코드 반환)\n\tsession.dataTask(with: url) { [weak self] data, response, error in\n......\n\t}\n```\n\n\u003e Describing check point in details in Jacob's DevLog - https://jacobko.info/ios/ios-06/\n\n## ❌ Error Check Point\n\n### 🔶 API Response Error 발생시 Error 처리\n\n![image](https://user-images.githubusercontent.com/28912774/146629837-ece86509-5b1e-4909-8a35-93463e0a82d5.png)\n\n위와 같이 textField 에서 도시이름이 오타나 검색이 되지 않으면, 404 error 가 발생합니다. 그럴때 alert 창으로 **도시이름이 일치하지 않습니다** 라는 나오게 하는 code 는 다음과 같습니다\n\n- Error message 처리를 위한 struct 모델 생성\n\n```swift\n// in ViewController.swift\n\n// Error message 가 alert 에 표시되게 하는 logic\nfunc showAlert(message: String) {\n\tlet alert = UIAlertController(title: \"Error\", message: message, preferredStyle: .alert)\n\talert.addAction(UIAlertAction(title: \"확인\", style: .default, handler: nil))\n\tself.present(alert, animated: true, completion: nil)\n}\n\n// URLSession 을 이용해서 currentWeather API를 호출하기\nfunc getCurrentWeather(cityName: String) {\n\tguard let url = URL(string: \"https://api.openweathermap.org/data/2.5/weather?q=\\(cityName)\u0026units=metric\u0026lang=kr\u0026appid=0fb8463dce1de96897cba0b1eff08e18\") else { return }\n\t// session 을 default session 으로 설정\n\tlet session = URLSession(configuration: .default)\n\t// compression handler 로써 closure 매개 변수에 data(서버에서 응답 받은 data), response(HTTP header 나 상태 코드의 metaData), error(error 코드 반환)\n\tsession.dataTask(with: url) { [weak self] data, response, error in\n\t\t// 응답받은 response (json data)를 weatherInfo struct 에 decoding 되게 하는 logic\n\t\tlet successRange = (200..\u003c300)\n\t\tguard let data = data, error == nil else { return }\n\t\tlet decorder = JSONDecoder()\n\t\t// 응답받은 data 의 statusCode 가 200번대 (200 ~ 299) 일때\n\t\tif let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) {\n\t\t\tguard let weatherInfo =  try? decorder.decode(WeatherInfo.self, from: data) else { return }\n\t\t\t// debugPrint(weatherInfo)\n\t\t\t// 받아온 데이터를 UI 에 표시하기 위해서는 main thread 에서 작업을 진행 햐여 됩\n\t\t\tDispatchQueue.main.async {\n\t\t\t\tself?.weatherStackView.isHidden = false\n\t\t\t\tself?.configureView(weatherInfo: weatherInfo)\n\t\t\t\t}\n\t\t\t} else { // status code 가 200 번대가 아니면 error 상태 이니까 error message 생성 logic\n\t\t\t\tguard let errorMessage = try? decorder.decode(ErrorMessage.self, from: data) else { return }\n\t\t\t\t// debugPrint(errorMessage)\n\t\t\t\t// main thread 에서 alert 이 표시되게 해야됨\n\t\t\t\tDispatchQueue.main.async {\n\t\t\t\t\tself?.showAlert(message: errorMessage.message)\n\t\t\t\t}\n\t\t}\n\t}.resume() // app 이 실행되게 함\n\t}\n```\n\n![Kapture 2021-12-18 at 14 33 20](https://user-images.githubusercontent.com/28912774/146630539-dcb10199-0ca2-419b-9513-8beb8eab2c97.gif)\n\n---\n\n🔶 🔷 📌 🔑 👉\n\n## 🗃 Reference\n\nJacob's DevLog - [https://jacobko.info/ios/ios-08/](https://jacobko.info/ios/ios-08/)\n\n아직은 어렵지 - [https://greatpapa.tistory.com/66](https://greatpapa.tistory.com/66)\n\nfastcampus - [https://fastcampus.co.kr/dev_online_iosappfinal](https://fastcampus.co.kr/dev_online_iosappfinal)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacobkosmart%2Fweatherapp-ios-practice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjacobkosmart%2Fweatherapp-ios-practice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjacobkosmart%2Fweatherapp-ios-practice/lists"}