Skip to content

jacobkosmart/covidApp-iOS-practice

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

10 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ’‰ CovidApp-iOS-practice

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ

๐Ÿ“Œ ๊ธฐ๋Šฅ ์ƒ์„ธ

  • ์‹œ๋„๋ณ„ ์‹ ๊ทœ ํ™•์ง„์ž ์ˆ˜๊ฐ€ ํŒŒ์ด ์ฐจํŠธ๋กœ ํ‘œ์‹œ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

  • ๋„์‹œ ํ•ญ๋ชฉ์„ ์„ ํƒํ•˜๋ฉด ์ƒ์„ธ ํ˜„ํ™ฉ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์œผ๋กœ ์ด๋™๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ”‘ Check Point !

image

๐Ÿ”ท App Model

// in CityCovidOverView.swift

import Foundation

struct CityCovidOverView: Codable {
	let korea: CovidOverview
	let seoul: CovidOverview
	let busan: CovidOverview
	let daegu: CovidOverview
	let incheon: CovidOverview
	let gwangju: CovidOverview
	let daejeon: CovidOverview
	let ulsan: CovidOverview
	let sejong: CovidOverview
	let gyeonggi: CovidOverview
	let gangwon: CovidOverview
	let chungbuk: CovidOverview
	let chungnam: CovidOverview
	let jeonbuk: CovidOverview
	let jeonnam: CovidOverview
	let gyeongbuk: CovidOverview
	let gyeongnam: CovidOverview
	let jeju: CovidOverview
}

struct CovidOverview: Codable {
	let countryName: String
	let newCase: String
	let totalCase: String
	let recovered: String
	let death: String
	let percentage: String
	let newCcase: String
	let newFcase: String
}

// in CovidTime.swift

import Foundation
struct CovidTime: Codable {
	let updateTime: String
}

๐Ÿ”ท ๊ตฟ๋ฐ”์ด ์ฝ”๋กœ๋‚˜ 19 API

Corona-19-API - https://github.com/dhlife09/Corona-19-API

๐Ÿ”ท Alamofire

  • Alamofire ๋Š” Swift ๊ธฐ๋ฐ˜์˜ HTTP ๋„คํŠธ์›Œํ‚น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž…๋‹ˆ๋‹ค. URLSession ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ์„œ, ๋„คํŠธ์›Œํ‚น ์ž‘์—…์„ ๋‹จ์ˆœํžˆ ํ•˜๊ณ  ๋„คํŠธ์›Œํ‚น์„ ์œ„ํ•œ ๋‹ค์–‘ํ•œ method, json parsing ๋“ฑ์„ ์ œ๊ณต ํ•ฉ๋‹ˆ๋‹ค.

Alamofire ์ฃผ์š” ํŠน์ง•

  • ์—ฐ๊ฒฐ ๊ฐ€๋Šฅํ•œ request, response method ๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, URL json parameter encoding ์„ ์ง€์› ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ, multi part form date ๋“ฑ upload ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉฐ, HTTP response ๊ฒ€์ฆ๊ณผ ๊ด‘๋ฒ”์œ„ํ•œ Unit Test, ํ†ตํ•ฉ Test ๋“ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค

completionHandler ์˜ escaping closure ์„ ์–ธ ์ด์œ 

  • func ์—์„œ escape ์„ ์–ธ์€ ํ•จ์ˆ˜์˜ scope ๋ฅผ ๋ฒ—์–ด ๋‚˜์„œ๋„ ๋ณ€์ˆ˜๊ฐ€ ์ฐธ์กฐ ๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ํด๋กœ์ ธ ์ž„. ์ฆ‰, ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ closure ๊ฐ€ ์ „๋‹ฌ๋˜์ง€๋งŒ, ๋ฐ˜ํ™˜๋œ ํ›„์—๋„ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

  • escaping closure ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ๊ฒฝ์šฐ๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์„ ํ•˜๋Š” ๊ฒฝ์šฐ completionHandler ๋กœ์„œ escaping closure ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ณดํ†ต ๋„คํŠธ์›Œํ‚น ํ†ต์‹ ์€ ๋น„๋™๊ธฐ ์ž‘์—…์œผ๋กœ ์ฒ˜๋ฆฌ ๋˜๋Š”๋ฐ, .responseData ์— ์ •์˜๋œ completionHandler closure ๋Š” fetch data ๊ฐ€ ๋ฐ˜ํ™˜ ๋œ ํ›„์—, ํ˜ธ์ถœ์ด ๋ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ธ์ œ ์‘๋‹ต ํ•ด์ค„์ง€ ๋ชจ๋ฅด๊ณ , ์‘๋‹ต์‹œ๊ฐ„์ด ๋กœ๋”ฉ ๋˜๊ธฐ ๋•Œ๋ฌธ์— server ์—์„œ ๋น„๋™๊ธฐ๋กœ ์‘๋‹ต ๋ฐ›๊ธฐ ์ „์— ์ฆ‰, .responseData ์— ์ „๋‹ฌํ•œ parameter ๊ฐ€ completionHandler๊ฐ€ ํ˜ธ์ถœ๋˜๊ธฐ ์ „์— ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋˜์„œ ์„œ๋ฒ„์˜ ์‘๋‹ต์„ ๋ฐ›์•„๋„ ๋™์ž‘ํ•˜์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค

  • ๊ทธ๋ž˜์„œ, ๋น„๋™๊ธฐ ์ž‘์—…์„ completionHandler ๋กœ callback ์„ ์‹œ์ผœ์ค˜์•ผ ํ•œ๋‹ค๋ฉด, escaping closure ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•จ์ˆ˜๊ฐ€ return ๋œ ํ›„์—๋„, ์‹คํ–‰ ์‹œ์ผœ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค

// in viewDidLoad.swift

	// fetch data SearchCovideOverview
	func fetchCovidOverview(
		// API ๋ฅผ ํ†ตํ•ด์„œ sever์—์„œ json dat ๋ฅผ ๋ฐ›๊ฑฐ๋‚˜, ์š”์ฒญ์— ์‹คํŒจ ํ•˜์˜€์„๋•Œ completionHandler ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ํ•ด๋‹น closure ๋ฅผ ์ •์˜ํ•˜๋Š” ๊ณณ์— ์‘๋‹ต๋ฐ›์€ data๋ฅผ ์ „๋‹ฌ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
		// completionHandler ๋ฅผ @escaping closure ๊ฐ€ ๋˜๊ฒŒ ์„ค์ •
		completionHandler: @escaping (Result<CityCovidOverView, Error>) -> Void
	) {
		let url = "https://api.corona-19.kr/korea/country/new/"
		let param = [
			"serviceKey": "16KIXAdhg7tk93ivjzsHFCQ8oOLyNSUuE"
		]

		// Alamofire ๋ฅผ ํ†ตํ•ด์„œ API ํ˜ธ์ถœ
		AF.request(url, method: .get, parameters: param)
			.responseData(completionHandler: { response in
				switch response.result {
				case let .success(data):
					do {
						let decoder = JSONDecoder()
						let result = try decoder.decode(CityCovidOverView.self, from: data)
						completionHandler(.success(result))
					} catch { // error code ์ฒ˜๋ฆฌ
						completionHandler(.failure(error))
					}
				case let .failure(error):
					completionHandler(.failure(error))
				}
			})
	}

๐Ÿ”ท Cocoapods

  • Apple platform ์—์„œ ๊ฐœ๋ฐœ์„ ํ•  ๋•Œ, ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ๋„๋ก ๋„์™€์ฃผ๋Š” ์˜์กด์„ฑ ๊ด€๋ฆฌ๋„๊ตฌ ์ž…๋‹ˆ๋‹ค

  • ํ”„๋กœ์ ํŠธ์—์„œ ํ•„์š”ํ•œ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ , ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Cocoapods official site - https://cocoapods.org/

Cocoapods ์„ค์น˜

$ sudo gem install cocoapods

Cocoapods ํ”„๋กœ์ ํŠธ์— ์ ์šฉ

  • xcode ๋กœ ์ƒˆ๋กœ์šด project ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ๊ทธ ๊ฒฝ๋กœ์— terminal ๋กœ ๊ฐ€์„œ Podfile ์ƒ์„ฑ
pod init
  • Podfile ์„ ์ˆ˜์ •ํ•˜๊ฒŒ ๋˜๋ฉด ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜, ์ˆ˜์ • ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”ท Loading indicator

  • ViewController ์— loading indicator ๋ฅผ ํ‘œ์‹œํ•˜์—ฌ, Covid API ๋ฅผ ํ˜ธ์ถœ ํ•˜์˜€์„ ๋•Œ, ์„œ๋ฒ„์—์„œ ์‘๋‹ต์ด ์˜ค๊ธฐ ์ „์ด๋ผ๋ฉด ํ™”๋ฉด์— indicator ๊ฐ€ ํ‘œ์‹œ๋˜๊ฒŒ ๊ตฌํ˜„ํ•˜๊ธฐ

  • ์„œ๋ฒ„์—์„œ ์‘๋‹ต์ด ์˜จ๋‹ค๋ฉด, indicator ๋ฅผ ์ˆจ๊ธฐ๊ณ  label ๊ณผ pieChart ๊ฐ€ ํ‘œ์‹œ๋˜๊ฒŒ ๊ตฌํ˜„ ํ•˜๊ธฐ

๊ตฌํ˜„ ๋ฐฉ๋ฒ•

  • storyboard ์—์„œ Activity Indicator ์ถ”๊ฐ€ ํ•œ๋‹ค์Œ์— ๊ธฐ์กด์— StackView์˜ label, pieChartView ์—๋Š” hidden ์„ ์ฒดํฌ ํ•ด์ค˜์„œ ์ž ์‹œ ์ˆจ๊น€๋‹ˆ๋‹ค
// in ViewController.swift

	override func viewDidLoad() {
		super.viewDidLoad()
		// app ์ด ์‹คํ–‰๋˜๋ฉด indicator ์˜ animation ์‹œ์ž‘
		self.indicatorView.startAnimating()
		// ์ˆœํ™˜์ฐธ์กฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ [weak self] ๋กœ capture list ๋ฅผ ์ •์˜ํ•ด์คŒ
		self.fetchCovidOverview(completionHandler: { [weak self] result in
			guard let self = self else { return } // self ๊ฐ€ ์ผ์‹œ์ ์œผ๋กœ strong ์ด ๋˜๊ฒŒ ํ•จ
			// ์‘๋‹ต์ด ์˜ค๋ฉด completionHandler ๊ฐ€ ์ž‘๋™๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋•Œ indicatorView ์˜ animating ์„ stop ์‹œ์ผœ ์ฃผ๊ณ , hidden ์‹œ์ผœ ์ฃผ๊ณ , ๋‚˜๋จธ์ง€ information data ๋ถ€๋ถ„์ด ๋‚˜ํƒ€๋‚˜๊ฒŒ ํ•ด์คŒ
			self.indicatorView.stopAnimating()
			self.indicatorView.isHidden = true
			self.labelStackView.isHidden = false
			self.readingDate.isHidden = false
			self.pieChartView.isHidden = false
  ......
    }
  }

๐Ÿ”ท API Key ์ˆจ๊ธฐ๊ธฐ

  • GitHub ๋‚˜ ์™ธ๋ถ€๋กœ public ํ•˜๊ฒŒ project ๊ณต์œ ์‹œ, apiKey ๋‚˜, passWord, authentication ๊ด€๋ จ key ๊ฐ’๋“ค์„ ์™ธ๋ถ€์— ๋…ธ์ถœ ์‹œํ‚ค์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ code ์— key ๊ฐ’์„ ๋ฐ”๋กœ ์ ๋Š”๊ฒƒ์ด ์•„๋‹ˆ๋ผ, plist (property list) ๋ฅผ ์ƒ์„ฑํ•ด์ค˜์„œ ๋”ฐ๋กœ ๊ด€๋ฆฌ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

1. API_KEY ๋ฅผ ์ €์žฅํ•  plist ์ƒ์„ฑ

  • Key ๊ฐ’์œผ๋กœ API_KEY value ๊ฐ’์œผ๋กœ API key ๊ฐ’์„ ๋„ฃ์Šต๋‹ˆ๋‹ค

image

2. key ๊ฐ’์„ ๋ถˆ๋Ÿฌ์˜ฌ Extension ํŒŒ์ผ ์ƒ์„ฑ

// in CovidApp++Bundle.swift
// "์•ฑ์ด๋ฆ„++Bundle.swift ํ˜•์‹์œผ๋กœ

import Foundation

// Bundle ๊ฐ’์œผ๋กœ ๋ณ€์ˆ˜ key ๋ฅผ return
extension Bundle {
	var apiKey: String {
		guard let file = self.path(forResource: "CovidKey", ofType: "plist") else { return ""}

		guard let resource = NSDictionary(contentsOfFile: file) else {return ""}
		guard let key = resource["API_KEY"] as? String else { fatalError("Check API Key value")}
		return key
	}
}

๐Ÿ“Œ Bundle ์— extension ํ•ด์„œ ์ž‘์„ฑํ•˜๋Š” ์ด์œ ?

  • Bundle ์ด๋ž€ ์‹คํ–‰๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ์™€ ๊ทธ ์ฝ”๋“œ์˜ ์ž์›์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ๋””๋ ‰ํ† ๋ฆฌ ์ž…๋‹ˆ๋‹ค.

  • Bundle ์•ˆ์— apiKey ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด Bundle.main.apiKey ์ˆœ์œผ๋กœ ์ ‘๊ทผํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค

3. API ํ˜ธ์ถœ ํ• ๋•Œ plist ์— ์ €์žฅํ•œ API_KEY ์ •๋ณด ๋ถˆ๋Ÿฌ์™€ ๋Œ€์ž…ํ•˜๊ธฐ

// in viewController.swift

// fetch data SearchCovideOverview
func fetchCovidOverview(
	// API ๋ฅผ ํ†ตํ•ด์„œ sever์—์„œ json dat ๋ฅผ ๋ฐ›๊ฑฐ๋‚˜, ์š”์ฒญ์— ์‹คํŒจ ํ•˜์˜€์„๋•Œ completionHandler ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ํ•ด๋‹น closure ๋ฅผ ์ •์˜ํ•˜๋Š” ๊ณณ์— ์‘๋‹ต๋ฐ›์€ data๋ฅผ ์ „๋‹ฌ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
	// completionHandler ๋ฅผ @escaping closure ๊ฐ€ ๋˜๊ฒŒ ์„ค์ •
	completionHandler: @escaping (Result<CityCovidOverView, Error>) -> Void
) {
	let apiKey = Bundle.main.apiKey
	let url = "https://api.corona-19.kr/korea/country/new/"
	let param = [
		"serviceKey": apiKey
	]
....

4. .gitignore ์— ์ถ”๊ฐ€ ์‹œํ‚ค๊ธฐ

# Created by https://www.toptal.com/developers/gitignore/api/xcode,cocoapods
# Edit at https://www.toptal.com/developers/gitignore?templates=xcode,cocoapods

### CocoaPods ###
## CocoaPods GitIgnore Template

# CocoaPods - Only use to conserve bandwidth / Save time on Pushing
#           - Also handy if you have a large number of dependant pods
#           - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE
Pods/


### Xcode ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

##APIKEY
CovidKey.plist

## User settings
xcuserdata

.....

5. Git ์ถ”์  ์ค‘์ง€ ์‹œํ‚ค๊ธฐ

  • ์ถ”ํ›„ git clone project ์‹คํ–‰์‹œ local ํ™˜๊ฒฝ์—์„œ APIKEY ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ git ์—์„œ ์ž๋™ ์ถ”์ ๋˜์ง€ ์•Š๊ณ  gitHub ๋“ฑ์— ์—…๋กœ๋“œ ๋˜์ง€ ์•Š๊ฒŒ ํ•˜๊ธฐ์ž…๋‹ˆ๋‹ค
# git update-index --skip-worktree  ํ”„๋กœ์ ํŠธ๋ช…/ํŒŒ์ผ๋ช….plist
git update-index --skip-worktree  07_covid_app/covidKey.plist
  • ๋‹ค์‹œ ์›์ƒ ๋ณต๊ท€๋Š” --no-skip ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค
git update-index --no-skip-worktree  07_covid_app/covidKey.plist

Describing check point in details in Jacob's DevLog - https://jacobko.info/ios/ios-08/


๐Ÿ”ถ ๐Ÿ”ท ๐Ÿ“Œ ๐Ÿ”‘ ๐Ÿ‘‰

๐Ÿ—ƒ Reference

Jacob's DevLog - https://jacobko.info/uikit/ios-08/

๋‚˜๋ฅธํ•œ ์ฝ”๋”ฉ - https://nareunhagae.tistory.com/44

codewithchrist - https://codewithchris.com/alamofire/

fastcampus - https://fastcampus.co.kr/dev_online_iosappfinal

Releases

No releases published

Packages

No packages published