ios - Store Kit crashes on 2nd request -
i building first store kit app , have crash issue:
the app sprite kit game
in mainmenuscene.swift
allocate removeadbutton
when tapped call initpurchaseremoveads()
function
mainmenuscene.swifte = skpaymentqueue.defaultqueue()
func initpurchaseremoveads() { let actionrotate = skaction.rotatebyangle(cgfloat(m_2_pi), duration: 0.1) let spin = skaction.repeatactionforever(actionrotate) self.loadingwheel.runaction(spin) self.loadingwheel.hidden = false nsnotificationcenter.defaultcenter().postnotificationname(keyvaluekeyclassification.notificationbuyremovead.rawvalue, object: nil, userinfo: nil) }
gameviewcontroller.swift
var request : skproductsrequest? var queue : skpaymentqueue. = skpaymentqueue.defaultqueue() // called through notification (works!) func initpurchase(notification: nsnotification) { println("user clicked remove ads") if skpaymentqueue.canmakepayments() { println("user can make payments") self.request = skproductsrequest(productidentifiers: nsset(object: purchaseclassification.removeadid.rawvalue string)) self.request?.delegate = self self.request?.start() } else { println("user cannot make payments") } } func restorepurchases() { skpaymentqueue.defaultqueue().restorecompletedtransactions() } func purchase(product : skproduct) { let payment = skpayment(product: product) skpaymentqueue.defaultqueue().addtransactionobserver(self) skpaymentqueue.defaultqueue().addpayment(payment) } func productsrequest(request: skproductsrequest!, didreceiveresponse response: skproductsresponse!) { var adprodfound : bool = false var count = response.products.count if count > 0 { p in response.products { if p skproduct { var prod = p skproduct if prod.productidentifier == purchaseclassification.removeadid.rawvalue string { println("remove ad product available") adprodfound = true purchase(prod) } } } } if !adprodfound { println("remove ad product not available") } } func paymentqueuerestorecompletedtransactionsfinished(queue: skpaymentqueue!) { println("received restored transactions") t in queue.transactions { if t skpaymenttransaction { var trans = t skpaymenttransaction if trans.transactionstate == skpaymenttransactionstate.restored { println("transactionstate = restored") removeads() break } } } } func paymentqueue(queue: skpaymentqueue!, updatedtransactions transactions: [anyobject]!) { t in queue.transactions { if t skpaymenttransaction { var trans = t skpaymenttransaction switch trans.transactionstate { case .purchasing: break case .purchased: removeads() skpaymentqueue.defaultqueue().finishtransaction(trans) break case .restored: skpaymentqueue.defaultqueue().finishtransaction(trans) case .failed: if trans.error.code != skerrorpaymentcancelled { println("transaction state -> cancelled") } stopwheel() skpaymentqueue.defaultqueue().finishtransaction(trans) break default: stopwheel() skpaymentqueue.defaultqueue().finishtransaction(trans) } } } } func stopwheel() { println("stop wheel")
nsnotificationcenter.defaultcenter().postnotificationname(keyvaluekeyclassification.notificationstopwheel.rawvalue, object: nil, userinfo: nil) }
func removeads() { stopwheel() nsuserdefaults.standarduserdefaults().setbool(true, forkey: keyvaluekeyclassification.keyadsremoved.rawvalue) nsnotificationcenter.defaultcenter().postnotificationname(keyvaluekeyclassification.notificationremoveads.rawvalue, object: nil, userinfo: nil) }
problem
* purchase works fine when done once * app crashes if abort purchase (wheel stops spinning, fine) , tap removeads
button again
error reproduction:
- click remove ad purchase
- cancel purchase
- click remove ad purchase again
output:
user clicked remove ads user can make payments remove ad product available stop wheel user clicked remove ads user can make payments (lldb) bt thread #1: tid = 0x3186, 0x0000000102abd00b libobjc.a.dylib`objc_msgsend + 11, queue = 'com.apple.main-thread', stop reason = exc_bad_access (code=exc_i386_gpflt) frame #0: 0x0000000102abd00b libobjc.a.dylib`objc_msgsend + 11 frame #1: 0x000000010084eeee storekit`__34-[skproductsrequest _handlereply:]_block_invoke + 52 frame #2: 0x0000000107070ba6 libdispatch.dylib`_dispatch_call_block_and_release + 12 frame #3: 0x000000010708e7f4 libdispatch.dylib`_dispatch_client_callout + 8 frame #4: 0x00000001070778fb libdispatch.dylib`_dispatch_main_queue_callback_4cf + 949 frame #5: 0x00000001012e9fe9 corefoundation`__cfrunloop_is_servicing_the_main_dispatch_queue__ + 9 frame #6: 0x00000001012aceeb corefoundation`__cfrunlooprun + 2043 frame #7: 0x00000001012ac486 corefoundation`cfrunlooprunspecific + 470 frame #8: 0x00000001031819f0 graphicsservices`gseventrunmodal + 161 frame #9: 0x000000010176e420 uikit`uiapplicationmain + 1282 * frame #10: 0x000000010061a3ae shapesly`top_level_code + 78 @ appdelegate.swift:14 frame #11: 0x000000010061a3ea shapesly`main + 42 @ appdelegate.swift:0 frame #12: 0x00000001070c3145 libdyld.dylib`start + 1 (lldb)
error:
[app.gameviewcontroller retain]: message sent deallocated instance 0x7fc2fc845880
i believe problem skproductsrequest not being retained.
the best solution move storekit logic helper class:
class storekithelper: nsobject,skproductsrequestdelegate, skpaymenttransactionobserver, skrequestdelegate { var request : skproductsrequest? var queue : skpaymentqueue = skpaymentqueue.defaultqueue() class var defaulthelper : storekithelper { struct static { static let instance : storekithelper = storekithelper() } return static.instance } override init() { super.init() } func initpurchase() { println("user clicked remove ads") if skpaymentqueue.canmakepayments() { println("user can make payments") self.request = skproductsrequest(productidentifiers: nsset(object: purchaseclassification.removeadid.rawvalue string)) self.request?.delegate = self self.request?.start() } else { println("user cannot make payments") } } ...
you can init purchase without notifications so:
storekithelper.defaulthelper.initpurchase()
this way can sure properties retained , above error not reoccur.
Comments
Post a Comment