実現したいこと
アプリ内課金で購入されたSubscription商品のレシート検証の際、realmを使おうとするとクラッシュしてしまうのでしないようにしたいです。
エラーメッセージ
Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread.
該当のソースコード
swift
1func validateReceipt(completion: @escaping (Bool) -> Void) { 2 3 //(中略) 4 5 // 自前のサーバーを経由してAppleに問い合わせて取得したレシート情報が以下の receipt 6 verifier.verify { receipt in 7 guard let receipt = receipt else { 8 return 9 } 10 11 // レシート検証ロジック 12 //1. 正当なレシートか検証 13 if receipt["status"] as! Int == 0 { 14 15 //2. 自分のアプリのレシートか検証 16 let receiptDictionary = receipt["receipt"] as! NSDictionary 17 if receiptDictionary["bundle_id"] as! String == "com.〇〇.〇〇" { 18 19 //3. 未登録のレシートか最新のexpires_date_msがRealmにあるか否かで検証 20 21 let latestReceiptInfoArray = receipt["latest_receipt_info"] as! Array<Dictionary<String, AnyObject>> 22 23 var latestExpireDate:Int = 0 24 for latestReceiptInfo in latestReceiptInfoArray { 25 let receiptExpireDateMs = Int(latestReceiptInfo["expires_date_ms"] as? String ?? "") ?? 0 26 let receiptExpireDateS = receiptExpireDateMs / 1000 27 if receiptExpireDateS > latestExpireDate { 28 latestExpireDate = receiptExpireDateS 29 } 30 } 31 32 //RealmでlatestExpireDateで検索 33 let predicate = NSPredicate(format: "expireDate == %i",latestExpireDate) 34 let fetchedSubscriptionDataArray = self.realm.objects(Subscription.self).filter(predicate) 35 36 if fetchedSubscriptionDataArray .isEmpty { 37 38 //4. 有効期限内か確認 39 40 let now = Int(Date().timeIntervalSince1970) 41 42 //有効期限以内であればレシート検証結果OK 43 if latestExpireDate > now { 44 45 //Subscriptionを始めたことをRealmに保存 46 let subscription = Subscription() 47// subscription.setValue(true, forKey: "valid") 48// subscription.setValue(Date(), forKey: "startedTime") 49// subscription.setValue(Date(), forKey: "updatedTime") 50// subscription.setValue(latestExpireDate, forKey: "expireDate") 51 52 let subscriptionRef = ThreadSafeReference(to: subscription) 53 DispatchQueue(label: "background").async { 54 let realm = try! Realm() 55 guard let subscription = realm.resolve(subscriptionRef) else { 56 return 57 } 58 try! realm.write { 59 subscription.setValue(true, forKey: "valid") 60 subscription.setValue(Date(), forKey: "startedTime") 61 subscription.setValue(Date(), forKey: "updatedTime") 62 subscription.setValue(latestExpireDate, forKey: "expireDate") 63 } 64 } 65 66// do { 67// try self.realm.write { 68// self.realm.add(subscription) 69// } 70// } catch { 71// print("Error saving (error)") 72// } 73 74 completion(true) 75 76 } else { 77 print("Invalid Receipt (This receipt expired)") 78 completion(false) 79 } 80 } else { 81 print("Invalid Receipt (This receipt was already used)") 82 completion(false) 83 } 84 } else { 85 print("Invalid Receipt (Other product receipt)") 86 completion(false) 87 } 88 } else { 89 print("Invalid Receipt") 90 completion(false) 91 } 92 } 93}
以下トランザクション内の上記ファンクションが使われる該当箇所
Swift
1func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { 2 3 for transaction in transactions { 4 5 if transaction.transactionState == .purchased { 6 7 print("Transaction successful!") 8 9 //レシートを検証 10 validateReceipt { (Bool) in 11 if Bool == true { 12 13 // MainPageに遷移 14 self.performSegue(withIdentifier: "welcomeToMain", sender: nil) 15 SKPaymentQueue.default().finishTransaction(transaction) 16 17 } else { 18 19 print("Receipt Validation Failed") 20 21 } 22 } 23 } 24 //(中略) 25 } 26}
試したこと
始めは上のソースコードでコメントアウトしているいつも自分が使っている方法でrealmにデータを保存しようとしていたのですが上記のエラーメッセージが出てしまったため、realmのこちらのドキュメントを参照して、スレッド間でのデータの受け渡しを可能にするスレッドセーフ参照を使えばいいのではないかと考え、上のソースコードのように書きかえました。
ですが同じエラーメッセージが出て来てしまい先に進めません。
皆様のお力添えいただけますと幸いです。
何卒よろしくお願いいたします。
補足情報
Swift 5.0.1
Xcode 10.3
Realm 3.17.3
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/08/06 09:06