🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
RxSwift

RxSwiftは、Reactive ExtensionsのSwift向けの実装です。iOS開発に用いられ、リアクティブプログラミングを可能にします。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

668閲覧

APIクライアントでUnexpectedly found nil ~ と出る

退会済みユーザー

退会済みユーザー

総合スコア0

RxSwift

RxSwiftは、Reactive ExtensionsのSwift向けの実装です。iOS開発に用いられ、リアクティブプログラミングを可能にします。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/01/07 03:02

RxSwiftでAPIクライアントを作っているのですが、
Unexpectedly found nil while unwrapping an Optional value
となってしまいます。

まずモデルとデコードです。

// OkashiPage.swift struct OkashiSearchResponse: Decodable { let item: [OkashiPage] } struct OkashiPage { let name: String let maker: String let url: URL } extension OkashiPage: Decodable { private enum CodingKeys: String, CodingKey { case name case maker } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.name = try container.decode(String.self, forKey: .name) print("Name: (self.name)") self.maker = try container.decode(String.self, forKey: .maker) self.url = URL(string: "https://www.sysbird.jp/webapi/?apikey=guest&format=json&keyword=(name)")! } }

検索APIです。

// OkashiAPI.swift protocol OkashiAPI { func search(from word: String) -> Observable<[OkashiPage]> } class OkashiDefaultAPI: OkashiAPI { private let host = URL(string: "https://sysbird.jp")! private let path = "/toriko/api/" private let URLSession: Foundation.URLSession init(URLSession: Foundation.URLSession){ self.URLSession = URLSession } func search(from word: String) -> Observable<[OkashiPage]> { var components = URLComponents(url: host, resolvingAgainstBaseURL: false)! components.path = path let items = [ URLQueryItem(name: "apikey", value: "guest"), URLQueryItem(name: "keyword", value: word), URLQueryItem(name: "format", value: "json"), URLQueryItem(name: "max", value: "10") ] components.queryItems = items let request = URLRequest(url: components.url!) return URLSession.rx.response(request: request) .map { pair in do { let response = try JSONDecoder().decode(OkashiSearchResponse.self, from: pair.data) return response.item } catch { throw error } } } }

そしてViewModelとViewControllerです。

class OkashiSearchViewModel { let okashiPages: Observable<[OkashiPage]> init(searchWord: Observable<String>, okashiAPI: OkashiAPI){ okashiPages = searchWord .filter { 3 <= $0.count } .flatMapLatest { return okashiAPI.search(from: $0) .catchErrorJustReturn([]) } .share(replay: 1) } } class ViewController: UIViewController, UISearchBarDelegate { @IBOutlet weak var searchBar: UISearchBar! @IBOutlet weak var tableView: UITableView! private let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() searchBar.delegate = self searchBar.placeholder = "お菓子の名前を入力してください" let viewModel = OkashiSearchViewModel(searchWord: searchBar.rx.text.orEmpty.asObservable(), okashiAPI: OkashiDefaultAPI(URLSession: .shared)) viewModel.okashiPages .bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, result, cell in cell.textLabel?.text = result.name } .disposed(by: disposeBag) } }

問題が生じるのはextension OkashiPageの

self.url = URL(string: "https://www.sysbird.jp/webapi/?apikey=guest&format=json&keyword=(name)")!

です。

イメージ説明説明](711dd6e289423bf3cd4ef898c9a26903.jpeg)

英語だと大丈夫なのですが、数字や日本語だとエラーが出てしまいます。
url はオプショナルで、後ろで強制アンラップしているはずなのに出てしまいます。
原因はnameだと分かっているのですが、どうしたらいいのか分かりません。
どなたか解決方法を教えていただけると恐縮です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

クエリとして使う文字列に対して、あらかじめ下記のメソッドを使ってパーセントエンコーディングをかけておく必要があるかと思います。

Returns a new string made from the receiver by replacing all characters not in the specified set with percent-encoded characters.

投稿2021/01/07 07:12

TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2021/01/08 04:17

横から失礼します。addingPercentEncoding ですが、問題は withAllowedCharacters に何を指定すべきかですよね。.urlQueryAllowed あたりが使われることが多いようですが、& をエンコードしないので…。URLComponents を使った方がいいのかなとか、JavaScript の encodeURIComponent と同等のものがあればいいのにとか思ったりしてます。name が日本語だけならどれでも一緒ですけどね。
TsukubaDepot

2021/01/08 04:48

いえいえ、このようにコメントを入れて頂いた方が私も勉強になります。 withAllowedCharacters の話はどうしよう、と思っていたのですが、質問者さんが元にされているコードはなんとなくあの書籍のあの部分だったような、という気がしていましたし、そこにはaddingPercentEncoding の話も載っていたはずなので、上記の回答程度で大丈夫かなと思い端折りました(個人的にはそのコードを RxSwift として記述し直しいている方に感銘しました)。 とはいえ、「.urlQueryAllowed あたりが使われることが多いようですが、& をエンコードしない」という話は確かにその通りだと思ったので勉強になりました。ありがとうございます。
TsukubaDepot

2021/01/08 05:16

拝見しました。素晴らしすぎます。もはや回答にされた方が良いかもしれません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問