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

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

詳細はこちら
バックグラウンド処理

バックグラウンド処理とは、マルチタスク環境において、ユーザーに対して前面に表示させている処理の裏側で実行させる処理のことを呼びます。バックグラウンド処理を行う事によって、ユーザーが他の作業に携わることが可能となります。

Swift

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

Q&A

解決済

1回答

1646閲覧

swift: バックグラウンド処理について

hikaruhara

総合スコア6

バックグラウンド処理

バックグラウンド処理とは、マルチタスク環境において、ユーザーに対して前面に表示させている処理の裏側で実行させる処理のことを呼びます。バックグラウンド処理を行う事によって、ユーザーが他の作業に携わることが可能となります。

Swift

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

0グッド

1クリップ

投稿2019/12/13 06:54

前提・実現したいこと

ToDoリストのようなアプリで、追加したセルのボタンを押すとタイマーが作動して勉強時間を計測したい。

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

バックグラウンドでタイマーが停止してしまうことと、一度アプリを閉じてタイマーの開始ボタンを押下すると、

Can't end BackgroundTask: no background task exists with identifier 6 (0x6), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.

というメッセージが表示され、タイマーが動かなくなる(セルの追加や削除は行えます)

該当のソースコード

Swift

1class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIApplicationDelegate { 2 3 @IBOutlet weak var regiButton: UIButton! 4 @IBOutlet weak var textInput: UITextField! 5 var item:Item? 6 var items = [Item]() 7 let userDefaults = UserDefaults.standard 8 var timer: Timer? 9 var count: Int = 0 10 11 12 func encodeData() { 13 let encodedData = try! NSKeyedArchiver.archivedData(withRootObject: items, requiringSecureCoding: false) 14 userDefaults.set(encodedData, forKey: "items") 15 userDefaults.synchronize() 16 } 17 18 func decodeData() { 19 if let storedData = userDefaults.data(forKey: "items") { 20 let decodedData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(storedData) as? [Item] 21 items = decodedData! 22 tableView.reloadData() 23 } 24 } 25 26 //カテゴリー登録 27 @IBAction func regiButtonOnPressed(_ sender: Any) { 28 //テキスト取得 29 let text = textInput.text 30 if text != "" { 31 //インスタンス生成 32 let item = Item(name: text!, time: "0") 33 //配列に追加 34 items.append(item) 35 //Data型に変換して保存 36 encodeData() 37 decodeData() 38 } 39 textInput.text = "" 40 } 41 42 // 43 // 44 //タイマー 45 @IBAction func startButtonOnPressed(_ sender: UIButton) { 46 if sender.self.currentTitle == "START" { 47 sender.self.setTitle("STOP", for: .normal) 48 //タイマースタート 49 timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCount), userInfo: nil, repeats: true) 50 } else { 51 sender.self.setTitle("START", for: .normal) 52 //タイマーストップ 53 timer?.invalidate() 54 //親のcontentviewを取得 55 if let parentContentView = sender.self.superview { 56 //contentviewの親のcellを取得 57 if let parentCell = parentContentView.superview as? UITableViewCell { 58 //取得したcellのindexを取得 59 let row = tableView.indexPath(for: parentCell)?.row 60 let stopTime = count 61 var storedTime = Int(items[row!].time) 62 storedTime = storedTime! + stopTime 63 items[row!].time = String(storedTime!) 64 print(items[row!].time + "です") 65 } 66 } 67 count = 0 68 } 69 } 70 71 @objc func updateCount() { 72 count = count + 1 73 print(count) 74 } 75 76 77 func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { 78 return true 79 } 80 81 //セル数指定 82 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 83 return items.count 84 } 85 86 //セルの生成 87 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 88 let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) 89 cell.textLabel!.text = items[indexPath.row].name 90 return cell 91 } 92 93   //セル削除時の処理 94 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 95 if editingStyle == UITableViewCell.EditingStyle.delete { 96 items.remove(at: indexPath.row) 97 tableView.deleteRows(at: [indexPath as IndexPath], with: UITableView.RowAnimation.automatic) 98 encodeData() 99 decodeData() 100 } 101 } 102 103 104 @IBOutlet weak var tableView: UITableView! 105 override func viewDidLoad() { 106 super.viewDidLoad() 107 108 if let defaultData = userDefaults.data(forKey: "items") { 109 //デコードする 110 let decodedData = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(defaultData) as? [Item] 111 items = decodedData! 112 } 113 } 114} 115
//自作クラスの定義(別ファイルで管理) class Item: NSObject, NSCoding, NSSecureCoding { static var supportsSecureCoding = true var name:String var time:String init(name:String, time:String) { self.name = name self.time = time } func encode(with coder: NSCoder) { coder.encode(name, forKey: "name") coder.encode(time, forKey: "time") } required init?(coder: NSCoder) { name = coder.decodeObject(forKey: "name") as! String time = coder.decodeObject(forKey: "time") as! String } }

どこのソースコードが影響しているのか判断がつかなかったため、全文を掲載させていただきます。

試したこと

同じエラー文でググったところ、
こちらが見つかったため、
Symbolic BreakpointにUIApplicationEndBackgroundTaskErrorと入力、同じようにアプリを再起動させたところ

UIKitCore`UIApplicationEndBackgroundTaskError: -> 0x109976334 <+0>: pushq %rbp 0x109976335 <+1>: movq %rsp, %rbp 0x109976338 <+4>: movq %rdi, %rdx 0x10997633b <+7>: leaq 0x96e01e(%rip), %rdi ; @"Can't end BackgroundTask: no background task exists with identifier %lu (0x%lx), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug." 0x109976342 <+14>: movq %rdx, %rsi 0x109976345 <+17>: xorl %eax, %eax 0x109976347 <+19>: callq 0x109ee9988 ; symbol stub for: NSLog 0x10997634c <+24>: popq %rbp 0x10997634d <+25>: retq

が表示され、

-> 0x109976334 <+0>: pushq %rbp

に対して「Thread 1: breakpoint 1.」が表示され、これについても調べたのですが有効な手段が分かりませんでした。

補足情報(FW/ツールのバージョンなど)

swift 5.1.3
Xcode 11.3
シミュレーター iPhone Xs iOS 13.3

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

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

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

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

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

vanderlvov

2019/12/13 09:15

エラーが出ているのは何行目ですか?
hikaruhara

2019/12/13 09:21

Symbolic BreakpointにUIApplicationEndBackgroundTaskErrorと入力後、ビルドしてシミュレーター上でアプリをいったん閉じてもう一度アプリアイコンを押してアプリを起動するとシミュレーターが止まり上記の Thread 1: breakpoint 1.1が表示されるという様子で、コード上にエラーが赤く表示されることはありません。
guest

回答1

0

ベストアンサー

iOSでは、バックグランド処理に用途制限があり、Capabilityを設定する必要があります。(プロジェクトを選択し、Signing & Capabilitiesタブを選択し、+Capabilityを押してBackground Modesを選択して追加できます。)

以下の記事に用途に関する説明がありました。
Qiita [Swift] iOSのバックグラウンド処理について

ご要望の用途であれば、単に開始時間を記録して、次にアプリに戻ってきたときに終了時間を記録して比較することでいいのではないでしょうか。

エラーはAppDelegateの処理のどこかででている気がしますが、iOS13のバグという話もあるようなので、気にしなくてもよいかもしれません。

投稿2019/12/17 12:21

eytyet

総合スコア803

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

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

hikaruhara

2020/01/04 06:28

ご丁寧にありがとうございます。また、時間の計算部分は開始時間と終了時間の差分から取るというのは全く考えていない方法だったので参考にさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問