まえがき
初めまして。ペンギン村の通行人k_miyasakaです。
APIKitを使用してGoogle Suggest APIからXMLを取得する方法をまとめました。
結果
"hello"という文字に対するサジェスト結果をパースしてテーブルに表示
xml取得部分はこんな感じのコードになりました。
import APIKit import SwiftyXMLParser /* リクエスト */ struct GoogleSuggestionRequest: Request { typealias Response = [String] var baseURL: URL = URL(string: "https://www.google.com")! var path: String = "/complete/search" var method: HTTPMethod = .get var parameters: Any? = ["q": "hello", "hl": "ja", "output": "toolbar"] var dataParser: DataParser = MyXMLParser() func response(from object: Any, urlResponse: HTTPURLResponse) throws -> [String] { return object as! [String] // 強気😼 } } /* DataParser実装クラス */ class MyXMLParser: DataParser { public var contentType: String? { return "application/xml" } public func parse(data: Data) throws -> Any { guard let xmlString = String(data: data, encoding: .shiftJIS) else {throw ResponseError.unexpectedObject("文字列にキャストできなかった。")} let xml = try SwiftyXMLParser.XML.parse(xmlString) let suggestion: [String] = xml["toplevel", "CompleteSuggestion"].map{ $0["suggestion"]}.flatMap{$0.attributes["data"]} if suggestion.isEmpty { throw ResponseError.unexpectedObject("候補なし") } return suggestion } } /* 適当な通信するクラス */ class Connector { static func getSuggestion() { let request = GoogleSuggestionRequest() Session.send(request){result in switch result { case .success(let suggestion): print(suggestion) // 成功した時の結果 case .failure(let error): print(error) // 通信、パースのエラーはここでハンドリング } } } }
つまづいたポイント
"https"から始まるURLじゃないと通信できない。
AppleによるATS(App Transport Security)のため、
”http“で始まるURLには原則アクセスできません。
以下のようなエラーログが出力されます。
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.String(data: data, encoding: .shiftJIS)
独自のXMLパーサーを実装する必要がある。
APIKitのRequestプロトコルはdataParserというプロパティを持っています。
(APIKitのDocumentにはたぶん記載がなく、コードを見て知りました。)
明示的に指定しない場合はデフォルトでJSONのパースを実行するようになっています。
(この際Requestクラスのresponse
メソッドは実行されません。)
デフォルト実装のままxmlを取得するとパースに失敗し以下のようなエラーとなります。
responseError(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text
XMLを取得する場合はdataParserプロパティにDataParserプロトコルに準拠した独自のXMLParserを代入する必要があります。
var dataParser: DataParser = MyXMLParser()
また、今回はSwiftyXMLParserというライブラリを使用しXMLをパースしました。
文字エンコーディングに気をつけよう
取得したData型をString型に直す時は文字エンコーディングの形式を指定する必要があります。
String(data: data, encoding: .shiftJIS)
指定するエンコーディングはresponseメソッドの引数のurlResponseに格納されており、 print(urlResponse.textEncodingName)のようにして確認しました。