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 1 year has passed since last update.

BGMアプリ開発記録簿①

Last updated at Posted at 2023-05-29

こんちにちは。今日はWebViewを使用した開発記録をつけていきたいと思います。
私は自分のECサイトを運営しているのですがそのECサイトとアプリを連携させたいというのが今回の狙いです。

基本的な機能は、例えばボタンなどはWeb内において作成しているのでとても簡単にアプリの大まかな機能は作成することができるのがメリットです。ただしあまりにもアプリとしての機能が単純で、単にWebViewを貼り付けただけのアプリだったらApStoreにリリースする際にリジェクトされるので注意が必要です。

今回もChatGPTにSwiftUI開発用のコマンドを打ち込みそれをもとに質問しながら学習を開始します。

開発環境
フレームワーク ChatGPT
SwiftUI browsing

でははじめていきましょう。
image.png

返ってきた答えがこちら

import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    let url: URL

    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        let request = URLRequest(url: url)
        uiView.load(request)
    }
}

struct ContentView: View {
    var body: some View {
        WebView(url: URL(string: "https://www.example.com")!)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

まず、import SwiftUIとimport WebKitを使用して必要なモジュールをインポートします。次に、WebViewというSwiftUIのビューを作成し、その中にWKWebViewを埋め込みます。最後に、UIViewRepresentableプロトコルを適用して、UIKitのWKWebViewをSwiftUIで使用できるようにします。

ほうほう。まあこれで単純にWebを表示することができるということですね。
それでは表示の仕方がだいたいわかったので設計を含めて書き直してもらおうと思います。

image.png

するとContentView,WebView,WebViewLoaderという3つのファイルを作成してくれました

WebViewLoader

import Combine
import WebKit

final class WebViewLoader: NSObject, WKNavigationDelegate {
    @Published var isLoading: Bool = true
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        isLoading = false
    }
    
    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        isLoading = false
    }
}

WebView

import SwiftUI

struct WebView: UIViewRepresentable {
    let url: URL
    @ObservedObject var loader: WebViewLoader

    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        webView.navigationDelegate = loader
        return webView
    }

    func updateUIView(_ webView: WKWebView, context: Context) {
        if webView.url == nil {
            webView.load(URLRequest(url: url))
        }
    }
}

ContentView

struct ContentView: View {
    private let url = URL(string: "https://otostation1969.com")!
    @ObservedObject private var loader = WebViewLoader()

    var body: some View {
        VStack {
            if loader.isLoading {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
                    .scaleEffect(2)
            }
            WebView(url: url, loader: loader)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

このように、WebViewLoader、WebView、およびContentViewを分離することで、各部分の役割が明確になり、コードがより読みやすく、メンテナンスが容易になります。また、Combineフレームワークを使用することで、データのフローと状態の管理がより簡潔で効果的になります。

ここでビルドしてみると

Generic struct 'ObservedObject' requires that 'WebViewLoader' conform to 'ObservableObject'

という警告が出ました

エラーメッセージは、WebViewLoaderがObservableObjectプロトコルに準拠していないと言っています。ObservableObjectプロトコルは、オブジェクトが変更を発行できるようにするためのもので、ObservedObjectプロパティラッパーと共に使用します。
あなたのWebViewLoaderクラスにObservableObjectプロトコルを追加することで、この問題を解決することができます。以下に修正したWebViewLoaderクラスを示します。

修正 ついでにファイル名も変更

import Combine
import WebKit

final class WebViewLoader: NSObject, WKNavigationDelegate, ObservableObject {
    @Published var isLoading: Bool = true
    @Published var error: Error?

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        isLoading = false
    }

    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        isLoading = false
        self.error = error
    }
}
import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    let url: URL
    @ObservedObject var loader: WebViewLoader

    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        webView.navigationDelegate = loader
        return webView
    }

    func updateUIView(_ webView: WKWebView, context: Context) {
        if webView.url == nil {
            webView.load(URLRequest(url: url))
        }
    }
}
import SwiftUI

struct MainView: View {
    let url: URL
    @ObservedObject private var loader = WebViewLoader()

    var body: some View {
        VStack {
            if loader.isLoading {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
                    .scaleEffect(2)
            } else if let error = loader.error {
                Text("An error occurred: \(error.localizedDescription)")
            } else {
                WebView(url: url, loader: loader)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        MainView(url: URL(string: "https://otostation1969.com")!)
    }
}

MainViewをさらに修正

struct MainView: View {
    private let url = URL(string: "https://otostation1969.com")!
    @ObservedObject private var loader = WebViewLoader()

    var body: some View {
        ZStack {
            WebView(url: url, loader: loader)
            if loader.isLoading {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
                    .scaleEffect(2)
            }
        }
    }
}

URLを定数にいれ、ProgressViewを中央に来るように修正(すいません、Progress
Viewも途中で作ってもらいました)

image.png

Assistant(ゆるふわ先生):それは素晴らしいアイデアですね、ちぬたさん!ローディング画面を見ているだけで退屈することはありますよね。それを楽しい経験に変えると、ユーザーエクスペリエンスが大幅に向上します。
ローディング画面に色を追加し、アニメーションを適用することで視覚的に魅力的なものにすることができます。以下にその一例を示します。

struct MainView: View {
    private let url = URL(string: "https://otostation1969.com")!
    @ObservedObject private var loader = WebViewLoader()

    var body: some View {
        ZStack {
            WebView(url: url, loader: loader)
            if loader.isLoading {
                Rectangle()
                    .fill(Color.blue.opacity(0.2))
                    .edgesIgnoringSafeArea(.all)
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
                    .scaleEffect(2)
                    .rotationEffect(Angle(degrees: loader.isLoading ? 360 : 0))
                    .animation(Animation.linear(duration: 1).repeatForever(autoreverses: false))
            }
        }
    }
}

上記のコードでは、ローディング中に全画面を薄い青色で覆うRectangleを追加し、ProgressViewに無限に繰り返す回転アニメーションを適用しています。
これにより、ローディング画面がより視覚的に魅力的になり、ユーザーが待つ時間を楽しむことができます。

ここで一旦実行。

うん。画像にはありませんが、ProgressViewもういい感じでした。

今日は一旦ここまで。

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?