Swift - FireBaseStore,ActiveLabelを用いて、ハッシュタグ機能を作成する

# この記事の目標
ハッシュタグ機能を持ったアプリを作成する
#前提条件

Xcode : 12.5
Swift : 5.4

##使用したCocoaPods

```
pod 'Firebase'
pod 'Firebase/Firestore'
pod 'ActiveLabel' 
```

## 前準備
前準備として、入力した文字列を投稿し、投稿した文字列が一覧で表示されるアプリケーションを作成します。
データベースには、FireStore Databaseを使用しています。
以下の画像が、イメージ画像です。

![スクリーンショット 2021-06-04 17.12.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911580/ca001e20-c64c-c5b8-febe-5e8895fbc341.png)

コードは、以下のようになっています。

```swift:ViewController.swift
import UIKit
import FirebaseFirestore

class ViewController: UIViewController {
    
    //文字列を入力するtextFieldに紐づいています。
    @IBOutlet weak var textField: UITextField!
    
    //投稿するボタンに紐づいています。
    @IBAction func send(_ sender: Any) {
        //入力された文字列をfireStoreのデータベースに保存します。
        Firestore.firestore().collection("collection").document().setData(["text" : textField.text!])
        
        //データを保存したら、画面遷移を行います。
        let tableVC = storyboard?.instantiateViewController(identifier: "tableVC") as! TableViewController
        navigationController?.pushViewController(tableVC, animated: true)
        
    }
}

```

```swift:TableViewController.swift
import UIKit
import FirebaseFirestore

class TableViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
    
    //取得したデータが入る配列
    var textArray = [String]()
    
    //StoryBoard上のtableViewと紐づけています。
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //プロトコルの委任
        tableView.delegate = self
        tableView.dataSource = self
        
        //データの取得、およびtableViewを読み込むメソッドを使用
        loadData()
        
    }
    
    //tableのセルの数は、取得するデータ数に設定
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        textArray.count
    }
    
    //取得してきたデータを、セル内のラベル（textLabelに反映
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        //storyBoard上のセルと、紐付けを行なっています。
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let textLabel = cell.contentView.viewWithTag(1) as! UILabel
        
        //取得してきた文字列をセルに反映しています。
        textLabel.text = textArray[indexPath.row]
        
        return cell
    }
    
    //セルの高さは、適当に設定しています。
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return view.frame.size.height / 10
    }
    
    //セクションの数は1です
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    
    //fireStoreから、データを取得する関数
    func loadData(){
        
        Firestore.firestore().collection("collection").addSnapshotListener { snapShot, error in
            
            if let snapShotDoc = snapShot?.documents{
                
                for doc in snapShotDoc {
                    
                    let data = doc.data()
                    
                    if let text = data["text"] as? String {
                        print(text)
                        //取得してきたデータを、配列に入れていく。
                        self.textArray.append(text)
                    }
                }
                //データを取得し終わったら、tableViewを更新する。
                self.tableView.reloadData()
            }
        }
    }
}
```

# 本題　ハッシュタグの実装！

ここからハッシュタグの実装に移ります。目標は、以下の画像のようにタップしたハッシュタグのデータだけを抽出して画面に表示させるようにします。


![スクリーンショット 2021-06-04 20.42.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911580/01f24b72-d6a4-5ca3-183e-1ae51e4e34eb.png)


## 手順１　データベースに、ハッシュタグ別のデータを保存する。

ハッシュタグの投稿一覧を表示させるためには、選択されたハッシュタグに紐づいた投稿だけをデータベースから持ってこなければなりません。まずはそのために、ハッシュタグ別で管理されたデータをデータベースに格納する処理を記述します。

```swift:ViewController.swift

import UIKit
import FirebaseFirestore

class ViewController: UIViewController {
    
    //文字列を入力するtextFieldに紐づいています。
    @IBOutlet weak var textField: UITextField!
    
    //投稿するボタンに紐づいています。
    @IBAction func send(_ sender: Any) {
        //入力された文字列を引数に、下記に記述したメソッドを呼んでいます。
        Firestore.firestore().collection("collection").document().setData(["text" : textField.text!])
        
        //追加部分 ==================================================================
        let hashTagText = textField.text as NSString?
        
        do{
            let regex = try NSRegularExpression(pattern: "#\\S+", options: [])
            //見つけたハッシュタグを、for文で回す。
            for match in regex.matches(in: hashTagText! as String, options: [], range: NSRange(location: 0, length: hashTagText!.length)) {
                
                //見つかったハッシュタグを引数に入れデータベースに保存
                Firestore.firestore().collection(hashTagText!.substring(with: match.range)).document().setData(["text" : self.textField.text!])
            }
        }catch{
        }
        //追加部分 ==================================================================
        
        //データを保存したら、画面遷移を行います。
        let tableVC = storyboard?.instantiateViewController(identifier: "tableVC") as! TableViewController
        navigationController?.pushViewController(tableVC, animated: true)
    }

```
以上の記述で、入力された文字列の中に、ハッシュタグがあった場合は、ハッシュタグの数だけ別のコレクションでデータベースに保存ができるようになりました。FireStoreの中身は、下記のようになり、ちゃんとハッシュタグ別でデータが格納されていることがわかります。

![スクリーンショット 2021-06-04 18.21.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911580/5564b630-e1d6-9ea6-4dd1-1c6a56e62dc2.png)

## 手順2 ハッシュタグをタップしたら、ハッシュタグ別一覧画面に遷移できるようにする。

手順２では、以下のことを行います。

・一覧画面に表示されるハッシュタグを、タップできるようにする
・ハッシュタグをタップしたら、ハッシュタグ別一覧ページに画面遷移を行う。

まずは、遷移先のHashTagViewControllerを作成します。
この時点では、画面遷移元のTableViewControllerから、ハッシュタグの情報を取得するためのインスタンスを作成しておきます。

```swift:HashTagViewController.swift
import UIKit

class HashTagViewController: UIViewController {

    //TableViewControllerから、タップしたハッシュタグの情報を取得するため
    var hashTag = String()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
```

次に、ハッシュタグをタップしたら、ハッシュタグ別一覧ページに画面遷移を行う記述をします。
UILabelにハッシュタグを認識させるために、「ActiveLabel」というライブラリを使用します。

```swift:TableViewController.swift
import UIKit
import FirebaseFirestore
import ActiveLabel   //ライブラリを追加

・
・
・

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        //storyBoard上のセルと、紐付けを行なっています。
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let textLabel = cell.contentView.viewWithTag(1) as! ActiveLabel   // ActiveLabelに変更する
        
        //取得してきた文字列をセルに反映しています。
        textLabel.text = textArray[indexPath.row]
        
        //ハッシュタグをタップしたらどうなるかの処理を追加記述 ==============================
        
        textLabel.handleHashtagTap { hashTag in
            
            //storyBoard上のhashTagViewControllerをインスタンス化
            let hashVC = self.storyboard?.instantiateViewController(identifier: "hashVC") as! HashTagViewController
            
            //hashVCへ、タップしたハッシュタグの情報を渡す
            hashVC.hashTag = hashTag
            
            //画面遷移
            self.navigationController?.pushViewController(hashVC, animated: true)
        }
        
        //ハッシュタグをタップしたらどうなるかの処理を追加記述 ==============================
        
        return cell
    }

```

## 手順３ ハッシュタグ別に投稿一覧を表示するページを作成する

手順3では、以下の処理を記述します。

・タップされたハッシュタグの情報をもとに、fireStoreからデータを抽出する
・抽出したデータを、HashTagViewControllerに表示させる
・HashTagViewController上でハッシュタグをタップした場合は、表示内容を更新する。

記述は、以下のようになります。

```swift:TableViewController.swift
import UIKit
import ActiveLabel
import FirebaseFirestore

class HashTagViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource {

    //TableViewControllerから、タップしたハッシュタグの情報を取得するため
    var hashTag = String()
    var textArray = [String]()
    
    @IBOutlet weak var tableView: UITableView!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //プロトコルの委任
        tableView.delegate = self
        tableView.dataSource = self
        
        //取得したハッシュタグのデータをもとに、fireStoreからデータを取得し画面に反映させる。
        loadData(hashTag: hashTag)
    }
    
    //tableのセルの数は、取得するデータ数に設定
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        textArray.count
    }
    
    //取得してきたデータを、セル内のラベル（textLabelに反映
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        //storyBoard上のセルと、紐付けを行なっています。
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let textLabel = cell.contentView.viewWithTag(1) as! ActiveLabel   // ActiveLabelに変更する
        
        //取得してきた文字列をセルに反映しています。
        textLabel.text = textArray[indexPath.row]
        
        //ハッシュタグをタップしたら、そのハッシュタグに適したデータを再ロードする
        textLabel.handleHashtagTap { hashTag in
            self.loadData(hashTag: hashTag)
        }
        return cell
    }
    
    //セルの高さは、適当に設定しています。
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return view.frame.size.height / 10
    }
    
    //セクションの数は1です
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    
    //fireStoreから、データを取得する関数
    func loadData(hashTag:String){
        
        //collectionの引数をhashTagにすることによって、選択したハッシュタグに関連するデータだけを抽出する。
        Firestore.firestore().collection("#\(hashTag)").addSnapshotListener { snapShot, error in
            
            self.textArray = []
            
            if let snapShotDoc = snapShot?.documents{
                
                for doc in snapShotDoc {
                    
                    let data = doc.data()
                    
                    if let text = data["text"] as? String {
                        print(text)
                        //取得してきたデータを、配列に入れていく。
                        self.textArray.append(text)
                    }
                }
                //データを取得し終わったら、tableViewを更新する。
                self.tableView.reloadData()
                self.navigationItem.title = "#\(hashTag)"
            }
        }
    }
}

```
以上で、記述は終了です。

#最後に

読んでくださりありがとうございました！
もし変な記述や、間違った記述があったらコメントしていただけると幸いです！

#参考資料

[Udemy 【iOS14対応】未経験者がiPhoneアプリ開発者になるための全て iOS Boot Camp](https://www.udemy.com/course/ios14-iphone-ios-boot-camp/)

[ハッシュタグを判別するための記述の際に参考にした記事]
(https://stackoverflow.com/questions/39701316/use-regex-to-match-emojis-as-well-as-text-in-string)