SwiftでXMLをパースする。ありがとうyahoojapan

XMLパーサーは、極論すると、これで終わる。簡単!

github.com

こんな感じ。

import SwiftyXMLParser

if let path: String = Bundle.main.path(forResource: "hoge", ofType: "txt") {
  do {
// ファイルの内容を取得する
    let content = try String(contentsOfFile: path)
// print("content: \(content)")
    let xml = try XML.parse(content)
    var i = 0
    while true {
      guard let str = xml["root", "data", i, "value"].text else { break; }
      print("\(str)")
      i += 1
    }
    print("ループ終わり")
  } catch  {
    print("ファイルの内容取得時に失敗")
  } // do
} else {
  print("指定されたファイルが見つかりません")
} // if let

真面目にやると結構大変です。XMLParser()を使うのだけれど、 要素(エレメント、属性)が1つ見つかる度に、XMLParserDelegateにあるparse(..)という名前の 関数が呼び出されて、結果を取り出して自分で使えるように加工しないといけません。 この辺りを全部やってくれるのが、上記のyahoojapanのコードです。

if let 変数 と guard let 変数

if let a = myFunc() {成功したらやること} else {失敗したらやること}

なお、myFunc()は、成功時は値を返して、失敗時はnil(NULLのこと)を返すように実装する。 これで、通常動作と失敗時の動作が両方書ける。なお、失敗時はaにはnilが入る。

if guard let a = myFunc() else {中断; returnとかexit(-1)とか}

成功時は処理を続けるが、失敗したら処理が続けることができない時の実装形式。 myFunc()は、成功時は値を返して、失敗時はnil(NULLのこと)を返すように実装する。 aには値しか入らない。nilが入ることはなく、失敗時は次の処理ができない。

失敗と成功の処理をしっかり書きなさいというルールです。 なお、if let a = の方は、aがnilかもしれないので、aの値を普通に使おうとすると、 nilを考えてないぜ! っというビルドエラーになる。

SwiftUIでコード内の文字をWebKitで表示してjavascriptの結果を受け取る

ContentView.swift

import SwiftUI
 
struct ContentView: View {
    var body: some View {
        WebView(url: URL(string: "dummy")!)
    }
}

WebView.swift

import SwiftUI
import WebKit
 
struct WebView: UIViewRepresentable {
    var url: URL
    
    func makeUIView(context: Context) -> WKWebView {
        let webConfig = WKWebViewConfiguration()
        let userController = WKUserContentController()
        userController.add(makeCoordinator(), name: "hoge")
        webConfig.userContentController = userController
        let wkWebView = WKWebView(frame: .zero, configuration: webConfig)
        return wkWebView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        // 普通にurlでブラウズするコード
        // let req = URLRequest(url: url)
        // uiView.load(req)
 
        // ローカルファイルindex.htmlを表示するコード
        // guard let path: String = Bundle.main.path(forResource: "index", ofType: "html")
        //     else { return }
        // let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
        // uiView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
 
        let str = "<!DOCTYPE html>"
        + "<html>"
        + "<head>"
        + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>"
        + "<title>スマホでの見え方調整</title>"
        + "<meta name=\"viewport\" content=\"width=280\" />"
        + "</head>"
        + "<body>"
        +   "<input type=\"button\" value=\"Exec\" onclick=\"var number = 123;"
        +   "webkit.messageHandlers.hoge.postMessage(number);\" /><br />"
        +   "<br />"
        + "</body>"
        + "</html>"
        uiView.loadHTMLString(str, baseURL: nil)
    }
 
    func makeCoordinator() -> WebView.Coordinator {
        return Coordinator()
    }
    
} // struct
 
extension WebView {
    class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
       func userContentController(
           _ userContentController: WKUserContentController,
           didReceive message: WKScriptMessage) {
               if message.name == "hoge" {
                 let number = message.body as! Int
                 print("JavaScript is sending a number \(number)")
            }
        }
    }
}
 
struct WebView_Previews: PreviewProvider {
    static var previews: some View {
        WebView(url: URL(string: "dummy")!)
    }
}

SwiftUIでローカルのindex.htmlをWebKitで読んでjavascriptの結果をSwiftコードで読む

ContentView.swiftを変更

import SwiftUI
 
struct ContentView: View {
    var body: some View {
        WebView(url: URL(string: "dummy")!)
    }
}

WebView.swiftを新規作成

import SwiftUI
import WebKit
 
struct WebView: UIViewRepresentable {
    var url: URL
    
    func makeUIView(context: Context) -> WKWebView {
        let webConfig = WKWebViewConfiguration()
        let userController = WKUserContentController()
        userController.add(makeCoordinator(), name: "hoge")
        webConfig.userContentController = userController
        let wkWebView = WKWebView(frame: .zero, configuration: webConfig)
        return wkWebView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        // let req = URLRequest(url: url)
        // uiView.load(req)
        guard let path: String = Bundle.main.path(forResource: "index", ofType: "html") else { return }
        let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
        uiView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
    }
/*
     これだと動かない
     func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "hoge" {
            print("JavaScript is sending a message \(message.body)")
        }
    }
*/
    func makeCoordinator() -> WebView.Coordinator {
        return Coordinator()
    }
    
} // struct
extension WebView {
    class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
       func userContentController(
           _ userContentController: WKUserContentController,
           didReceive message: WKScriptMessage) {
               if message.name == "hoge" {
                 let number = message.body as! Int
                 print("JavaScript is sending a number \(number)")
            }
        }
    }
}
 
struct WebView_Previews: PreviewProvider {
    static var previews: some View {
        WebView(url: URL(string: "dummy")!)
    }
}

index.htmlを新規作成(WebView.swiftと同じフォルダーに置く)

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title></title>
  <meta charset="utf-8" />
</head>
<body>
  <input type="button" value="Exec" onclick="var number = 123; webkit.messageHandlers.hoge.postMessage(number);" /><br />
  <br />
  <div id="output"></div>
</body>
</html>

実行結果(デバッグコンソールに) JavaScript is sending a number 123
なお、ブラウザに出るボタンがめちゃちっちゃい!

画面に図形を表示するには GeometryReaderとPath

(4) 画面に図形を表示するには
(4.1) 四角を表示、大きさはおまかせ
struct ContentView : View {
var body: some View {
VStack {
  Text("Hello There!")
  Rectangle().fill(Color.blue)
}.frame(width: 150, height: 100).border(Color.black)
}
}
(4.2) 四角を表示、元の表示場所から右に+5外れて大きさを半分に
struct ContentView : View {
var body: some View {
VStack {
  Text("Hello There!")
  GeometryReader { geometry in
    Rectangle()
    .path(in: CGRect(
       x: geometry.size.width + 5,
       y: 0,
       width: geometry.size.width / 2.0,
       height: geometry.size.height / 2.0))
    .fill(Color.blue)
  }
}.frame(width: 150, height: 100).border(Color.black)
}
}
 
(4.3) 関数にした
struct ContentView : View {
var body: some View {
VStack {
  Text("Hello There!")
  MyRectangle()
}.frame(width: 150, height: 100).border(Color.black)
}
}
 
struct MyRectangle: View {
var body: some View {
GeometryReader { geometry in
  Rectangle()
  .path(in: CGRect(
    x: geometry.size.width + 5,
    y: 0,
    width: geometry.size.width / 2.0,
    height: geometry.size.height / 2.0))
  .fill(Color.blue)
  }
}
}
 
(4.4) 自由な形を表示
struct ContentView : View {
var body: some View {
HStack {
  Text("SwiftUI")
  .foregroundColor(.black).font(.title).padding(15)
  .background(RoundedCorners(color: .green, tr: 30, bl: 30))
  Text("Lab")
  .foregroundColor(.black).font(.title).padding(15)
  .background(RoundedCorners(color: .blue, tl: 30, br: 30))
} // HStack
.padding(20).border(Color.gray).shadow(radius: 3)
} // some View
} // View
 
struct RoundedCorners: View {
var color: Color = .black
var tl: CGFloat = 0.0
var tr: CGFloat = 0.0
var bl: CGFloat = 0.0
var br: CGFloat = 0.0
var body: some View {
GeometryReader { geometry in
Path { path in
  let w = geometry.size.width
  let h = geometry.size.height
  // We make sure the redius does not exceed the bounds dimensions
  let tr = min(min(self.tr, h/2), w/2)
  let tl = min(min(self.tl, h/2), w/2)
  let bl = min(min(self.bl, h/2), w/2)
  let br = min(min(self.br, h/2), w/2)
  path.move(to: CGPoint(x: w / 2.0, y: 0))
  path.addLine(to: CGPoint(x: w - tr, y: 0))
  path.addArc(
    center: CGPoint(x: w - tr, y: tr),
    radius: tr,
    startAngle: Angle(degrees: -90),
    endAngle: Angle(degrees: 0),
    clockwise: false)
  path.addLine(to: CGPoint(x: w, y: h - br))
  path.addArc(
    center: CGPoint(x: w - br, y: h - br),
    radius: br,
    startAngle: Angle(degrees: 0),
    endAngle: Angle(degrees: 90),
    clockwise: false)
  path.addLine(to: CGPoint(x: bl, y: h))
  path.addArc(
    center: CGPoint(x: bl, y: h - bl),
    radius: bl,
    startAngle: Angle(degrees: 90),
    endAngle: Angle(degrees: 180),
    clockwise: false)
  path.addLine(to: CGPoint(x: 0, y: tl))
  path.addArc(
    center: CGPoint(x: tl, y: tl),
    radius: tl,
    startAngle: Angle(degrees: 180),
    endAngle: Angle(degrees: 270),
    clockwise: false)
} // Path
.fill(self.color)
} // GeometryReader
} // some View
} // View

SwiftUIでURLを入力してボタンを押したらWEBページを表示する

ContentView.swift

import SwiftUI
 
struct ContentView: View {
@State var workUrl: String = ""
@State var fixedUrl: String = "https://www.google.com/"
var body: some View {
VStack(alignment: .leading) {
    TextField(" Enter Url (例 https://apple.com)", text: $workUrl)
    Text("\(fixedUrl)")
    Button(action: {
        self.fixedUrl = self.workUrl
    }) {
        Text("go url")
    }
    WebView(url: URL(string: fixedUrl)!)
}.padding() // VStack
} // some View
} // View
 
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

WebView.swift ContestView.swiftを変更する

import SwiftUI
import WebKit
 
struct WebView: UIViewRepresentable {
    var url: URL
    func makeUIView(context: Context) -> WKWebView {
        return WKWebView(frame: .zero)
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        let req = URLRequest(url: url)
        uiView.load(req)
    }
}
 
struct WebView_Previews: PreviewProvider {
    static var previews: some View {
        WebView(url: URL(string: "https://apple.com")!)
    }
}

SwiftUIでWEBブラウザを作るには

WebView.swiftを新しく作る

import SwiftUI
import WebKit
 
struct WebView: UIViewRepresentable {
    var url: URL
    func makeUIView(context: Context) -> WKWebView {
        return WKWebView(frame: .zero)
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        let req = URLRequest(url: url)
        uiView.load(req)
    }
}
 
struct WebView_Previews: PreviewProvider {
    static var previews: some View {
        WebView(url: URL(string: "https://apple.com")!)
    }
}

ContestView.swiftを変更する

import SwiftUI
 
struct ContentView: View {
    var body: some View {
      WebView(url: URL(string: "https://apple.com")!)
    }
}
 
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}