LoginSignup
2
4

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-02-02

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

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

Observerパターン概要

  • Observerとは「観察者」という意味です。
  • オブジェクトの状態が変化した際に、そのオブジェクト自身が、「観察者」に状態の変化を「通知」する仕組みです。
  • GoFのデザインパターンでは振る舞いに関するパターンに分類されます。
  • iOSアプリ開発では、他のどのパターンよりも実用的なパターンと思います。

使い所

実務においては、ViewControllerにAPI通信やデータ取得/更新などのロジックを詰め込むとViewControllerが肥大化してしまい、保守性が低下します。

そこで例えば、API通信やデータ取得/更新をViewModelに追い出す「MVVMアーキテクチャー」などを導入したりします。
ViewModelからViewControllerに状態変化の通知をする際にObserverパターンを良く使います。

サンプルコード

Xcode 11.3 / Swift 5.1 です。

import UIKit

// MARK: - Imageの変化を通知するためのprotocol
protocol ImageObserver: class {
    func didChange()
}

// MARK: - ViewModel
final class ViewModel {
    weak var imageObserver: ImageObserver?

    var image: UIImage? {
        didSet {
            // 値が変わったらObserverに通知
            if image != oldValue {
                imageObserver?.didChange()
            }
        }
    }

    /// サーバーから画像を取得する
    func requestImage(with url: URL) {
        URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
            if let error = error {
                print("画像取得失敗: ", error)
                return
            }
            guard let data = data, let image = UIImage(data: data) else {
                return
            }
            self?.image = image // image.didSetが実行され、ViewController.didChange()に通知される
        }.resume()
    }
}

// MARK: - ViewController
class ViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!

    private let viewModel = ViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        viewModel.imageObserver = self
        let imageUrl = URL(string: "https://upload.wikimedia.org/wikipedia/commons/5/56/Donald_Trump_official_portrait.jpg")!
        viewModel.requestImage(with: imageUrl)
    }
}

// ViewControllerをImageObserver protocolに準拠
extension ViewController: ImageObserver {
    func didChange() {
        DispatchQueue.main.async { [weak self] in
            self?.imageView.image = self?.viewModel.image
        }
    }
}

補足

Property Observers

didSetはプロパティに値がセットされた後に呼ばれる処理ブロックで、Property Observers(プロパティ・オブザーバー)と呼びます。
oldValueという予約された変数によって、以前値を取得できます。

プロパティ・オブザーバーには、値がセットされる前に呼ばれるwillSet(_:)もあります。
こちらは引数で新しい値を取得できます。

プロパティ・オブザーバーがあることで、上のサンプルコードで仮にViewModel.imageをセットする箇所が増えたとしても、 imageObserver.didChange()の呼び出しを増やさずに済みます。

SwiftはObserverパターンを実装しやすい言語であると思います。

(参考リンク)
The Swift Programming Language - Properties

Observerパターン以外の通知方法

iOS/Swiftにおいては、Observerパターン以外にもオブジェクト間の通知の方法がいくつかあります。

こちらの記事が参考になります。
Swiftとオブジェクト間の通知のパターン

2
4
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
2
4