Giter Site home page Giter Site logo

jacobkosmart / creditcard-ios-practice Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 29 KB

To practice firebase realtime DB and firestroe CRUD

Home Page: https://jacobko.info/firebaseios/ios-firebase-02/

Swift 98.26% Ruby 1.74%
firebase firebase-realtime-database firestore-database kingfisher lottie

creditcard-ios-practice's Introduction

๐Ÿ’ณ creditCard-iOS-practice

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

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

  • Firebase realtime, fireStore DB ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์นด๋“œ์ถ”์ฒœ ํŽ˜์ด์ง€ ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค

  • Firebase ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ๊ณผ์ •์„ ์—ฐ์Šตํ•ฉ๋‹ˆ๋‹ค

๐Ÿ‘‰ Pod library

๐Ÿ”ท Kingfisher library

  • ์ด๋ฏธ์ง€ ์„œ๋ฒ„์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ๋‹ค๊ฐ€ UI์— ๊ฐ€์ ธ๋‹ค๊ฐ€ ํ‘œ์‹œ ํ•ด์ค„๋•Œ ์‚ฌ์šฉ๋˜๋Š” ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์˜คํ”ˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž…๋‹ˆ๋‹ค

Kingfisher cheatSheet - https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet

์„ค์น˜

pod init

  # Pods for 09_creditCardList
  pod 'Kingfisher'
end

pod install

์‚ฌ์šฉ๋ฒ•

//  CardListViewController.swift

// URL ํƒ€์ž…์œผ๋กœ ํƒ€์ž…๋ณ€ํ™˜ํ•จ
let imageURL = URL(string: creditCardList[indexPath.row].cardImageURL)
// Kingfisher ๋ฅผ ์‚ฌ์šฉํ•ด์„œ UI์— image ํ‘œ์‹œ
cell.cardImageView.kf.setImage(with: imageURL)

๐Ÿ”ท Lottie library

Lottie-ios Github - https://github.com/airbnb/lottie-ios

  • Lottie ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฐฑํ„ฐ ๊ธฐ๋ฐ˜ ์• ๋‹ˆ๋ฉ”๋ฏธ์…˜๊ณผ ์•„ํŠธ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋žœ๋”๋งํ•˜๋Š” Airbnb ์—์„œ ๊ฐœ๋ฐœํ•œ ์˜คํ”ˆ ์†Œ์Šค ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž…๋‹ˆ๋‹ค

  • Lottie ๋ฅผ bodymovin JSON ํ˜•์‹์œผ๋กœ ๋ณด๋‚ธ ์—๋ฏธ๋ฉ”์ด์…˜์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค

์„ค์น˜

  # Pods for 09_creditCardList
  pod 'lottie-ios'
end

pod install

์‚ฌ์šฉ๋ฒ•

  • storyBoard ์—์„œ ์—๋‹ˆ๋ฉ”์ด์…˜์ด ๋ณด์—ฌ ์ง€๋Š” ๊ณณ์— view object ๋ฅผ ์ง€์ •ํ•˜๊ณ , class ์„ค์ •์„ AnimationView ์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค

image

// in CardDetailViewController.swift

override func viewDidLoad() {
  super.viewDidLoad()

  // lottie animation ์„ ์–ธ (name ์€ lottie ๋กœ ๋ถˆ๋Ÿฌ์˜ฌ json ํŒŒ์ผ ์ด๋ฆ„์œผ๋กœ)
  let animationView = AnimationView(name: "card")
  lottieView.contentMode = .scaleAspectFit // ์ด๋ฏธ์ง€ container ์‚ฌ์ด์ฆˆ์— ๋งž์ถ”๊ธฐ
  lottieView.addSubview(animationView)
  animationView.frame = lottieView.bounds
  animationView.loopMode = .loop // animation ์ด ๊ณ„์† ๋ฐ˜๋ณต
  animationView.play() // animation ์‹œ์ž‘
}

Kapture 2021-12-24 at 14 13 33

๐Ÿ”‘ Check Point !

๐Ÿ”ท UI Structure

image

  • CardListCell ์€ .xib ํŒŒ์ผ๋กœ ์ž‘์„ฑ

image

image

๐Ÿ”ท Model

import Foundation

struct CreditCard: Codable {
	let id: Int
	let rank: Int
	let name: String
	let cardImageURL: String
	let promotionDetail: PromotionDetail
	let isSelected: Bool? // ์‚ฌ์šฉ์ž๊ฐ€ ์นด๋“œ๋ฅผ ์„ ํƒ ํ–ˆ์„ ๋•Œ, ์ƒ์„ฑ์ด ๋จ ๊ทธ์ „์—๋Š” nil ์ด๋‹ˆ๊นŒ optional ์„ค์ •
}

struct PromotionDetail: Codable {
	let companyName: String
	let amount: Int
	let period: String
	let benefitDate: String
	let benefitDetail: String
	let benefitCondition: String
	let condition: String
}

๐Ÿ”ท Firebase Firestore

Firebase Firestore ์„ค์น˜

Get started with Cloud Firestore - https://firebase.google.com/docs/firestore/quickstart#ios+

  # Pods for 09_creditCardList
  pod 'Firebase/Firestore'
  pod 'FirebaseFirestoreSwift'

pod install

Firebase firestore ์— ๋ฐ์ดํ„ฐ ์ž…๋ ฅ

  • firestore ์—๋Š” json ํŒŒ์ผ์„ web console ์„ ํ†ตํ•ด์„œ ํ•œ๋ฒˆ์— ๋ฐ”๋กœ ์ž…๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— code swift ์—์„œ dummy data ๋ฅผ import ํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
// in CreditCardDummy.swift

import Foundation

struct CreditCardDummy {
    static let card0 = CreditCard(id: 0, rank: 1, name: "์‹ ํ•œ์นด๋“œ", cardImageURL: "https://www.shinhancard.com/_ICSFiles/afieldfile/2019/04/26/190426_pc_mrlife_cardplate600x380.png", promotionDetail: PromotionDetail(companyName: "์‹ ํ•œ", period: "2023.01.07(๋ชฉ)~2023.01.31(ํ† )", amount: 13, condition: "์˜จ๋ผ์ธ ์ฑ„๋„์„ ํ†ตํ•ด ์ด๋ฒคํŠธ ์นด๋“œ๋ฅผ ๋ณด์œ ํ•˜๊ณ , ํ˜œํƒ์กฐ๊ฑด์„ ์ถฉ์กฑํ•˜์‹  ๋ถ„", benefitCondition: "์ด๋ฒคํŠธ ์นด๋“œ๋กœ ๊ฒฐ์ œํ•œ ๊ธˆ์•ก์ด ํ•ฉํ•ด์„œ 10๋งŒ์›์ด์ƒ ๊ฒฐ์ œ", benefitDetail: "ํ˜„๊ธˆ 10๋งŒ์›", benefitDate: "2023.03.01(์›”)์ดํ›„"), isSelected: nil)
    static let card1 = CreditCard(id: 1
		......
// in AppDelegate.swift

import FirebaseFirestoreSwift

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
	func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

		// firebase init
		FirebaseApp.configure()

		// firebase db ์„ ์–ธ
		let db = Firestore.firestore()
		// collection ์—์„œ creditCardList ๋ฅผ ์ฐพ๊ณ , snapshot ๊ณผ error ๋ฅผ ๋ถˆ๋Ÿฌ์˜ด(ํ•ด๋‹น db์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ์— ํ•œ๋ฒˆ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด ์ฃผ๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ)
		db.collection("creditCardList").getDocuments { snapshot, _ in
			guard snapshot?.isEmpty == true else { return } // snapshot ์œผ๋กœ db๊ฐ€ ๋น„์–ด ์žˆ๋Š” ์ƒํƒœ์—์„œ๋งŒ ture ๋กœ ์„ค์ •
			let batch = db.batch()

			let card0Ref = db.collection("creditCardList").document("card0")
			let card1Ref = db.collection("creditCardList").document("card1")
			let card2Ref = db.collection("creditCardList").document("card2")
			let card3Ref = db.collection("creditCardList").document("card3")
			let card4Ref = db.collection("creditCardList").document("card4")
			let card5Ref = db.collection("creditCardList").document("card5")
			let card6Ref = db.collection("creditCardList").document("card6")
			let card7Ref = db.collection("creditCardList").document("card7")
			let card8Ref = db.collection("creditCardList").document("card8")
			let card9Ref = db.collection("creditCardList").document("card9")

			do {
				try batch.setData(from: CreditCardDummy.card0, forDocument: card0Ref)
				try batch.setData(from: CreditCardDummy.card1, forDocument: card1Ref)
				try batch.setData(from: CreditCardDummy.card2, forDocument: card2Ref)
				try batch.setData(from: CreditCardDummy.card3, forDocument: card3Ref)
				try batch.setData(from: CreditCardDummy.card4, forDocument: card4Ref)
				try batch.setData(from: CreditCardDummy.card5, forDocument: card5Ref)
				try batch.setData(from: CreditCardDummy.card6, forDocument: card6Ref)
				try batch.setData(from: CreditCardDummy.card7, forDocument: card7Ref)
				try batch.setData(from: CreditCardDummy.card8, forDocument: card8Ref)
				try batch.setData(from: CreditCardDummy.card9, forDocument: card9Ref)
			} catch let error {
				print("ERROR: wirting card to Firestore \(error.localizedDescription)")
			}
			// batch ์— commit ์„ ํ•ด์ฃผ์–ด์•ผ์ง€ data ๊ฐ€ ์ถ”๊ฐ€๊ฐ€ ๋จ
			batch.commit()
		}
		return true
	}
  • app build ํ›„์— firestore ์—์„œ data ๊ฐ€ import ๋œ ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

image

firebase Firestore ์ฝ๊ธฐ
import UIKit
import Kingfisher
import FirebaseFirestore



// UITableViewController ๋Š” UITableView ์— ํ•„์š”ํ•œ delegate source ๋ฅผ ๊ธฐ๋ณธ ์—ฐ๊ฒฐ๋œ ์ƒํƒœ๋กœ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„๋กœ delegate ์„ ์–ธ์„ ํ•˜์ง€ ์•Š์•„๋„ ๋จ
// ๋˜, rootView ๋กœ UItableView ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค
class CardListViewController: UITableViewController {

	// DB ์„ ์–ธ
	var db = Firestore.firestore()

	// MARK: Variable
	var creditCardList: [CreditCard] = []

	// MARK: LifeCycle
	override func viewDidLoad() {
		super.viewDidLoad()

		// UITabelView Cell Register
		let nibName = UINib(nibName: "CardListCell", bundle: nil)
		tableView.register(nibName, forCellReuseIdentifier: "CardListCell")

		// firestore ์ฝ๊ธฐ code ์ถ”๊ฐ€
		db.collection("creditCardList").addSnapshotListener { snapshot , error in
			guard let documents = snapshot?.documents else {
				// ๊ฐ’์ด ์—†์„ ๊ฒฝ์šฐ์— error ์ฒ˜๋ฆฌ
				print("ERROR Firesotre fetching document \(String(describing: error))")
				return
			}
			// ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ : compactMap ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ nil ๊ฐ’์„ ๋ฐฐ์—ด ์•ˆ์— ๋„ฃ์ง€ ์•Š๊ฒŒ ํ•˜์ง€ ์œ„ํ•ด์„œ
			self.creditCardList = documents.compactMap { doc -> CreditCard? in
				do {
					let jsonData = try JSONSerialization.data(withJSONObject: doc.data(), options: [])
					let creditCard = try JSONDecoder().decode(CreditCard.self, from: jsonData)
					return creditCard
				} catch let error {
					print("ERROR JSON Parsing \(error)")
					return nil
				}
			}.sorted { $0.rank < $1.rank }

			// main tread ์—์„œ ๋Œ์•„๊ฐ€๋Š” tableView reload
			DispatchQueue.main.async {
				self.tableView.reloadData()
			}
		}
	}
}

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

firebase Firestore ์“ฐ๊ธฐ
  • struct model ์— ์žˆ๋Š” isSelected: Bool? ์„ ๊ฐ‘์„ ํ†ตํ•ด ์„ ํƒ๋˜๋ฉด select ๊ฐ€ ๋˜๊ฒŒ๋” firestore ์— ๊ฐ’ ์ž…๋ ฅํ•˜๊ธฐ ์ž…๋‹ˆ๋‹ค

  • ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋ฅผ ์•Œ๋•Œ์™€ ๋ชจ๋ฅผ๋•Œ ๋‘๊ฐ€์ง€ ๊ฒฝ์šฐ์— ์ˆ˜์— ๋”ฐ๋ผ code ๋ฐฉ์‹์ด ๋‹ค๋ฆ„

// in CardListViewController.swift

// didSelectRowAt: cell ์„ ์„ ํƒ ํ–ˆ์„๋•Œ, CardDetailViewController ๋กœ ๋„˜์–ด๊ฐ€๋Š” action
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
	// ์ƒ์„ธํ™”๋ฉด ์ „๋‹ฌ
	let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
	guard let detailViewController = storyboard.instantiateViewController(withIdentifier: "CardDetailViewController") as? CardDetailViewController else { return }
	detailViewController.promotionDetail = creditCardList[indexPath.row].promotionDetail
	self.show(detailViewController, sender: nil)

	// Firestore ๋ฐ์ดํ„ฐ ์“ฐ๊ธฐ
	// option1 : ๊ฒฝ๋กœ๋ฅผ ์•Œ๊ณ  ์žˆ์„ ๊ฒฝ์šฐ
	let cardID = creditCardList[indexPath.row].id
	db.collection("creditCardList").document("card\(cardID)").updateData(["isSelected": true])

	// option2: ๊ฒฝ๋กœ๋ฅผ ๋ชจ๋ฅด๊ณ  ์žˆ์„ ๊ฒฝ์šฐ
	// id ๊ฐ’์„ ๊ฒ€์ƒ‰ํ•œ๋‹ค์Œ์— ๊ทธ ๊ฒฐ๊ณผ๋กœ ์ฐพ์€ ๋ฌธ์„œ์— ์—…๋ฐ์ดํŠธ ํ•ด์ค˜์•ผํ•จ
	db.collection("creditCardList").whereField("id", isEqualTo: cardID).getDocuments { snapshot, _ in
		guard let document = snapshot?.documents.first else {
			// error ์ฒ˜๋ฆฌ
			print("ERROR Firestore fetching document")
			return
		}
		// cardID ๊ฐ€ ์žˆ๋‹ค๋ฉด
		document.reference.updateData(["isSelected": true])
	}
}

ํ•ญ๋ชฉ์„ ํด๋ฆญํ•œ ๊ฐ’์— data field ์— isSelected: true ๊ฐ€ ์ƒ์„ฑ๋จ์„ ํ™•์ธ

image

firebase Firestore ์‚ญ์ œ
// in CardListViewController.swift

// forRowAt: cell delete
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
	if editingStyle == .delete {

		// firestore ์˜ ์‚ญ์ œ
		// Option 1: ๊ฒฝ๋กœ๋ฅผ ์•Œ๊ณ  ์žˆ์„๋•Œ
		let cardID = creditCardList[indexPath.row].id
		// db.collection("creditCardList").document("card\(cardID)").delete()

		// Option 2: ๊ฒฝ๋กœ๋ฅผ ๋ชจ๋ฅด๊ณ  ์žˆ์„๋•Œ : wherefield method ๋ฅผ ํ†ตํ•ด ๋ฌธ์„œ ์ „์ฒด๋ฅผ ๊ฒ€์ƒ‰ํ•œ ํ›„์— snpshot ์„ ์ œ๊ณตํ•จ
		db.collection("creditCardList").whereField("id", isEqualTo: cardID).getDocuments { snapshot, _ in
			guard let document = snapshot?.documents.first else {
				print("ERROR")
				return
			}
			document.reference.delete()
		}
	}
}

๐Ÿ”ท Firebase Realtime DB

  • Realtime DB ๋Š” ๋‹จ์ผ json ํŒŒ์ผ์„ ๊ด€๋ฆฌ ํ•˜๊ธฐ์— ์šฉ์˜ํ•œ DB ์ž…๋‹ˆ๋‹ค (json import ๊ธฐ๋Šฅ ์ง€์›)

Firebase Realtime DB ์„ค์น˜

  # Pods for 09_creditCardList
 	pod 'Firebase/Database'

pod install

Firebase Realtime DB ์ฝ๊ธฐ

//  CardListViewController.swift
import FirebaseDatabase

class CardListViewController: UITableViewController {

	var ref: DatabaseReference!  // Firebase Realtime DB ์ฐธ์กฐ ๋ณ€์ˆ˜

// MARK: Firebase Realtime DB READ
/*Firebase Database ์ฝ๊ธฐ*/
self.ref = Database.database().reference()

self.ref.observe(.value) { snapshot in
	guard let value = snapshot.value as? [String: [String: Any]] else { return }
	do {
		let jsonData = try JSONSerialization.data(withJSONObject: value)
		let cardData = try JSONDecoder().decode([String: CreditCard].self, from: jsonData)
		let cardList = Array(cardData.values)
		self.creditCardList = cardList.sorted { $0.rank < $1.rank }

		DispatchQueue.main.async {
			self.tableView.reloadData()
		}
	} catch let error {
		print("Error json parsing \(error)")
	}
}

image

Firebase Realtime DB ์“ฐ๊ธฐ

// in CardListViewController.swift

// MARK: Firebase realtime DB Write
		 let cardID = creditCardList[indexPath.row].id
		 //option1: ๊ฒฝ๋กœ๋ฅผ ์•„๋Š” ๊ฒฝ์šฐ์— ์“ฐ๊ธฐ
		 self.ref.child("Item\(cardID)/isSelected").setValue(true)
		 //option2: ๊ฒฝ๋กœ๋ฅผ ๋ชจ๋ฅด๋Š” ๊ฒฝ์šฐ
		 self.ref.queryOrdered(byChild: "id").queryEqual(toValue: cardID).observe(.value) {[weak self] snapshot in
		 guard let self = self,
		 let value = snapshot.value as? [String: [String: Any]],
		 let key = value.keys.first else { return }

		 self.ref.child("\(key)/isSelected").setValue(true)
		 }

Firebase Realtime DB ์‚ญ์ œ

	// forRowAt: cell delete
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
	if editingStyle == .delete {

	// MARK: Firebase realtime DB Delete
		let cardID = creditCardList[indexPath.row].id
		self.ref.queryOrdered(byChild: "id").queryEqual(toValue: cardID).observe(.value) {[weak self] snapshot in
		guard let self = self,
		let value = snapshot.value as? [String: [String: Any]],
		let key = value.keys.first else { return }

		self.ref.child(key).removeValue()
		}
		...

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


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

๐Ÿ—ƒ Reference

Jacob's DevLog - https://jacobko.info/firebaseios/ios-firebase-02/

LEEO TIL Dev Log - https://dev200ok.blogspot.com/2020/09/ios-kingfisher.html

iOS์—์„œ Lottie ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘ํ•˜๊ธฐ - https://ichi.pro/ko/ioseseo-lottie-aenimeisyeon-sijaghagi-29592323663035

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

creditcard-ios-practice's People

Contributors

jacobkosmart avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.