今回はSNSのアプリをリリースする際に必要となるブロック機能を実装していきたいと思います。
まず初めにブロック機能とは、SNSで閲覧したくないものをブロックすることでタイムライン上に表示されないようにするために実装します。
サンプルとしてNCMBを用いて取得したユーザーのオブジェクトIDをブロックする対象とします。
ここでは、NCMBのデータストアにブロッククラスを作成し、タイムライン上に表示させたくない投稿の投稿者のオブジェクトIdをアップロードしブロック機能を実装します。
前提として、SNSアプリ報告機能の実装は完了しているものとし、ViewControllerのタイムラインTableViewのカスタムセルのデリゲートメソッドの一つとしてブロック機能を実装しています。
完成サンプル
一番下にコードの全容があるので、コピぺしたかったらそこまで飛んでつけてみてください!
目次
1. ブロック機能の理論の話
ブロック機能は
→1.非表示にしたいユーザーをブロックする作業
→2.ブロックしたユーザーの投稿を非表示にする作業
の2つの手順に分けることができます!
2. 作り方の説明
1.非表示にしたいユーザーをブロックする作業
→1.1タイムラインのViewControlerでアラートコントローラーのアクションの設置→→ViewController
→1.2ブロック機能のコード→→ViewController
2.ブロックしたユーザーの投稿を非表示にする機能の仕組み
####初期状態
func didTapMenuButton(targetCell tableViewCell: UITableViewCell, targetButton button: UIButton) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { (action) in
alertController.dismiss(animated: true, completion: nil)
}
//selctedPostUserは選択してる投稿の投稿者を指しています。
let selectedPostUser = posts[tableViewCell.tag].object(forKey: "user") as! NCMBUser
//投稿者がログインユーザーか否かを条件に分岐している
if selectedPostUser.object(forKey: "objectId") as! String == NCMBUser.current()?.objectId{
// ログインユーザーの投稿を削除する時の一連の流れ
let deleteAction = UIAlertAction(title: "削除する", style: .destructive) { (action) in
//削除機能のコード
}
alertController.addAction(deleteAction)
alertController.addAction(cancelAction)
}else{
//ログインユーザーの投稿でない場合のながれ
let reportAction = UIAlertAction(title: "報告する", style: .destructive) { (action) in
//報告機能のコード
}
alertController.addAction(reportAction)
alertController.addAction(cancelAction)
}
self.present(alertController, animated: true, completion: nil)
}
####1. 非表示にしたいユーザーをブロックする作業
1.1アラートコントローラーにアクションの設置
ここではdidTapMenuButton関数の内容を記述します。 今回はアラートコントローラーを設置し、報告機能や削除機能のようにアクションの一つとしてブロック機能を実装します。
そのため操作するのはViewController
です。
let blockAction = UIAlertAction(title: "ブロックする", style: .default) { (action) in
HUD.show(.progress, onView: self.view)
//NCMBのBlockクラスにオブジェクトを作成する
let object = NCMBObject(className: "Block")
//"blockUserId"をキーとしてブロックされる側(投稿者)のオブジェクトIDを格納
let user = self.posts[tableViewCell.tag].object(forKey: "user") as! NCMBUser
object?.setObject(user.objectId, forKey: "blockUserId")
//"user"をキーとしてブロックする側(現在ログインしているユーザー)をNCMBUser型として格納
object?.setObject(NCMBUser.current(), forKey: "user")
//NCMB上にオブジェクトを保存
object?.saveInBackground({ (error) in
if error != nil {
HUD.show(.progress, onView: self.view)
} else {
HUD.hide(animated: true)
//ここで③を読み込んでいる
//self.getBlockUser()
}
})
}
上記のブロックアクションのコードをdidTapMenuButton関数のログインユーザーの投稿でない場合の流れの中に記述します。
1.2ブロック機能のコード
func didTapMenuButton(targetCell tableViewCell: UITableViewCell, targetButton button: UIButton) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { (action) in
alertController.dismiss(animated: true, completion: nil)
}
//selctedPostUserは選択してる投稿の投稿者を指しています。
let selectedPostUser = posts[tableViewCell.tag].object(forKey: "user") as! NCMBUser
//投稿者がログインユーザーか否かを条件に分岐している
if selectedPostUser.object(forKey: "objectId") as! String == NCMBUser.current()?.objectId{
// ログインユーザーの投稿を削除する時の一連の流れ(略)
}else{
//ログインユーザーの投稿でない場合のながれ
let reportAction = UIAlertAction(title: "報告する", style: .destructive) { (action) in
//報告機能のコード
}
let blockAction = UIAlertAction(title: "ブロックする", style: .default) { (action) in
HUD.show(.progress, onView: self.view)
//NCMBのBlockクラスにオブジェクトを作成する
let object = NCMBObject(className: "Block")
//"blockUserId"をキーとしてブロックされる側(投稿者)のオブジェクトIDを格納
let user = self.posts[tableViewCell.tag].object(forKey: "user") as! NCMBUser
object?.setObject(user.objectId, forKey: "blockUserId")
//"user"をキーとしてブロックする側(現在ログインしているユーザー)をNCMBUser型として格納
object?.setObject(NCMBUser.current(), forKey: "user")
//NCMB上にオブジェクトを保存
object?.saveInBackground({ (error) in
if error != nil {
HUD.show(.progress, onView: self.view)
} else {
HUD.hide(animated: true)
//ここの後で記述する関数です。
self.getBlockUser()
}
})
}
alertController.addAction(reportAction)
//alertControllerのアクションとして追加
alertController.addAction(blockAction)
alertController.addAction(cancelAction)
}
self.present(alertController, animated: true, completion: nil)
}
以上で非表示にしたいユーザーをブロックする作業のコードは完了です。
###2.ブロックしたユーザーの投稿を非表示にする作業
ブロックしたユーザーをタイムラインから非表示にする方法は大きく分けて二つの手順があります。
一つ目に、下記画像の①
のNCMBからブロックしているユーザーの情報を取得する機能
二つ目に、下記画像の②
のNCMBからタイムラインに表示させる投稿情報を取得するときに、ブロックしているユーザーをはじく機能です。
実際にコードを書いていきましょう^-^
①NCMBからブロックしているユーザーの情報を取得する機能
ここではgetBlockUser()
という関数を定義し、viewWillAppear
の中で呼び出します。そうすることでこのViewController{{が表示されるたびに、ログインユーザーがブロックしているユーザーのオブジェクトIDを
blockUserIdArray``という配列に格納します。
(今回の機能に関係のないコードは省いています。)
import UIKit
import NCMB
import PKHUD
import Kingfisher
import SwiftDate
class ViewController: UIViewController, UITableViewDataSource,UITableViewDelegate,TimeLineTableViewCellDelegate{
var followings = [NCMBUser]()
//ブロックしているユーザーのオブジェクトIDを格納する配列を宣言
var blockUserIdArray = [String]()
var posts = [NCMBObject]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
getBlockUser()
loadFollowingUsers()
}
//ログインユーザーがブロックしているユーザーのオブジェクトIDを格納する関数
func getBlockUser(){
//NCMBのBlockクラスのクエリを宣言
let query = NCMBQuery(className: "Block")
//検索結果にuserカラムの情報を含める
query?.includeKey("user")
//userカラムがログインユーザーと一致するレコードを取得
query?.whereKey("user", equalTo: NCMBUser.current())
query?.findObjectsInBackground({ (result, error) in
if error != nil{
//もし保存する際にエラーがあったらその地域の言葉でエラーの内容を表示させます。
HUD.flash(.labeledError(title: error?.localizedDescription, subtitle: nil), delay: 1)
}else{
//removeAll()で初期化をし、データの重複を防ぐ
self.blockUserIdArray.removeAll()
for blockObject in result as! [NCMBObject]{
//blockUserIdArrayに取得したレコードのblockUserIdカラムの要素のみを加える
self.blockUserIdArray.append(blockObject.object(forKey: "blockUserId") as! String)
}
//タイムラインを読み込みする
self.loadTimeline()
}
})
}
}
②表示する投稿情報からブロックしているユーザーをはじく機能
ブロックしているユーザーを表示する投稿情報から弾くコードはNCMBから情報を取得するReadの関数(今回は、loadTimeline()
)に記述します。
クエリによる検索結果を表示する投稿情報の配列(今回はposts
)に加える一歩前で、投稿者のオブジェクトIDがブロックしているユーザーIDの配列に含まれていなければ、表示する配列に加えるというコードを記述します。
func loadTimeline(){
//オートログアウト
guard let currentUser = NCMBUser.current() else{
//ログアウト成功
let storyboard = UIStoryboard(name: "SignIn",bundle: Bundle.main)
let rootViewController = storyboard.instantiateViewController(withIdentifier: "RootNavigationController")
UIApplication.shared.keyWindow?.rootViewController = rootViewController
//ログイン情報の保持
let ud = UserDefaults.standard
ud.set(true, forKey: "isLogin")
return
}
//"Postクラスから取ってくる"というクエリを宣言する.クエリは仕様書のイメージですね!この仕様書をもとにデータを取ってくるって感じです。
let query = NCMBQuery(className: "Post")
//日付の降順でとるってことを仕様書に追記してるイメージ
query?.order(byDescending: "createDate")
// 投稿したユーザーの情報も同時取得することを仕様書に追記してるイメージです。
query?.includeKey("user")
//以上の仕様書を元にデータを取ってきます。無事取れたらresultという変数に結果(Any型配列)が入る。失敗したらerrorという変数にエラー内容(NSError型)が入ります。
query?.findObjectsInBackground({(result,error) in
if error != nil{
//エラーだったら、エラーの内容を表示する
print(error)
}else{
//データの取得がうまくいったら、postsっていう別の変数(箱)にデータを移し変えてあげましょう!
for postObject in result as! [NCMBObject]{
let user = postObject.object(forKey: "user") as! NCMBUser
//念のため取れてるかprintして確認できるようにしておきます。
print(user)
print(postObject)
if self.blockUserIdArray.firstIndex(of: user.objectId) == nil {
//postsっていう別の変数(箱)にデータを写変える文章
self.posts.append(postObject)
}
}
//tableViewを再読み込みする
self.timelineTableView.reloadData()
}
})
}
※**if self.blockUserIdArray.firstIndex(of: user.objectId) == nil
に関して
firstIndexとは配列に対して利用できるもので、先頭から要素を検索し、該当する要素のインデックスを返すメソッドです。
そのため、今回の左辺は、blockUserIdArray
(ログインユーザーがブロックしているユーザーのオブジェクトIDの配列)の中で、user.objectId
(投稿者のオブジェクトID)は何番目の要素かというものを示しています。右辺はnil(空)を示します。したがって、該当する要素のインデックスがnilである場合self.posts.append(postObject)
**のコードが実行されるようになり、タイムラインに表示されるようになります。
少し複雑な内容になっているので一度で理解できなかったとしても焦らず、それぞれの変数が何を表しているのかを考えながらなんども読み込んでみてください!
うまくいかなかったらメンターに聞いてみてください!
ここで止まったらこの後の教材もうまくいかないままになってしまいます!
不備/疑問点アンケート
教材の不備を見つけた方や、わからなかったこと、もっと詳しく解説してほしいことに関しては、こちらの学習進捗フォームに答えてください!
著者:髙橋直輝