【もう、なくそう。】誹謗中傷を「なくす」アプリの開発

# はじめに
**技術的な内容のみをご覧になりたい方は[こちら](#技術的なポイント)からどうぞ**
2021/11/22追記：アップデートで機能を追加しました。これについては[こちら](https://qiita.com/KokumaruGarasu/items/a9e4b8c3b5ba5ebb42ec)でどうぞ。


[<img = auto src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/713689/70202ca2-4ed5-bb80-2d5f-31bae721a638.png">](https://tokiwaproject.page.link/mute)



**誹謗中傷をネット上から「なくす」アプリ**を開発しました。簡単にいうと、Web上の特定の単語、過激な表現や人を傷つけるような言葉を非表示にするものです。

アプリ開発の動機など、いわゆる「ポエム」よりの内容は[note](https://note.com/izumo092/n/ne17f44c9fda4)に掲載しているので、Qiitaでは機能や技術面に絞ってお話ししたいと思います。

**[ダウンロードはこちらから](https://tokiwaproject.page.link/mute)**

https://apps.apple.com/jp/app/ミュート-やさしいインターネット/id1593160738



# アプリについて


##不適切な表現をブロック

![過激な言葉をブロックします-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/713689/3dca0ecd-0ecc-ef56-67d6-246f926b6951.jpeg)

ネット上に存在する**誹謗中傷や過激、差別的な表現をブロック**し非表示にするアプリです。【衝撃】、【悲報】など過剰に感情を煽り立てるようなものも対象としています。

その他、**広告などといったサイト内要素もブロック**することができるので広告ブロックとしてもお使いいただけます。Safari拡張機能として導入すれば普段のブラウジングを快適にすることができます。


##フィルタリングのカスタマイズ


![Reboot For Freedom & Energyのコピー-4-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/713689/88e28e7c-4856-1a2b-737f-d87f9ab108be.png)


ブロックする単語や表現はあらかじめいくつか登録していますが、新しく追加することも可能です。また、特定の単語が含まれている場合、**その文章ごとブロック**するように設定することも可能です。



#技術的なポイント
##使用言語

主な使用言語は**Swift**ですが、Safari機能拡張に関わる部分は**Javascript**で構成しています。HTMLやCSSも多少含まれていますが、これも機能拡張の一部で、本アプリでは大きな役割は持っていません。
細かい内訳は以下のようになっています。UIなどアプリ本体がSwiftで記述されているため、その割合が最も大きくなっていることがわかると思います。

![名称未設定のデザイン-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/713689/bf3a311e-c285-b199-0a66-7d5a93a65943.png)

Safariの拡張機能はChromeと似たものなので、そちらの使用言語にSwiftが加わった形になっていると考えてもらうとよいかもしれません。

##フィルタリングの仕組み

![感情分析、機械学習、 人工知能などの不使用.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/713689/2219b1e6-ed6b-5f01-5d0d-35cf09b932b4.png)

先ほどの使用言語にPythonが含まれていないことから予想された方もいるかもしれませんが、感情分析、人工知能や機械学習のような技術は**使っていません**。

その理由は**処理能力**です。一般的にモバイル機器の処理能力には限りがあるため、デスクトップ版の拡張機能のように高度なことをするには限界があるからです。

もちろん`Core ML`というAppleの機械学習フレームワークを使えば先述の問題も解消される可能性はありますが、簡単なモデルでは太刀打ちできないこともあります。

表現や単語の判別は事前に用意されたリストとユーザーの登録したものとの一致のみで行われます。無理に全ての判別を自動化するより、手軽にひとりひとりが判断できるようにすることを優先することにしています。

###JavaScriptでの処理

JavaScriptでの処理によってページ上の文字を置き換えています。これは基本的に**[Appleのサンプルコード](https://github.com/artemnovichkov/wwdc21-samplecode/tree/main/AdoptingNewSafariWebExtensionAPIs)**を参考にしているので、自作されたい方はそちらを参考にされるとよいかと思います。

アプリ本体とのデータ連携はまだ情報が少なく、自分の理解もまだ十分ではありません。応用が効くくらいに知見が深まったら記事を書きたいと思っていますが、現時点では以下のようにしています。

```javascript:content.js
var wordReplacementMap = {
    "【悲報】": " ",
    "【郎報】": " ",
    //省略・・・
};

browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
　　　//以下でアプリ側からの単語のリストを入手
    request["blockWord"].forEach(element => wordReplacementMap[element] = "_");

　　　//置き換えの処理
    for (var wordToReplace in wordReplacementMap) {
        replace(document.body, wordToReplace, wordReplacementMap[wordToReplace]);
    }
});
```





##SwiftUI＆MVVM
アプリ内のほとんどを**SwiftUI**で構成しています。UIKitを使用したのは色の微調整など、デザイン的にどうしてもという部分だけにとどめました。

また、本アプリでは**MVVM**も取り入れています。MVVMはコードデザインのパターンのひとつで、Model と View を分離していることが特徴です。今回はユーザーが登録する単語をModelとして扱っています。

```Swift:WordModel.swift
class WordModel: ObservableObject {
    //Extensionとの共有のために使用
    let defaults = UserDefaults(suiteName: "group.example.suiteName")

    @Published var blockWord: [String] {
        didSet {
            defaults!.set(blockWord, forKey: "blockWord")
        }
    }
    /// 初期化処理
    init() {
        blockWord = defaults!.object(forKey: "blockWord") as? [String] ?? [
            "example🐍"
        ]
    }
    
    func saveWord() {
        defaults!.set(blockWord, forKey: "blockWord")
    }
}

```




##Safari拡張

機能のほとんどはSafari拡張としてのものです。以前までSafari拡張機能自体はMac版のみでしたが、iOS15からiPhoneでも使用可能になりました。仕組みも使用感もChrome拡張に近いものですが、本体アプリの存在がそれとの明確な違いだといえます。

このアプリでは、**アプリ側で行った変更を拡張機能へ反映**することが可能です。先ほどのコードのように`Userdefault`と`App Groups`を利用することで、Safari拡張でもWidgetや他のExtensionのように素早いデータ反映を実現しています。

##Swift Package Manager

ライブラリは全て**Swift Package Manager（SPM）**で管理しています。Podfileなども必要なく、導入や削除もXcode内で完結させることも可能なので非常に管理が楽になりました。

特にXcode13では、SPM導入画面に以前使った履歴やREADMEが表示されたりと、使いやすさもかなり向上している印象です。

![スクリーンショット 2021-11-05 14.18.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/713689/0f527cbb-d6e8-aa8c-36ad-18f24ba07b16.png)




#デザイン
![Reboot For Freedom & Energyのコピー-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/713689/bf03a80d-c414-e5c7-b78e-f8e29ff63b9b.png)

## Glassmorphism
以前の記事でもご紹介しましたが、**Glassmorphism**という手法に興味があったのでそちらを参考にしています。iOS15から新たに使えるようになった`Material`も活用しながら半透明の画面を構築しました。機能自体はSwiftUIの`List`のものをそのまま使えるような設計なので、編集ボタンも利用可能です。

## ダークモード

ダークモードにも対応しており、それに合わせて背景画像などの差し替えを行なっています。Assertsファイルから設定することで各種色の切り替えも行っています。

## SF Symbol
アイコンはFontawesomeなど外部のものは使わず、SF Symbolのみを使用しています。新たに画像を読み込む必要もありませんし、使う側も見慣れたアイコンを使うことができるからです。


## アクセシビリティ
まだ完璧とは言えませんが、本アプリではアクセシビリティにも配慮しています。SwiftUIの`TabView`のアイコンは通常だと色のみで選択しているものを見分ける形になります。しかし、これだと色覚頼りになってしまうため、塗りつぶしありとなしも切り替えて表示することでよりわかりやすく判別できるようにしています。

#さいごに

このアプリはネット上の悪質な表現や誹謗中傷対策として開発されたものです。これに関しては悲しい事件も起きているので、ちょっとしたことですが、自分でも何かできることはと思い制作しました。

共感していただければLGTMなどしていただけると励みになります☺️少しでもみなさんのお役に立てれば幸いです。

---

よかったらこちらもよろしくお願いします

・その他作成したアプリ：[RebootFilter](https://twitter.com/tokiwa16448392)

・各種お知らせ：[Twitter](https://twitter.com/tokiwa16448392)

・作成途中のアプリなどについて：[note](https://note.com/izumo092)