【SwiftUI】Likeボタンタップ時のLike数増減とFirebaseの更新機能の実装


# はじめに
SwiftUIでカード内のLikeボタンを押した場合にLike数を+1し、再度押した場合に-1する機能を実装することと、その場合にFirebaseのデータベースを更新できるようにすることを目的とする。

今回はカード内のLikeボタンの実装を行います。

前回までの記事は以下を参考ください。

**参考記事**
[【SwiftUI】Likeボタンとリスト内セルにボタンを実装する場合の注意点](https://qiita.com/m37335/items/66f59135e98c2b206a36)

# 開発環境
OSX 10.15.7 (Catalina) 
Xcode 12.2.0
CocoaPods 1.10.0

## 実装したコード
![ContentRowView_swift.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/867981/b0ef64d3-6287-b772-d9c2-3c24be9f755f.png)

**主な変更点**
**・Likeボタン内にif文を設定**
→ボタンを押した場合はLike数を+1、再度押した場合は-1になるようにしました。また、押した場合はFirebaseのデータベースが更新されるようにしています。

```swift
struct ContentRowView: View {
    var id  = ""
    var user = ""
...略...
    static let formatter = RelativeDateTimeFormatter()
    
    // 以下を追記
    @State var isLiked = false
    
    var body: some View {
        
...略...

            // Likes Number
            HStack {
                Image(systemName: isLiked ? "heart.fill": "heart").font(.headline).foregroundColor(Color("pinkColor"))
                Text(likes).font(.headline).foregroundColor(Color("pinkColor"))
                Spacer()
            }.padding(.top, 5)

            // Info
            HStack {
...略...
                
                // Like Button push Image to add like's Number.
                Image(systemName: isLiked ? "heart.fill": "heart")
                    .font(.title)
                    .foregroundColor(Color("pinkColor"))
                    .onTapGesture {
                        self.isLiked.toggle()
                        if isLiked == true {
                            // push button
                            print("true. likes+1")
                            // update likes...
                                                
                            let db = Firestore.firestore()
                           
                            let like = Int.init(self.likes)!
                            db.collection("ikku").document(id).updateData(["likes": "\(like + 1)"]) { (err) in
                                
                                if err != nil{
                                    
                                    print((err) as Any)
                                    return
                                }
                                print("updated....")
                            }
          
                        } else {
                            // And push Button
                            print("false. likes-1")
                            // update likes...
                                                
                            let db = Firestore.firestore()
                           
                            let like = Int.init(self.likes)!
                            db.collection("ikku").document(id).updateData(["likes": "\(like - 1)"]) { (err) in
                                
                                if err != nil{
                                    
                                    print((err) as Any)
                                    return
                                }
                                
                                print("updated....")
                            }
                        }
                    }
                    .frame(width: 30, height: 30)
            }.padding(5)
        }.padding(.top, 8).frame(height: 391)
    }
}
```

**・getDataクラス内に```.modified```を実装**
→Likeボタンを押すと、Like数も変更するようにしました。実装にあたって、ボタンを押すと、Like数左にあるハートマークも変更できるようにしています。

```swift```
class getData: ObservableObject {
    
    @Published var datas = [dataType]()
    
    init() {
        
        let db = Firestore.firestore()
        
        db.collection("ikku").order(by: "now", descending: true).addSnapshotListener{ (snap, err) in
            
            if err != nil{
                
                print((err?.localizedDescription)!)
                return
            }
            
            for i in snap!.documentChanges{
                
                if i.type == .added{
                    
                    let id = i.document.documentID
                    let user = i.document.get("userName") as! String
                    let haiku = i.document.get("haiku") as! String
                    let place = i.document.get("place") as! String
                    let likes = i.document.get("likes") as! String
                    let mapImage = i.document.get("mapImage") as! String
                    let userImage = i.document.get("userImage") as! String
                    
                    let now = i.document.get("now") as! Timestamp
                    let stamp = i.document.get("createdDate") as! Timestamp
                    
                    let formatterDate = DateFormatter()
                    formatterDate.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
                    let createdDate = formatterDate.string(from: stamp.dateValue())
                    
                    let isLiked = false
                    
                    DispatchQueue.main.async {
                        self.datas.append(dataType(id: id, user: user, haiku: haiku, place: place, likes: likes, mapImage: mapImage, userImage: userImage, stamp: stamp.dateValue(), createdDate: createdDate, now: now.dateValue(), isLiked: isLiked ))
                        
                    }
                }
                
                // 以下を追加
                if i.type == .removed{
                    
                    let id = i.document.documentID
                    
                    for j in 0 ..< self.datas.count {
                        
                        if self.datas[j].id == id{
                            
                            self.datas.remove(at: j)
                            return
                        }
                    }
                }
                
                if i.type == .modified{
                    
                    let id = i.document.documentID
                    let likes = i.document.get("likes") as! String
                    
                    for j in 0 ..< self.datas.count {
                        
                        if self.datas[j].id == id{
                            
                            self.datas[j].likes = likes
                            return
                        }
                    }
                }
            }
        }
    }
}

```

## 実装後のイメージ
**タップ前**
![iPhone_11_–_14_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/867981/d9407b87-9bcb-c565-f616-44f6262d6ed9.png)

![Haiku_-_Cloud_Firestore_-_Firebase_コンソール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/867981/35ff3eca-9113-f595-b8af-a979610109f1.png)

**タップ後**
![iPhone_11_–_14_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/867981/9e779848-76cd-8fe0-1f89-7e12dbf12a8f.png)

![Haiku_-_Cloud_Firestore_-_Firebase_コンソール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/867981/7e36ebf7-b6a9-760b-7133-4a6f379c54b0.png)

再度、タップするとFirebaseのLike数は-1になり、Likeの見た目が元に戻ります。

## ハートをアニメーションで動かす
Likeボタンをタップした場合に、ハートにアニメーションがかかるようにしました。

###実装方法
![ezgif-7-f0350d0318d1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/867981/713a1ebd-ff12-21b9-db1e-63ef32c7d60a.gif)


```swift
import SwiftUI

struct ContentView: View {
    
    @State private var isLiked = false
    
    var body: some View {
        VStack {
            Text(isLiked ? "Liked!" : "unLiked!")
            HeartButton(isLiked: $isLiked)
        }
    }
}

struct HeartButton: View {
    
    @Binding var isLiked : Bool
    
    private let animationDuration : Double = 0.1
    private var animationScale : CGFloat {
        isLiked ? 0.7 : 1.3
    }
    
    @State private var animate = false
    
    var body: some View {
        
        Button(action: {
            self.animate = true
            
            DispatchQueue.main.asyncAfter(deadline: .now() + self.animationDuration, execute: {
                self.animate = false
                self.isLiked.toggle()
            })
            
            
            if isLiked == true {
                // push button
                print("true")
            } else {
                // And push Button
                print("false")
            }
        }, label: {
            Image(systemName: isLiked ? "heart.fill": "heart")
                .font(.largeTitle)
                .foregroundColor(Color.red)
                .frame(width: 100, height: 100)
        })
        .scaleEffect(animate ? animationScale : 1)
        .animation(.easeIn(duration: animationDuration))
    }
}
```

###実際に実装した後のイメージ
かわいい感じになりました。
![ezgif-7-eca8634e67ec.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/867981/193a31c7-c918-c8ed-54f4-2946ddc4ac6d.gif)

以上です。