0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン ~Mediator~

Last updated at Posted at 2020-01-19

この記事シリーズは、iOS/Swiftエンジニアである執筆者個人が、
ごく普通のiOSアプリ開発でよくある状況
Swiftのコアライブラリやフレームワークで使われているパターン
着目してデザインパターンを学び直してみた記録です。

関連記事一覧
[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン

Mediatorパターン概要

  • Mediatorとは「仲介者」という意味です。
  • 複数のオブジェクト間で直接やり取りをせずにMediatorを介してやり取りします。
  • 各オブジェクトが依存する相手をMediatorだけにすることで、オブジェクト同士が疎結合になり、関連オブジェクトが多い場合には保守性を向上できます。
  • GoFのデザインパターンでは振る舞いに関するパターンに分類されます。

使い所

実務的な例としては、UIPageViewControllerの配下にある子ViewController同士の通知が考えられます。
以下のサンプルコードは、赤背景のViewControllerが非表示になる時に、青背景のViewControllerに通知を行う例です。

qiita20200119.gif

UIPageViewControllerがMediatorの役割を担っています。
メリットは、新たにReceiverとなる子ViewControllerが増えた時、既存の子ViewControllerは変更しなくても済む点です。

サンプルコード

Xcode 11.3でシングルページアプリケーションを新規作成し、ViewController.swiftに以下のコードをコピペすれば動作します。

// MARK: - プロトコル
protocol Receiver {
    func receive(message: String)
}

protocol Sender {
    func send(message: String)
}

protocol Mediator: class {
    var recipients: [Receiver] { get }
    func send(message: String)
}

// MARK: - PageViewControllerの子ViewController
// 送り手のViewController
final class SenderViewController: UIViewController {
    // Message送信をMediatorに委譲する
    weak var messageDelegate: Mediator?

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // 表示されなくなった時にMessageを送信(Mediatorに委譲)
        messageDelegate?.send(message: "SenderViewController.viewDidDisappear()")
    }
}

// 受け手のViewController ※Receiverプロトコルに準拠
final class RecieverViewController: UIViewController, Receiver {
    func receive(message: String) {
        print("\(message)を受信しました")
    }
}

// MARK: - PageViewController
final class ViewController: UIPageViewController {
    // 送り手のViewController
    let senderViewController = SenderViewController()
    // 受け手のViewController
    let receiverViewController = RecieverViewController()
    // 子ViewController配列
    var controllers = [UIViewController]()

    override func viewDidLoad() {
        super.viewDidLoad()
        // PageViewControllerの子ViewControllerを設定
        senderViewController.view.backgroundColor = .red
        controllers.append(senderViewController)

        receiverViewController.view.backgroundColor = .blue
        controllers.append(receiverViewController)

        setViewControllers([controllers[0]], direction: .forward, animated: false, completion: nil)
        dataSource = self
        
        // SenderViewControllerの委譲先に自分を設定する
        senderViewController.messageDelegate = self
    }
}

// PageViewControllerをMediatorプロトコルに準拠
extension ViewController: Mediator {
    var recipients: [Receiver] {
        // 子ViewControllerの中でReceiverプロトコルに準拠しているものを返す
        return controllers.filter { $0 is Receiver } as! [Receiver]
    }

    func send(message: String) {
        for recipient in recipients {
            recipient.receive(message: message)
        }
    }
}
※説明には無関係ですが動かす時にコピペが必要なコード
// UIPageViewControllerDataSource
extension ViewController: UIPageViewControllerDataSource {
    // 右にスワイプ(戻る)
    func pageViewController(_ pageViewController: UIPageViewController,
                            viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard
            let index = controllers.firstIndex(of: viewController),
            index > 0
            else {
                return nil
        }
        return controllers[index - 1]
    }

    // 左にスワイプ(進む)
    func pageViewController(_ pageViewController: UIPageViewController,
                            viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard
            let index = controllers.firstIndex(of: viewController),
            index < controllers.count - 1
            else {
                return nil
        }
        return controllers[index + 1]
    }

    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        return controllers.count
    }
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?