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

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

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

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

Q&A

解決済

1回答

2285閲覧

Alamofireでネットワーク通信中にViewへデータを書き込みたい

takhub

総合スコア28

Swift

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

0グッド

0クリップ

投稿2016/09/19 23:49

編集2016/09/20 11:23

お世話になります。長文になりますがお許しください。

###前提・実現したいこと
Swiftで栄養管理アプリを作っています。
大まかな機能としては、バーコードでスキャンした製品の栄養バランスをグラフ化するというものです。
FatSecretという全世界の製品を網羅する栄養データベースにアクセスし、そのデータをグラフに反映させようとしています。

###実現したい動作フロー
下記フローの中で4の箇所を実現するために、まずはサンプルデータで上手くいくかどうかを試しています。
0. scanViewControllerクラスのfoundCodeメソッドでバーコード番号読み取り
0. nutritionDisplayViewControllerクラスのfoodFindIdForBarcodeメソッドへ1を渡し、製品IDを取得
0. nutritionDisplayViewControllerクラスのfoodGetメソッドへ2を渡し、栄養パラメータを取得
0. BarChartViewクラスに3を投げてグラフを作成
0. scanViewControllerクラスに戻り、transitionメソッドでnutritionDisplayViewControllerクラスへ画面遷移

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

FatSecretのデータベースへアクセス中にView(barChartView)へグラフを書き込もうとすると下記エラーが発生してしまいます。

fatal error: unexpectedly found nil while unwrapping an Optional value

エラーが発生している箇所は特定できているのですが、原因がわからず困っております。何卒よろしくお願いします。

またUI上では、スキャン後に画面遷移する仕様で画面遷移はするのですが、下記のメッセージが表示されてしまいます。(投稿者自身が、データがないときにこのメッセージを表示するようにしています)

"You need to provide data for the chart"

###該当のソースコード

swift

1// scanViewController.swift 2 3// バーコード番号を読み取る関数 4public func foundCode(scannedBarcode: String) { 5 nutritionDisplayViewController().foodFindIdForBarcode(scannedBarcode) 6 //省略 7 transition() 8} 9 10func transition() { 11 let next = self.storyboard?.instantiateViewControllerWithIdentifier("modal") as! nutritionDisplayViewController 12 self.navigationController?.pushViewController(next, animated: true) 13 //print(test) 14}

swift

1// nutritionDisplayViewController.swift 2 3public class nutritionDisplayViewController: UIViewController, ChartViewDelegate { 4 5 @IBOutlet weak var barChartView: BarChartView! 6 7 var months: [String]! //サンプルデータ 8 var nutritions: [[String: String?]] = [] //FatSecretからとってくる栄養パラメータ 9 10 override public func viewDidLoad() { 11 super.viewDidLoad() 12 // Do any additional setup after loading the view, typically from a nib. 13 } 14 15 // 製品のバーコードに紐づけられた独自のIDを取得 16 public func foodFindIdForBarcode(scannedBarcode:String) { 17 18 let URL = "http://platform.fatsecret.com/rest/server.api" 19 var parameters = [String: String]() 20 21 parameters["format"] = "json" 22 parameters["method"] = "food.find_id_for_barcode" 23 parameters["barcode"] = scannedBarcode 24 25 parameters["oauth_version"] = "1.0" 26 //省略 27 parameters["oauth_signature"] = oauthSignatureForMethod("GET", url: NSURL(string: URL)!, parameters: parameters) 28 29 // FatSecretにリクエスト 30 Alamofire.request(.GET, URL, parameters: parameters) 31 .responseJSON { response in 32 guard let object = response.result.value else { 33 return 34 } 35 let json = JSON(object) 36 json.forEach { (_, json) in 37 let nutrition: [String: String?] = [ 38 "barcode": json["barcode"].string 39 ] 40 self.nutritions.append(nutrition) 41 } 42 self.foodGet(json["food_id"]["value"].string!) 43 44 if let JSON = response.result.value { 45 print("JSON: \(JSON)") 46 } 47 } 48 } 49 50 // foodFindIdForBarcode()で取得したIDをもとに栄養パラメータを取得(この関数内でのネットワーク通信中にエラーが発生しています) 51 public func foodGet(fid:String) { 52 53 //省略(foodFindIdForBarcodeと同様) 54 55 Alamofire.request(.GET, URL, parameters: parameters) 56 .responseJSON { response in 57 guard let object = response.result.value else { 58 return 59 } 60 let json = JSON(object) 61 json.forEach { (_, json) in 62 let nutrition: [String: String?] = 63 ["food_id": json["food_id"]["value"].string, 64 "brand_name": json["food"]["brand_name"].string, 65 "vitamin_c": json["food"]["servings"]["serving"]["vitamin_c"].string 66 ] 67 self.nutritions.append(nutrition) 68 } 69 70 if let JSON = response.result.value { 71 print("JSON: \(JSON)") 72 } 73 74 /*********************/ 75 self.barChartView.delegate = self //ここで "fatal error: unexpectedly found nil while unwrapping an Optional value" 76 /*********************/ 77 78 self.months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 79 let unitsSold = [20.0, 4.0, 6.0, 3.0, 12.0, 16.0, 4.0, 18.0, 2.0, 4.0, 5.0, 4.0] 80 81 self.setChart(self.months, values: unitsSold) 82 self.barChartView.descriptionText = "" 83 self.barChartView.xAxis.labelPosition = .Bottom 84 self.barChartView.backgroundColor = UIColor(red: 189/255, green: 195/255, blue: 199/255, alpha: 1) 85 86 self.barChartView.animate(xAxisDuration: 2.0, yAxisDuration: 2.0, easingOption: .EaseInBounce) 87 88 let ll = ChartLimitLine(limit: 10.0, label: "Target") 89 } 90 viewDidAppear(true) 91 } 92 93 override public func viewDidAppear(animated: Bool) { 94 } 95 96 /*----グラフ作成(ios-charts参考)----*/ 97 public func setChart(dataPoints: [String], values: [Double]) { 98 barChartView.noDataText = "You need to provide data for the chart" 99 100 var dataEntries: [BarChartDataEntry] = [] 101 102 for i in 0..<dataPoints.count { 103 let dataEntry = BarChartDataEntry(value: values[i], xIndex: i) 104 dataEntries.append(dataEntry) 105 } 106 107 let chartDataSet = BarChartDataSet(yVals: dataEntries, label: "Units Sold") 108 let chartData = BarChartData(xVals: months, dataSet: chartDataSet) 109 110 chartDataSet.colors = ChartColorTemplates.colorful() 111 112 barChartView.data = chartData 113 } 114 115 public func chartValueSelected(chartView: ChartViewBase, entry: ChartDataEntry, dat![イメージ説明](2d825031817cd81197be20b7d407c8eb.png)dex: Int, highlight: ChartHighlight) { 116 print("\(entry.value) in \(months[entry.xIndex])") 117 } 118 119 /*---- OAuth関連 ----*/ 120 //省略 121

###Storyboard
Storyboard全体のイメージです。nutritionDisplayViewControllerを選択しています。

イメージ説明

nutritionDisplayViewController内のBarChartViewを選択しています。

イメージ説明

###試したこと
1.nilとなっている箇所の特定


エラー箇所特定のため、print("(1)")とprint("(2)")を書いてみたところ、(1)のみ表示されるため、self.barChartView.delegate = selfが上手くいっていないと思います。
nilが返されていると思われるself.barChartView.delegateの情報を見ようと、print(self.barChartView.delegate)で出力してみましたが、何も出力されません。

2.viewDidLoad内への移動


下記の部分をviewDidLoad内に書いてみました。動作するものの、これではネットワーク通信で取得したデータを反映させようがないのでダメですね。

swift

1self.barChartView.delegate = self 2self.months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 3 let unitsSold = [20.0, 4.0, 6.0, 3.0, 12.0, 16.0, 4.0, 18.0, 2.0, 4.0, 5.0, 4.0] 4 5self.setChart(self.months, values: unitsSold) 6self.barChartView.descriptionText = "" 7self.barChartView.xAxis.labelPosition = .Bottom 8self.barChartView.backgroundColor = UIColor(red: 189/255, green: 195/255, blue: 199/255, alpha: 1) 9 10self.barChartView.animate(xAxisDuration: 2.0, yAxisDuration: 2.0, easingOption: .EaseInBounce) 11 12let ll = ChartLimitLine(limit: 10.0, label: "Target")

3.response.dataの確認


2016-09-20 12:46:13.107 FoodAPISample[2316:1015696] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. ( "<NSLayoutConstraint:0x144d93660 V:|-(20)-[UIInputSetContainerView:0x144d91820] (Names: '|':UITextEffectsWindow:0x144d8eab0 )>", "<NSLayoutConstraint:0x144d7fc70 'UIInputWindowController-top' V:|-(0)-[UIInputSetContainerView:0x144d91820] (Names: '|':UITextEffectsWindow:0x144d8eab0 )>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0x144d93660 V:|-(20)-[UIInputSetContainerView:0x144d91820] (Names: '|':UITextEffectsWindow:0x144d8eab0 )> Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

###補足情報(言語/FW/ツール等のバージョンなど)
#####バージョン情報
Alamofire 3.4
Charts 2.2.5
XCode 7
iOS 9.3.5

#####利用しているライブラリ
ios-charts
Alamofire
SwiftyJSON

#####利用しているAPI
FatSecret Platform REST API

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

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

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

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

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

guest

回答1

0

ベストアンサー

そもそも、通信は関係ありますか?
単にios-charts 関連の利用方法あるいは、画面遷移に関する理解のような気がします。

viewDidLoadで動作するということは、後者のように思います。
で、storyboardの画面キャプチャを見ると、nutritionDisplayViewControllerの画面には
どのように遷移するのかがわかりませんでした。
scanviewControllerが初期画面として、そこからどのように画面遷移するのでしょうか。
この画面遷移がうまく設定できていないため、delegateで渡すselfがきちんと設定されていないと思われます。

投稿2016/09/20 06:06

t_obara

総合スコア5488

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

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

takhub

2016/09/20 10:05

tomohiro_obaraさん、回答くださりありがとうございます。 冷静に考えると、仰る通り画面遷移の問題でした。 投稿欄に画面遷移を含め「実現したい動作フロー」を記入いたしましたが、そもそもこれに無理があるのかもしれません。画面遷移を先にして、そのあとにグラフを描画するようなフローで再度検討してみます。
takhub

2016/09/20 10:37

上記の流れでやろうとしましたが結局、同様の箇所で躓いてしまいます。 外部のクラス(nutritionDisplayViewController)に紐付いているview(barChartView)へアクセスするには、どうすればよいのでしょうか? barChartView.delegate = nutritionDisplayViewController()でやってみましたが、エラーは同様(fatal error: unexpectedly found nil while unwrapping an Optional value )です。 そもそもこのやりかただとAlamofireの外に出てしまうため、データベースから取得した値をグラフへ反映させることができなさそうなのですが...。
takhub

2016/09/20 13:30

foodGet()内、Alamofireの外でもグラフ作成に失敗しているため、やはり通信は関係ないようです。単純に、関数内からライブラリの呼び出しができていないという状況です。
t_obara

2016/09/21 02:15

viewDidLoadでうまくいくのであれば、画面遷移をする元の画面にnutritionDisplayViewControllerに紐付いている画面に遷移する処理を入れて、 nutritionDisplayViewControllerのviewDidLoadで処理を行えば良いと思いますが、 それでも動作しないのですか? すでに質問内容と異なっているので、別の質問タイトルにして質問しない押したほうが、Chartの使い方と言う観点でよくご存知の人が助けてくれるかもしれません。
takhub

2016/09/21 02:38

コメントありがとうございます。 確かにviewDidLoadでグラフ作成の処理を行うことで画面遷移後にグラフは表示されますが、viewDidLoadに処理を書いてしまうと通信で取得した値を反映させることができないのではないかと思い、Alamofire内でグラフ作成の処理を書くなど他のやり方を考えています。 別の質問タイトルで質問し直させていただきますね。
takhub

2016/09/21 03:30

そもそもAPIとの通信結果を自由に扱えていないことが問題だったことを思い出し、下記の質問にさせていただきました。https://teratail.com/questions/48770
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問