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

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

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

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

API

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

Q&A

解決済

1回答

796閲覧

APIで取得した情報をローカル通知に載せたい

takaat

総合スコア4

Swift

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

API

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

0グッド

0クリップ

投稿2021/04/28 13:29

前提・実現したいこと

SwiftUIにて、指定した時間になるとローカル通知が発火し、その通知の本文などにAPIで取得した文字列を載せたいです。

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

通知が発火し、APIから文字列を取得しているところまで確認ができたのですが、肝心の通知には取得した文字列が反映されず、「初期値」のままになります。

該当のソースコード

SwiftUI

1import Foundation 2 3class APIRun: NSObject,ObservableObject,XMLParserDelegate{ 4 5 @Published var meigen = "初期値" 6 @Published var auther = "初期値" 7 private var element = "" 8 9 func getMeigen(){ 10 11 guard let req_url = URL(string: "http://meigen.doodlenote.net/api?c=1") else{ return } 12 13 let req = URLRequest(url: req_url) 14 let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main) 15 16 let task = session.dataTask(with: req,completionHandler: {(data,response,error) in session.finishTasksAndInvalidate() 17 guard let data = data else{ return } 18 let parser = XMLParser(data: data) 19 parser.delegate = self 20 parser.parse() 21 }) 22 23 task.resume() 24 } 25 26 func parserDidStartDocument(_ parser: XMLParser) { 27 28 } 29 30 func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { 31 element = elementName 32 if element == "response"{ 33 meigen = "" 34 auther = "" 35 } 36 } 37 38 func parser(_ parser: XMLParser, foundCharacters string: String) { 39 switch element { 40 case "meigen": 41 meigen += string 42 case "auther": 43 auther += string 44 default: 45 break 46 } 47 } 48 49 func parserDidEndDocument(_ parser: XMLParser) { 50 51 } 52 53 func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) { 54 print("エラー:" + parseError.localizedDescription) 55 } 56} 57

SwiftUI

1import Foundation 2import UserNotifications 3import SwiftUI 4 5struct TimeNotification { 6 7 @Binding var date: Date 8 let apiRun = APIRun() 9 10 init(date: Binding<Date>) { 11 self._date = date 12 } 13 14 func basedOnTimeNotification(){ 15 16 apiRun.getMeigen() 17 18 let center = UNUserNotificationCenter.current() 19 let content = UNMutableNotificationContent() 20 21 content.subtitle = apiRun.auther 22 content.body = apiRun.meigen 23 24 let component = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: date) 25 let trigger = UNCalendarNotificationTrigger(dateMatching: component, repeats: false) 26 let identifier = NSUUID().uuidString 27 let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger) 28 center.add(request){ (error : Error?) in 29 if let error = error { 30 print(error.localizedDescription) 31 } 32 } 33 } 34} 35

SwiftUI

1import SwiftUI 2 3struct ContentView: View { 4 5 @State var date = Date() 6 7 var body: some View { 8 VStack{ 9 10 DatePicker("", selection: $date) 11 .labelsHidden() 12 .padding() 13 14 Button("発火"){ 15 TimeNotification(date: $date).basedOnTimeNotification() 16 } 17 } 18 } 19}

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

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

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

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

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

hoshi-takanori

2021/04/28 13:39

API の処理は非同期なので、結果が返ってきたタイミングで通知を出す必要があるでしょうね。
takaat

2021/04/28 13:58

ご回答ありがとうございます。 今は、どのように実装すれば良いかわかりませんが、考えてみます。
guest

回答1

0

ベストアンサー

とりあえずこんな感じで動くかと。

APIRun.swift

  • コールバック関数を引数に追加して、task の最後で呼び出す

diff

1- func getMeigen(){ 2+ func getMeigen(callback: @escaping () -> Void) { 3 // 略 4 let task = session.dataTask(with: req,completionHandler: {(data,response,error) in session.finishTasksAndInvalidate() 5 // 略 6 parser.parse() 7+ callback() 8 })

TimeNotification.swift

  • struct から class に変更
  • basedOnTimeNotification のローカル通知を送信する処理をメソッド (クロージャでも可) に分ける
  • apiRun.getMeigen の引数に上記メソッドを渡す

diff

1-struct TimeNotification { 2+class TimeNotification { 3 4 // 略 5 6 func basedOnTimeNotification(){ 7- apiRun.getMeigen() 8+ apiRun.getMeigen(callback: sendNotification) 9+ } 10+ 11+ func sendNotification() { 12 let center = UNUserNotificationCenter.current() 13 // 略

処理の流れを確認すると、まず、ボタンが押された時に TimeNotification オブジェクトを生成し、basedOnTimeNotification 経由で getMeigen が呼ばれ、API 通信を開始して、すぐに return して、ボタンの処理は終了します。(この時点では task クロージャはまだ実行されてません。)

その後、API の結果が返ってきた時に getMeigen の中の task クロージャが実行されて meigen と auther の値が取得されます。この後で sendNotification を実行することで、API から取得した meigen と auther の値を使ってローカル通知が送信されます。

なお、TimeNotification が構造体のままだと、ボタンの処理が終わった時点で TimeNotification の値が破棄されてしまい、API の結果を受け取った時には処理が継続できませんが、class に変更することで、それを参照しているもの (コールバックに sendNotification を登録することで、間接的に TimeNotification オブジェクトが参照されます) が存在する限り TimeNotification オブジェクトが生き残り、処理が継続されます。

投稿2021/04/29 02:09

hoshi-takanori

総合スコア7901

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

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

takaat

2021/04/29 03:16

丁寧な解説と修正をしていただきありがとうございました。 期待通りの動作を確認することができました。 コールバック関数は初見であり、値型・参照型の違いは聞いたことはありましたが、実用するところまで至っておりませんでした。これからしっかりと解説の理解を深めていきたいと思います。 まずはお礼まで申し上げます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問