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

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

新規登録して質問してみよう
ただいま回答率
85.35%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

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

Q&A

解決済

1回答

842閲覧

do-catch内にてselfエラー【URLSessionを他クラスから呼び出したい。】

kazuki_user

総合スコア147

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

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

0グッド

0クリップ

投稿2020/09/09 13:35

編集2020/09/10 03:08

## やりたいこと

SearchRootVCクラスにて、URLSessionのコードをRepositoryApiクラスから呼び出したい。

SearchRootVC

1 var repositoryApi = RepositoryApi() 2 3... 4 5 func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { 6 repositoryApi.getRandomRepoUrlSession() 7 }

RepositoryApi

1 2// URLSessionの処理

## selfが原因の、EXC_BAD_ACCESSが出る

EXC_BAD_ACCESSエラーにて、

self = CodeCheck_Test_Yumemi.SearchRootVC 0x00007ff8c083db00だと エラー原因が特定出来たので、

下記を記述してselfの使用を避けようとしましたが、引き続きEXC_BAD_ACCESSでクラッシュします...

RepositoryApi

1var searchRootVC = SearchRootVC()

## URLSessionコード

なお、呼び出しをせずにSearchRootVCのみで実行すると
EXC_BAD_ACCESSは無く、正常に動作します。

RepositoryApi

1import UIKit 2 3class RepositoryApi: SearchRootVC { 4 5 var searchRootVC = SearchRootVC() 6 7 8 func getRandomRepoUrlSession() { 9 10 let word = searchBar.text! 11 let REPOSITORY_URL = URL_BASE + "(word)" 12 13 // nilは許さない。urlの強制アンラップを修正。 14 guard let url = URL(string: REPOSITORY_URL) else { return } 15 16 let task = URLSession.shared.dataTask(with: url) { (data, responce, error) in 17 18 guard error == nil else { 19 debugPrint(error.debugDescription) 20 return 21 } 22 23 // dataの強制アンラップを修正。 24 guard let data = data else { return } 25 26 // try!は、例外が発生したときにはクラッシュするので修正。(-> エラーが起こり得ないケースでのみ使用可) 27 // try?で例外を安全に無視できるが、エラーを表示するため do-catch を使用。 28 29 do { 30 let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] 31 if let items = json?["items"] as? [[String: Any]] { 32 self.searchRootVC.repo = items 33 // DispatchQueue で一つ以上のタスクを管理し、async で複数のAPIの非同期通信を実行。 34 DispatchQueue.main.async { 35 // UIを更新する処理 36 self.searchRootVC.tableView.reloadData() 37 } 38 } 39 } catch { 40 debugPrint(error.localizedDescription) 41 return 42 } 43 } 44 // 新しく初期化されたタスクは一時停止状態で開始されるため、このメソッドを呼び出してタスクを開始する必要がある。 45 task.resume() 46 } 47 48} 49 50

SearchRootVC全文

SearchRootVC

1import UIKit 2 3class SearchRootVC: UITableViewController, UISearchBarDelegate { 4 5 @IBOutlet weak var searchBar: UISearchBar! 6 7 // Var 8 var word: String! 9 var url: String! 10 var RepoToPass: Int! 11 12 var task: URLSessionTask? 13 var repo: [[String: Any]]=[] 14 15 var repositoryApi = RepositoryApi() 16 17 override func viewDidLoad() { 18 super.viewDidLoad() 19 setupTableView() 20 } 21 22 // 以下3つ、元から用意されているsearchBar関数名なので、変更NG。 23 func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool { 24 searchBar.text = "" 25 return true 26 } 27 28 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { 29 task?.cancel() 30 } 31 32 func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { 33 repositoryApi.getRandomRepoUrlSession() 34 } 35 36 func setupTableView() { 37 // UISearchBarのdelegateプロパティに、self(=SearchRootVC)を代入。 38 searchBar.delegate = self 39 } 40 41 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 42 if segue.identifier == Segues.ToProfileDetail { 43 if let detailVC = segue.destination as? ProfileDetailVC { 44 detailVC.selectedUser = self 45 } 46 } 47 } 48} 49 50 51// extension 52 53extension SearchRootVC { 54 55 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 56 return repo.count 57 } 58 59 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 60 61 // dequeueReusableCellで、セルを再利用。 62 // nilを返さない為、オプショナルバインディングは不要。 63 64 let cell: RepositoryCell = tableView.dequeueReusableCell(withIdentifier: Identifiers.RepositoryCell, for: indexPath) as! RepositoryCell 65 let UserRepo = repo[indexPath.row] 66 cell.configureCell(UserRepo) 67 cell.tag = indexPath.row 68 return cell 69 } 70 71 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 72 RepoToPass = indexPath.row 73 performSegue(withIdentifier: Segues.ToProfileDetail, sender: self) 74 } 75} 76

質問は以上です。
お時間あるときに、ご返信頂けましたら幸いです????

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

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

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

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

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

TsukubaDepot

2020/09/10 00:14

RepositoryApi は、SearchRootVCにかなり依存しているようなので、SearchRootVC が何者かわからないと回答できないと思います。 ご自身で作られたクラスであれば、依存関係はおわかりだと思うので、もう少し整理できるかと思いますが、そうでないならば、まずは依存関係を整理されたらいかがでしょうか。
TsukubaDepot

2020/09/10 03:13

質問の変更履歴ですが、適当なタイトル「あ、とかaとか」にせず、適切なタイトルをつけることはできないのでしょうか。 改変履歴は誰でも参照できますが、適当なタイトルだと何が変わったのかひと目ではわからないため大変困ります。
TsukubaDepot

2020/09/10 03:14

ちなみに、RepositoryApiのベースクラス(継承元)が SearchRootVC なのは、どういう意図からなのでしょうか。
kazuki_user

2020/09/10 03:20 編集

申し訳ありませんでした。変更タイトルに関して以後、気を付けます。????‍♂️ 質問に添った回答か分かりませんが.. 現在、コードに対してリファクタリングを行っており、 SearchRootVCにて、レポジトリ検索画面の主な機能(searchBarShouldBeginEditingなど) を実装しております。 URLSessionを他のファイルに分割する為、RepositoryApiを新たに作りURLSessionを記述し、 それをSearchRootVCから呼び出したいと考えています。
TsukubaDepot

2020/09/10 03:21

クラスの継承、という考え方は理解されていますでしょうか。 また、個別のクラス間におけるプロパティ参照の理屈についても理解されていますでしょうか。
kazuki_user

2020/09/10 03:27 編集

: で継承し、親の機能を参照できる..という認識です。 個別のクラス間におけるプロパティ参照に関しては、少し自信がないかもです.. 今回のエラーは、self含め、これらが原因でしょうか? selectedUserがnilで、Unexpectedly found nilエラーに関してもそうですが、 別ファイル間の行き来の問題でエラーが起こっているように思われます????
TsukubaDepot

2020/09/10 03:34

繰り返しになりますが、文法全般の理解が不足していることが原因かと思います。 また、不用意にクラスを継承させたことが原因で、見えなくてもいい変数が見えてしまい、それがかえって混乱を招いている雰囲気があります。 また、型や定数などの名付け方もSwiftの流儀に沿っていないところがあるので読みにくいところがあります。 リファクタリングは重要なことかと思いますが、どこまでを一般化するのか整理しないと、かえって使いづらくなるのではないでしょうか。 たとえば、RepositoryApiから親クラスの tableView などが見えていますが、見えているからといって具体的なインスタンス(値)が入っているわけではありません。プロパティやメソッドは経由しますが、プロパティに対して動的に代入される値までは継承されません。 なので、そのあたりをきちんと考えてリファクタリングする必要があります。
TsukubaDepot

2020/09/10 03:35

ちなみに、このアプリはどこのWebAPIを検索して(つまり、URL_BASEはどんなURLにアクセスして)結果を表示しようとしたのでしょうか。
guest

回答1

0

ベストアンサー

現状、あえてクラスを作るほどでもない感じもするのですが、たとえばこんな感じでしょうか。

Swift

1import UIKit 2 3class SearchRootVC: UITableViewController, UISearchBarDelegate { 4 @IBOutlet weak var searchBar: UISearchBar! 5 6 // MARK: - 以下のプロパティは使わないのでコメントアウト 7 // また、不用意に Implicitly Unrapped Optional を使わない 8 // Var 9 //var word: String! 10 //var url: String! 11 12 // MARK: - 変数名は大文字始まりにしない 13 //var RepoToPass: Int! 14 //var repoToPass: Int! 15 16 var task: URLSessionTask? 17 var repo: [[String: Any]] = [] 18 19 20 override func viewDidLoad() { 21 super.viewDidLoad() 22 setupTableView() 23 } 24 25 // 以下3つ、元から用意されているsearchBar関数名なので、変更NG。 26 func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool { 27 searchBar.text = "" 28 return true 29 } 30 31 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { 32 task?.cancel() 33 } 34 35 func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { 36 searchBar.resignFirstResponder() 37 38 // MARK: - UITextField がからの場合には検索しない 39 //let word = searchBar.text! 40 guard let word = searchBar.text, word.isEmpty == false else { 41 print("検索文字がない") 42 return 43 } 44 45 task = SearchAPI.getRandomRepoUrlSession(word, completionHandler: { items in 46 self.repo = items 47 // DispatchQueue で一つ以上のタスクを管理し、async で複数のAPIの非同期通信を実行。 48 DispatchQueue.main.async { 49 // UIを更新する処理 50 self.tableView.reloadData() 51 } 52 }, errorHandler: { error in 53 debugPrint(error?.localizedDescription) 54 }) 55 } 56 57 func setupTableView() { 58 // UISearchBarのdelegateプロパティに、self(=SearchRootVC)を代入。 59 searchBar.delegate = self 60 61 // MARK: - TableViewCell は StoryBoard で追加する 62 // Identifier は Cell 63 // Style は Subtitle 64 } 65 66 // MARK: - 画面遷移は今回は行わない 67 // override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 68 // if segue.identifier == Segues.ToProfileDetail { 69 // if let detailVC = segue.destination as? ProfileDetailVC { 70 // detailVC.selectedUser = self 71 // } 72 // } 73 // } 74} 75 76 77// extension 78 79extension SearchRootVC { 80 81 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 82 return repo.count 83 } 84 85 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 86 87 // dequeueReusableCellで、セルを再利用。 88 // nilを返さない為、オプショナルバインディングは不要。 89 90 // MARK: - 今回は標準の TableViewCell を使う 91 //let cell: RepositoryCell = tableView.dequeueReusableCell(withIdentifier: Identifiers.RepositoryCell, for: indexPath) as! RepositoryCell 92 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) 93 94 // MARK: - 大文字始まりはクラス名でのみ使う。変数名は小文字始まり 95 //let UserRepo = repo[indexPath.row] 96 let userRepo = repo[indexPath.row] 97 98 //MARK: - GitHub から得られる情報のうち、"name" と "description" だけ表示させる 99 if let userName = userRepo["name"] as? String { 100 cell.textLabel?.text = userName 101 } else { 102 cell.textLabel?.text = "(名無し)" 103 } 104 105 if let description = userRepo["description"] as? String { 106 cell.detailTextLabel?.text = description 107 } else { 108 cell.detailTextLabel?.text = "(詳細無し)" 109 } 110 111 // MARK: - とりあえず以下はコメントアウト 112 // cell.configureCell(UserRepo) 113 // cell.tag = indexPath.row 114 return cell 115 } 116 117 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 118 // MARK: - 画面遷移は今回は行わない 119 //repoToPass = indexPath.row 120 //performSegue(withIdentifier: Segues.ToProfileDetail, sender: self) 121 } 122} 123 124// MARK: - クラスメソッドとして定義 125class SearchAPI { 126 static private let githubBaseUrl = "https://api.github.com/search/repositories?q=" 127 static func getRandomRepoUrlSession(_ word: String, completionHandler completion: @escaping ([[String: Any]]) -> (), errorHandler: @escaping (Error?) -> ()) -> URLSessionTask? { 128 // MARK: - 下記の部分はメソッドに渡す前に処理しておく 129 // let word = searchBar.text! 130 // guard let word = searchBar.text, word.isEmpty == false else { 131 // print("検索文字がない") 132 // return 133 // } 134 135 // MARK: - 変数名を全て大文字にしない(流儀) 136 //let REPOSITORY_URL = URL_BASE + "(word)" 137 let repositoryUrl = githubBaseUrl + word 138 139 // nilは許さない。urlの強制アンラップを修正。 140 guard let url = URL(string: repositoryUrl) else { return nil } 141 142 143 //let task = URLSession.shared.dataTask(with: url) { (data, responce, error) in 144 let task = URLSession.shared.dataTask(with: url) { (data, responce, error) in 145 146 guard error == nil else { 147 // MARK: - 処理をクロージャに任せる 148 errorHandler(error) 149 // MARK: - 以下の処理はクロージャに任せる 150 // debugPrint(error.debugDescription) 151 return 152 } 153 154 // dataの強制アンラップを修正。 155 guard let data = data else { return } 156 157 // try!は、例外が発生したときにはクラッシュするので修正。(-> エラーが起こり得ないケースでのみ使用可) 158 // try?で例外を安全に無視できるが、エラーを表示するため do-catch を使用。 159 160 do { 161 let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] 162 if let items = json?["items"] as? [[String: Any]] { 163 // MARK: - 処理をクロージャに任せる 164 completion(items) 165 // MARK: - 以下の処理はクロージャに任せる 166 // self.repo = items 167 // // DispatchQueue で一つ以上のタスクを管理し、async で複数のAPIの非同期通信を実行。 168 // DispatchQueue.main.async { 169 // // UIを更新する処理 170 // self.tableView.reloadData() 171 // } 172 } 173 } catch { 174 // MARK: - 処理をクロージャに任せる 175 errorHandler(error) 176 // MARK: - 以下の処理は クロージャに任せる 177 // debugPrint(error.localizedDescription) 178 // return 179 } 180 } 181 // 新しく初期化されたタスクは一時停止状態で開始されるため、このメソッドを呼び出してタスクを開始する必要がある。 182 task.resume() 183 184 return task 185 } 186}

投稿2020/09/10 05:01

TsukubaDepot

総合スコア5086

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

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

TsukubaDepot

2020/09/10 05:02

変更点の詳細はコメントをご参照ください。 また、TableViewCell については、StoryBoard で作り必要がありますが、詳細については、同じくコメントをご参照ください。
kazuki_user

2020/09/10 05:13

勉強になります。 Implicitly Unrapped Optional始め、Swiftの文法基礎についてしっかり学び直します。 また、おかしな点がありましたら、ご指摘頂けると嬉しいです。
TsukubaDepot

2020/09/10 05:14

udemyで色々と学ばれていると思いますが、まずはごく簡単なアプリから(たとえば、TableViewならTableView、画面遷移なら遷移だけを行うアプリなど)を作り、一つひとつのパーツなどの動作についてクリアしてから組み合わせたほうが、結局は習得が早いかと思います。
kazuki_user

2020/09/10 05:17

なるほどです????‍♂️、了解しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問