https://github.com/jacobkosmart/creditcard-ios-practice
To practice firebase realtime DB and firestroe CRUD
https://github.com/jacobkosmart/creditcard-ios-practice
firebase firebase-realtime-database firestore-database kingfisher lottie
Last synced: 5 days ago
JSON representation
To practice firebase realtime DB and firestroe CRUD
- Host: GitHub
- URL: https://github.com/jacobkosmart/creditcard-ios-practice
- Owner: jacobkosmart
- Created: 2021-12-27T10:57:20.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2021-12-27T10:57:59.000Z (almost 4 years ago)
- Last Synced: 2024-12-28T03:27:52.968Z (9 months ago)
- Topics: firebase, firebase-realtime-database, firestore-database, kingfisher, lottie
- Language: Swift
- Homepage: https://jacobko.info/firebaseios/ios-firebase-02/
- Size: 28.3 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# π³ 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`
```ruby
# Pods for 09_creditCardList
pod 'Kingfisher'
end
````pod install`
#### μ¬μ©λ²
```swift
// 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` νμμΌλ‘ λ³΄λΈ μλ―Έλ©μ΄μ μ μ§μν©λλ€
#### μ€μΉ
```ruby
# Pods for 09_creditCardList
pod 'lottie-ios'
end
````pod install`
### μ¬μ©λ²
- storyBoard μμ μλλ©μ΄μ μ΄ λ³΄μ¬ μ§λ κ³³μ view object λ₯Ό μ§μ νκ³ , class μ€μ μ AnimationView μΌλ‘ μ§μ ν©λλ€

```swift
// in CardDetailViewController.swiftoverride 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 μμ
}
```
## π Check Point !
### π· UI Structure

- CardListCell μ `.xib` νμΌλ‘ μμ±


### π· Model
```swift
import Foundationstruct 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+
```ruby
# Pods for 09_creditCardList
pod 'Firebase/Firestore'
pod 'FirebaseFirestoreSwift'
````pod install`
#### Firebase firestore μ λ°μ΄ν° μ λ ₯
- firestore μλ json νμΌμ web console μ ν΅ν΄μ νλ²μ λ°λ‘ μ λ ₯νλ κΈ°λ₯μ΄ μκΈ° λλ¬Έμ code swift μμ dummy data λ₯Ό import νλ κ³Όμ μ κ±°μ ΈμΌ ν©λλ€.
```swift
// in CreditCardDummy.swiftimport 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
......
``````swift
// in AppDelegate.swiftimport 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 λ κ²μ νμΈ ν μ μμ΅λλ€

##### firebase Firestore μ½κΈ°
```swift
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 λ°©μμ΄ λ€λ¦
```swift
// 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` κ° μμ±λ¨μ νμΈ

##### firebase Firestore μμ
```swift
// 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 μ€μΉ
```ruby
# Pods for 09_creditCardList
pod 'Firebase/Database'
````pod install`
#### Firebase Realtime DB μ½κΈ°
```swift
// CardListViewController.swift
import FirebaseDatabaseclass 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)")
}
}
```
#### Firebase Realtime DB μ°κΈ°
```swift
// 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 μμ
```swift
// 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/](https://jacobko.info/firebaseios/ios-firebase-02/)
LEEO TIL Dev Log - [https://dev200ok.blogspot.com/2020/09/ios-kingfisher.html](https://dev200ok.blogspot.com/2020/09/ios-kingfisher.html)
iOSμμ Lottie μ λλ©μ΄μ μμνκΈ° - [https://ichi.pro/ko/ioseseo-lottie-aenimeisyeon-sijaghagi-29592323663035](https://ichi.pro/ko/ioseseo-lottie-aenimeisyeon-sijaghagi-29592323663035)
fastcampus - [https://fastcampus.co.kr/dev_online_iosappfinal](https://fastcampus.co.kr/dev_online_iosappfinal)