LoginSignup
1
4

More than 1 year has passed since last update.

【SwiftUI】オフラインになったら画面を切り替える

Last updated at Posted at 2022-11-25

はじめに

Webの場合、インターネット接続がないと以下のような表示になります。
スクリーンショット 2022-11-25 16.07.01.png

これをモバイルアプリでも再現したいと思います。

サンプルアプリ

RPReplay_Final1669361474_MP4_AdobeExpress.gif

実装

NWPathMonitor を Combine の Publisher として扱えるようにする拡張」を使用してCombineで使えるように拡張します。

NWPathMonitor
import Combine
import Network

extension NWPathMonitor {
    func publisher(queue: DispatchQueue = DispatchQueue.main) -> NWPathMonitor.Publisher {
        Publisher(monitor: self, queue: queue)
    }

    final class Subscription<S: Subscriber>: Combine.Subscription where S.Input == NWPath {
        private let subscriber: S
        private let monitor: NWPathMonitor
        private let queue: DispatchQueue
        private var isStarted = false

        init(subscriber: S, monitor: NWPathMonitor, queue: DispatchQueue) {
            self.subscriber = subscriber
            self.monitor = monitor
            self.queue = queue
        }

        func request(_ demand: Subscribers.Demand) {
            precondition(demand == .unlimited, "This subscription is only supported to `Demand.unlimited`.")

            guard !isStarted else { return }
            isStarted = true

            monitor.pathUpdateHandler = { [unowned self] path in
                _ = self.subscriber.receive(path)
            }
            monitor.start(queue: queue)
        }

        func cancel() {
            monitor.cancel()
        }
    }

    struct Publisher: Combine.Publisher {
        typealias Output = NWPath
        typealias Failure = Never

        private let monitor: NWPathMonitor
        private let queue: DispatchQueue

        init(monitor: NWPathMonitor, queue: DispatchQueue) {
            self.monitor = monitor
            self.queue = queue
        }

        func receive<S>(subscriber: S) where S: Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
            let subscription = Subscription(subscriber: subscriber, monitor: monitor, queue: queue)
            subscriber.receive(subscription: subscription)
        }
    }
}
NetworkMonitor
import Combine
import Network
import SwiftUI

fileprivate final class NetworkMonitorViewModel: ObservableObject {
    @Published var status: NWPath.Status = .satisfied

    private let monitor = NWPathMonitor()

    init() {
        monitor.publisher()
            .map(\.status)
            .assign(to: &$status)
    }
}

struct NetworkMonitorView<T: View>: View {
    @StateObject private var viewModel = NetworkMonitorViewModel()
    private let online: () -> T
    private let offline: () -> T
    init(@ViewBuilder online: @escaping () -> T,
         @ViewBuilder offline: @escaping () -> T
    ) {
        self.online = online
        self.offline = offline
    }
    var body: some View {
        switch viewModel.status {
        case .satisfied:
            online()
        case .unsatisfied:
            offline()
        case .requiresConnection:
            offline()
        @unknown default:
            fatalError()
        }
    }
}

使い方

ContentView
import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing: 10) {
            NetworkMonitorView {
                Image(systemName: "wifi")
                    .foregroundColor(.green)
                    .font(.system(size: 50))
                Text("オンライン")
                    .foregroundColor(.green)
                    .font(.system(size: 50))
            } offline: {
                Image(systemName: "wifi.slash")
                    .foregroundColor(.red)
                    .font(.system(size: 50))
                Text("オフライン")
                    .foregroundColor(.red)
                    .font(.system(size: 50))
            }
        }
    }
}

おわり

NWPathMonitor を Combine の Publisher として扱えるようにする拡張
これめっちゃ勉強になりました!

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