Swiftで学ぶObservationの方法10選

Swift言語でのObservationのイラスト図解Swift
この記事は約22分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

SwiftはAppleが開発したプログラミング言語で、iOSやmacOSなどのAppleのOSで動くアプリケーションを開発する際に広く利用されています。

その中でも、「Observation」という機能は、データや状態の変化を監視する際に非常に役立ちます。

この記事では、SwiftでのObservationの方法を10のサンプルコードと共に詳しく解説していきます。

初心者から上級者まで、Observationの使い方・応用例・注意点・カスタマイズ方法を順を追って解説していきます。

Observationとは、オブジェクトやデータの状態変化を検知し、その変化に応じて何らかのアクションを取るための仕組みを指します。

これにより、データの変更をリアルタイムに捉えてUIの更新などを行うことができます。

●SwiftとObservationの基本

Swiftの基本的な特性やObservationの背景について、順を追って解説していきます。

○Swift言語とは

Swiftは、2014年にAppleが公開した新しいプログラミング言語で、Objective-Cに代わる言語として導入されました。

その設計思想は、安全性と高速性を両立させることにあります。

簡潔で読みやすい文法が特徴で、プログラミング初心者でも学びやすい言語として評価されています。

Swiftは、メモリ管理や型安全性などの高度な機能を備えており、これにより安全なコードを書くことが可能となっています。

また、Appleの各プラットフォーム(iOS、macOS、watchOS、tvOS)での開発に適している点も大きな魅力となっています。

○Observationの基本理念

Observationの概念は、Swiftだけに限らず多くのプログラミング言語やフレームワークで見受けられます。

変更を監視し、変更があった際に通知を受け取ることで、システム全体の状態を最新に保つための重要な仕組みです。

SwiftにおけるObservationは、この基本的な考え方を踏まえつつ、Swiftの特性を活かして実装されています。

●Observationの使い方

Observationは、Swiftの中でも特に強力な機能として知られています。

これを用いることで、あるデータの変化を監視し、それに応じてアクションを実行することが可能となります。

ここでは、具体的な使い方をサンプルコードを交えて解説します。

○サンプルコード1:プロパティのObservation

このコードでは、SwiftのプロパティObserverを用いて、プロパティの変更を検知する方法を表しています。

この例では、scoreという整数のプロパティが変更されたときに、その変更を検知してメッセージを出力する機能を持ったクラスを作成しています。

class Player {
    var score: Int = 0 {
        willSet(newScore) {
            print("スコアが変更される予定: \(newScore)")
        }
        didSet {
            print("スコアが変更されました: \(score)")
        }
    }
}

let player = Player()
player.score = 10

このコードを実行すると、まず"スコアが変更される予定: 10"というメッセージが出力され、その後"スコアが変更されました: 10"というメッセージが出力されることを期待しています。

○サンプルコード2:配列の要素のObservation

Swiftでは、配列の要素の変更を監視するのは直接的な方法が提供されていませんが、カスタムクラスを使って実装することができます。

この例では、itemsという文字列の配列に変更が加えられたときに、その変更を検知するクラスを作成しています。

class ObservableArray {
    var items: [String] = [] {
        didSet {
            print("配列が変更されました: \(items)")
        }
    }
}

let array = ObservableArray()
array.items.append("apple")

このコードを実行すると、配列に”apple”が追加されたことを検知して、"配列が変更されました: ["apple"]"というメッセージが出力されることを期待しています。

○サンプルコード3:Notificationを用いたObservation

このコードでは、NotificationCenterを使って特定のイベントが発生したときに通知を受け取る方法を表しています。

この例では、Playerクラスが持つlevelUpメソッドが実行されるたびに、その事実を他のオブジェクトに通知する機能を持ったクラスを作成しています。

class Player {
    let levelUpNotification = Notification.Name("levelUpNotification")

    func levelUp() {
        NotificationCenter.default.post(name: levelUpNotification, object: nil)
    }
}

let player = Player()

NotificationCenter.default.addObserver(forName: player.levelUpNotification, object: nil, queue: nil) { _ in
    print("プレイヤーがレベルアップしました!")
}

player.levelUp()

このコードを実行すると、levelUpメソッドが呼ばれた後、"プレイヤーがレベルアップしました!"というメッセージが出力されることを期待しています。

●Observationの応用例

SwiftのObservation機能は、データの変更を監視し、変更があった場合に特定のアクションを実行するのに非常に役立ちます。

ここでは、Observationの高度な使用例として、カスタムデータのObservationや外部APIのデータ変更を検知する方法を2つのサンプルコードを交えて詳しく解説します。

○サンプルコード4:カスタムデータのObservation

このコードでは、カスタムクラス内のプロパティの変更を監視する方法を表しています。

この例では、ユーザーの名前が変更されたときに通知を受け取り、コンソールにメッセージを出力します。

class User {
    var name: String {
        didSet {
            print("ユーザーの名前が\(name)に変更されました。")
        }
    }

    init(name: String) {
        self.name = name
    }
}

let user = User(name: "Taro")
user.name = "Jiro"  // ユーザーの名前がJiroに変更されました。

この例の中心となるのはdidSetオブザーバーです。

これは、プロパティの値が変更された直後に実行されるものです。

上記のサンプルでは、nameプロパティの値が変更された後、新しい名前をコンソールに出力しています。

このコードを実行すると、user.name = "Jiro"というコード行によって、ユーザーの名前が”Jiro”に変更されたというメッセージがコンソールに出力されます。

○サンプルコード5:外部APIのデータ変更を検知するObservation

次に、外部のAPIから取得したデータの変更を検知する方法を解説します。

このコードでは、APIからのレスポンスデータの変更を監視し、変更があった場合にそれを検知して通知を行います。

import Foundation

class APIObserver {
    var data: Data? {
        didSet {
            if let newData = data {
                processData(newData)
            }
        }
    }

    func fetchDataFromAPI() {
        // ここでは外部APIからデータを取得する処理を模擬的に示しています。
        self.data = Data()  // 仮のデータを代入
    }

    private func processData(_ data: Data) {
        print("APIから新しいデータを取得しました。")
    }
}

let apiObserver = APIObserver()
apiObserver.fetchDataFromAPI()  // APIから新しいデータを取得しました。

上記のサンプルでは、APIObserverクラス内のdataプロパティに対して変更を監視しています。

具体的には、dataプロパティに新しいデータが代入された際に、processData関数が呼び出されて、”APIから新しいデータを取得しました。”というメッセージがコンソールに出力されます。

このサンプルコードを実行すると、APIからデータを取得する模擬処理が実行され、結果として上記のメッセージがコンソールに表示されることが期待されます。

○サンプルコード6:UI要素の変化を監視するObservation

Swiftでは、UIの要素の変化を監視するための仕組みとして、Key-Value Observing(KVO)が提供されています。

特に、UIKitの要素の状態変化やプロパティの変更を監視したい場合に有効です。

このコードでは、UIButtonのenabledプロパティの変化を監視して、変化があった場合に何らかの処理を行う例を表しています。

この例では、ボタンが活性・非活性に変わった際に、その状態をコンソールに表示しています。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var sampleButton: UIButton!

    // KVOでのObservation
    var observation: NSKeyValueObservation?

    override func viewDidLoad() {
        super.viewDidLoad()

        // UIButtonのenabledプロパティの変化を監視
        observation = sampleButton.observe(\.isEnabled, options: [.new]) { [weak self] (button, change) in
            guard let newValue = change.newValue else { return }
            print("ボタンの状態が\(newValue ? "活性" : "非活性")になりました。")
        }
    }

    @IBAction func toggleButtonState(_ sender: Any) {
        sampleButton.isEnabled.toggle()
    }
}

この例では、observeメソッドを使用して、isEnabledプロパティの変化を監視しています。

toggleButtonStateメソッドが呼ばれると、ボタンの活性・非活性が切り替わり、その結果がコンソールに出力されるようになっています。

このように、ボタンを押すとコンソールに「ボタンの状態が活性になりました。」または「ボタンの状態が非活性になりました。」というメッセージが表示されます。

これにより、状態変化を簡単に監視することができます。

○サンプルコード7:マルチスレッド環境での安全なObservation

マルチスレッド環境では、複数のスレッドから同時にデータにアクセスすることがあるため、データの破損や予期しない動作が起こる可能性があります。

そのため、マルチスレッド環境でのObservationを行う場合は特に注意が必要です。

このコードでは、マルチスレッド環境でのデータの変化を安全に監視する方法を表しています。

この例では、DispatchQueueを使用して、メインスレッドでのみデータの変化を監視・処理しています。

import Foundation

class DataObserver {

    var data: String = "" {
        didSet {
            DispatchQueue.main.async {
                self.notifyDataChange()
            }
        }
    }

    func updateData(newData: String) {
        DispatchQueue.global().async {
            self.data = newData
        }
    }

    func notifyDataChange() {
        print("データが\(data)に更新されました。")
    }
}

let observer = DataObserver()
observer.updateData(newData: "新しいデータ")

この例では、dataプロパティのdidSetを使用して、データの変化を検知しています。

updateDataメソッドでは、グローバルキューを使用して非同期にデータを更新しており、この変化をメインスレッドで監視して処理するようにしています。

このように、コードを実行するとコンソールに「データが新しいデータに更新されました。」というメッセージが表示されます。

これにより、マルチスレッド環境でもデータの変化を安全に監視することができます。

○サンプルコード8:データベースの変更を監視するObservation

データベースの変更をリアルタイムで監視することは、多くのアプリケーションで必要とされる機能の一つです。

特にユーザのアクションに応じてデータを更新し、その変更をすぐに画面上で反映したい場合などに役立ちます。

このコードでは、Swiftを使ってデータベースの変更を監視する方法を表しています。

具体的には、あるデータモデルに変更が加えられた際、それを検知して処理を実行する例を紹介します。

// データモデルの定義
class DataModel: ObservableObject {
    @Published var data: String = "Initial Data"
}

// データモデルのインスタンス化
let model = DataModel()

// Observationの実装
let observer = model.$data.sink { newValue in
    print("データが更新されました: \(newValue)")
}

// データの変更
model.data = "Updated Data"

この例では、ObservableObjectプロトコルを採用したDataModelというクラスを定義しています。

@Publishedプロパティラッパーを使用して、dataというプロパティの変更を監視します。

そして、.sinkメソッドを使って、データが更新された際の処理を指定しています。

データが「Updated Data」に変更されると、コンソールに「データが更新されました: Updated Data」と表示されます。

○サンプルコード9:ユーザの位置情報変更を検知するObservation

位置情報の変更を監視することは、位置情報に基づくサービスや機能を提供するアプリケーションにおいて非常に重要です。

このコードでは、Swiftを使ってユーザの位置情報の変更を検知する方法を表しています。

具体的には、Core Locationフレームワークを使用して、ユーザの位置情報が更新された際の処理を実装します。

import CoreLocation

class LocationManager: NSObject, CLLocationManagerDelegate {
    var locationManager = CLLocationManager()
    var currentLocation: CLLocation?

    override init() {
        super.init()
        locationManager.delegate = self
        locationManager.requestWhenInUseAuthorization()
        locationManager.startUpdatingLocation()
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            currentLocation = location
            print("現在の位置: \(location)")
        }
    }
}

let locationObserver = LocationManager()

この例では、CLLocationManagerを用いてユーザの位置情報を取得し、didUpdateLocationsメソッド内でその位置情報が更新された際の処理を実装しています。

○サンプルコード10:設定変更を監視するObservation

多くのアプリケーションには、設定やプリファレンスのページが存在し、その設定内容に応じてアプリの挙動を変更することが多いです。

このコードでは、Swiftを使ってアプリの設定変更を監視する方法を表しています。

具体的には、UserDefaultsを利用して設定内容の変更を検知し、その変更をハンドルします。

let settingsKey = "userSettings"

// UserDefaultsの値が変更された際のNotificationを監視
NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification, object: nil, queue: .main) { _ in
    if let newSettingsValue = UserDefaults.standard.string(forKey: settingsKey) {
        print("設定が変更されました: \(newSettingsValue)")
    }
}

// 設定値を変更
UserDefaults.standard.setValue("新しい設定内容", forKey: settingsKey)

この例では、UserDefaults.didChangeNotificationを監視して、UserDefaultsの値が変更された際の処理を定義しています。

設定内容が「新しい設定内容」に変更されると、コンソールに「設定が変更されました: 新しい設定内容」と表示されます。

このように、Swiftを使ってさまざまなObservationを行うことで、アプリケーションの動的な挙動やユーザのアクションに迅速に対応することが可能になります。

●Observationの注意点と対処法

Observationを使用する際に、開発者はいくつかの注意点に気をつける必要があります。

正しく扱わないと、アプリケーションのパフォーマンスや安定性に影響を与える可能性があるためです。

ここでは、SwiftにおけるObservationの主要な注意点と、それを回避または対処するための方法を解説します。

○コードのパフォーマンスと効率のバランス

Observationは非常に便利な機能であり、データの変化を監視することが容易になります。

しかし、頻繁に発火するObservationや多数のObserverを設置すると、パフォーマンスに悪影響を及ぼす可能性があります。

例えば、ある変数が頻繁に更新される場合、それを監視するObserverが何度も呼び出されます。

これが多数のObserverになると、アプリケーションの動作が遅くなることが考えられます。

このコードでは変数countを使って更新回数を表しています。

この例ではcountが更新されるたびにObserverが呼び出されます。

var count: Int = 0 {
    didSet {
        print("countが更新されました: \(count)")
    }
}

for _ in 1...10000 {
    count += 1
}

この例では、countが10000回更新されるたびにObserverが呼び出されます。

そのため、このコードは10,000回の”countが更新されました”という出力をします。

このような頻繁なObservationは、特に必要がない限り避けるべきです。

必要な場合でも、Observationの回数を最小限に抑える工夫が求められます。

○Observationの解除とメモリ管理

SwiftでのObservationを行う場合、特にNotificationCenterKVOを利用する際には、Observerの登録後に適切に解除を行わないとメモリリークの原因となる可能性があります。

例えば、NotificationCenterを使用してObservationを行う場合、次のようにObserverを登録することができます。

let observer = NotificationCenter.default.addObserver(forName: .someNotificationName, object: nil, queue: nil) { (notification) in
    print("通知を受け取りました!")
}

このコードでは、someNotificationNameという名前の通知を受け取るたびにprint文が実行されます。

しかし、このObserverを解除しないと、そのオブジェクトが解放されてもObserverは残り続け、メモリリークを引き起こす可能性があります。

そのため、適切なタイミングでObserverを解除することが重要です。

NotificationCenter.default.removeObserver(observer)

このコードは、先ほど登録したObserverを解除しています。

Observerを解除することで、メモリの無駄な占有を防ぎ、アプリケーションの安定性を保つことができます。

●Observationのカスタマイズ方法

Observationを最大限に活用するためには、そのカスタマイズ方法を知ることが欠かせません。

特定の目的やシナリオに合わせてObservationの動作を変更することで、さらに柔軟かつ効果的なコーディングが可能になります。

ここでは、SwiftでのObservationのカスタマイズ方法を2つのサブトピックに分けて解説していきます。

○カスタムObserverの作成

Swiftでは、特定の変更を監視するためのカスタムObserverを作成することができます。

これにより、既存のシステムの変更を最小限に抑えつつ、新しい機能や動作を追加することができます。

このコードでは、Int型のプロパティの変更を監視するカスタムObserverを作成するコードを表しています。

この例では、値が変更されるたびに、変更前と変更後の値をコンソールに出力しています。

class CustomIntObserver {
    var value: Int {
        didSet {
            // ここでoldValueは変更前の値を参照する特殊なキーワードです
            print("以前の値: \(oldValue), 現在の値: \(value)")
        }
    }

    init(_ initialValue: Int) {
        self.value = initialValue
    }
}

let observer = CustomIntObserver(5)
observer.value = 10

このコードを実行すると、”以前の値: 5, 現在の値: 10″というメッセージがコンソールに出力されます。このようにカスタムObserverを用いることで、変更を監視し、特定のアクションをトリガーすることができます。

○データバインディングのカスタマイズ

データバインディングとは、UIコンポーネントとデータの間の結びつきを意味します。

Swiftでは、このバインディングをカスタマイズすることで、UIの変更とデータの変更を同期させることができます。

このコードでは、UILabelのテキストとString型のプロパティをバインディングする方法を表しています。

この例では、文字列のプロパティが変更されると、UILabelのテキストも自動的に更新されます。

import UIKit

class BindingString {
    var text: String {
        didSet {
            label.text = text
        }
    }
    var label: UILabel

    init(_ label: UILabel, initialText: String) {
        self.label = label
        self.text = initialText
        self.label.text = initialText
    }
}

let myLabel = UILabel()
let bindingInstance = BindingString(myLabel, initialText: "初期テキスト")
bindingInstance.text = "更新されたテキスト"

このコードを実行すると、myLabelのテキストプロパティが”更新されたテキスト”に変わります。

このようなカスタマイズを利用することで、データとUIの間の連動を強化し、ユーザエクスペリエンスを向上させることができます。

まとめ

この記事では、SwiftでのObservationの方法について、基本から応用例、注意点、そしてカスタマイズ方法まで幅広く解説しました。

ObservationはSwiftにおいて非常に強力な機能であり、データやUIの変更を効果的に監視し、アプリケーションの動作を最適化するために不可欠です。

初心者の方々にとっては、プロパティやイベントの変化を検知し、それに応じてアクションを起こす基本的な使い方から学び、徐々に複雑なシナリオへと応用することが重要です。

また、上級者にとっては、カスタムObserverの作成やデータバインディングのカスタマイズなど、より洗練された技術を習得することで、さらなるアプリケーションの最適化が可能となります。

Observationを使用する際には、パフォーマンスとメモリ管理に留意することが重要です。

不適切なObservationの使用は、アプリケーションのパフォーマンス低下やメモリリークを引き起こす可能性がありますので、使用する際には常にこれらのポイントを念頭に置くことが肝心です。

最後に、SwiftでのObservationは常に進化しています。新しい機能や更新されたベストプラクティスに常に目を向け、学習を続けることが、Swiftの世界で生き残り、成功するための鍵となります。

本記事が、皆さんのSwiftでのプログラミング学習の一助となれば幸いです。