質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.50%
OAuth 2.0

OAuth 2.0(Open Authorization 2.0)は、APIを通して保護されたリソース(サードパーティのアプリケーション)へアクセスする為のオープンプロトコルです。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

1回答

1726閲覧

Qiita APIを用いたOAuth認証の実装・画面遷移

nesheep

総合スコア0

OAuth 2.0

OAuth 2.0(Open Authorization 2.0)は、APIを通して保護されたリソース(サードパーティのアプリケーション)へアクセスする為のオープンプロトコルです。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

0クリップ

投稿2021/10/15 07:40

編集2022/01/12 10:55

前提・実現したいこと

iOSアプリ開発初心者です。
QiitaのAPIを利用した、OAuth認証機能のあるアプリの実装で詰まっています。

①WKWebViewで認証画面を表示

②許可画面を押下

③アクセストークンを取得し、WKWebViewを閉じて画面遷移

を行いたいのですが、②の許可画面を押したところでクラッシュし、以下のメッセージが表示されます。

※QiitaのアカウントページでのリダイレクトURL、及びXcode内「info.plist」ファイルでのURL Schemeの設定はしている状態です。

発生している問題・エラーメッセージ

WebPageProxy::didFailProvisionalLoadForFrame: frameID = 3, domain = WebKitErrorDomain, code = 102

該当のソースコード(認証ページを表示する

SWift

1 2import UIKit 3import WebKit 4import Foundation 5import Alamofire 6import SwiftyJSON 7 8class OauthPageViewController: UIViewController, WKNavigationDelegate { 9 10 @IBOutlet weak var authWebView: WKWebView! 11 12 var code: String = "" 13 var myClientId: String = "" 14 var mySecretKey: String = "" 15 16 override func viewDidLoad() { 17 super.viewDidLoad() 18 setProperty() 19 showOauthWebView() 20 } 21 22 //settings.plistからclientID,clientSecretの取得 23 func setProperty() { 24 var oauthproperty: Dictionary<String, String> = [:] 25 guard let propertypath = Bundle.main.path(forResource: "settings", ofType: "plist") else { 26 return 27 } 28 let configurations = NSDictionary(contentsOfFile: propertypath) 29 if let datasourceDictionary: [String : String] = configurations as? [String : String] { 30 oauthproperty = datasourceDictionary 31 } 32 if let clientId: String = oauthproperty["qiitaClientId"] { 33 myClientId = clientId 34 } 35 36 if let secretKey: String = oauthproperty["qiitaSecretKey"] { 37 mySecretKey = secretKey 38 } 39 print(mySecretKey) 40 print(myClientId) 41 } 42 //認証画面表示処理 43 func showOauthWebView() { 44 guard let qiitaOauthUrl = URL(string: "https://qiita.com/api/v2/oauth/authorize?client_id=(myClientId)&scope=read_qiita") else { return } 45 let qiitaOauthRequest = URLRequest(url: qiitaOauthUrl) 46 authWebView.load(qiitaOauthRequest) 47 } 48 49 //URLからのコード取得処理 50 func getQueryStringParameter(url: String, param: String) -> String? { 51 guard let url = URLComponents(string: url) else { 52 return nil 53 } 54 return url.queryItems?.first(where: { $0.name == param })?.value 55 } 56 57 //ログイン後、コールバックURLの処理。 58 func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping(WKNavigationActionPolicy) -> Void) { 59 if navigationAction.request.url?.scheme == "cm-pgqiita" { 60 self.dismiss(animated: true, completion: nil) 61 62 let code = getQueryStringParameter(url: (navigationAction.request.url?.absoluteString)!, param: "code") 63 64 getAccessToken(code: code, completion: nil) 65 66 decisionHandler(WKNavigationActionPolicy.allow); 67 } 68 } 69 70 71// アクセストークン取得処理 72 func getAccessToken(code: String!, completion: (() -> Void)?) { 73 self.code = code 74 let url = "https://qiita.com/api/v2/access_tokens" 75 76 let headers = [ 77 "Content-Type": "application/json" 78 ] 79 80 let parameters: Parameters = [ 81 "client_id": myClientId, 82 "client_secret": mySecretKey, 83 "code": self.code 84 ] 85 86 Alamofire.request(url, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseJSON { response in 87 switch response.result { 88 case .success: 89 let json = JSON(response.result.value!) 90 guard let accessToken = json["token"].string else { return } 91 print(accessToken) 92 UserDefaults.standard.setValue(accessToken, forKey: "ACCESS_TOKEN") 93 completion?() 94 case .failure(let error): 95 print(error) 96 } 97 } 98 } 99 100} 101

試したこと

参考にしたのは以下のページです。
https://qiita.com/yyokii/items/87d4975716f1a6ed620c

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

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

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

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

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

guest

回答1

0

Apple Developer Documentation の webView(_:decidePolicyFor:decisionHandler:) メソッドについてのページでは、このメソッド内のどこかで必ず decisionHandler を呼び出すようにしなければならないと言われています。

If you implement this method, always execute the decisionHandler block at some point.

webView(_:decidePolicyFor:decisionHandler:) | Apple Developer Documentation

けれども、質問の中に掲載されたコードでは、if文の条件式を満たさない場合には decisionHandler が呼び出されていません。

あまり自信がないですが、if文の条件式を満たす場合には、 getAccessToken(code:completion:) メソッドの中で新たな通信処理が始まるので、 decisionHandler(WKNavigationActionPolicy.cancel) とするのが正しいのではないかなと予想しています。
また、if文の条件式を満たさない場合に decisionHandler(WKNavigationActionPolicy.allow) と呼び出す処理も追加する必要がありそうです。

diff

1 func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping(WKNavigationActionPolicy) -> Void) { 2 if navigationAction.request.url?.scheme == "cm-pgqiita" { 3 self.dismiss(animated: true, completion: nil) 4 5 let code = getQueryStringParameter(url: (navigationAction.request.url?.absoluteString)!, param: "code") 6 7 getAccessToken(code: code, completion: nil) 8 9- decisionHandler(WKNavigationActionPolicy.allow); 10+ decisionHandler(WKNavigationActionPolicy.cancel) 11+ return 12 } 13+ decisionHandler(WKNavigationActionPolicy.allow) 14 }

投稿2021/11/09 15:08

編集2021/11/09 16:44
__k_san__

総合スコア177

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問