I'm working on this subscription system for my app,it took a few days.But as I'm working more on it,it makes more problems.
Sometimes it happens that the app crashes when clicking on buy subscription.
Also I'm having the problem of not knowing when to stop the Please wait.... alert.I need to place the code in:
case .Purchasing:
self.alert_show()
break
But I don't know where to end it,I need to know the information when is the alert from iTunes loaded than to stop the Please wait.... alert.
The biggest problem right now that I'm facing is the crashing sometimes when i click on the Buy Button.
The crashing happens when first time launching the app,and clicking one the Buy Subscription.
Here is the code for the Buy Subscription:
#IBAction func buy_sub(sender: AnyObject) {
let payment:SKPayment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment)
}
Here is the error that I'm getting.
fatal error: unexpectedly found nil while unwrapping an Optional value..
I would like to share with you my source code of the subscription.If you have any advices what to add,or to correct or to correct this problems that i have it would be great.
Here is the full source code of the Subscription View:
class SubscriptionViewController: UIViewController ,SKPaymentTransactionObserver, SKProductsRequestDelegate {
//var productID = ""
var product: SKProduct!
#IBOutlet var buy_trial_button: UIButton!
override func viewDidAppear(animated: Bool) {
if(SKPaymentQueue.canMakePayments()) {
} else {
buy_trial_button.enabled = false
message_alert("Please enable IAPS to continue(Credit card information required in your iTunes account).")
}
//validateReceipt()
let keystore = NSUbiquitousKeyValueStore.defaultStore()
if keystore.objectForKey("expiration_date") != nil{
let expiration: AnyObject? = keystore.objectForKey("expiration_date")
let today = NSDate()
let expiredate = expiration as! NSDate
print("expiredate is %#",expiredate,today)
// var date1 = NSDate()
// var date2 = NSDate()
if(today.compare(expiredate) == NSComparisonResult.OrderedDescending){
print("today is later than expiredate")
validateReceipt()
print("Validating")
}else if(today.compare(expiredate) == NSComparisonResult.OrderedAscending){
print("today is earlier than expiredate")
self.performSegueWithIdentifier("subscriptionPassed", sender: self)
}
}else{
print("First time launch")
}
}
override func viewDidLoad() {
super.viewDidLoad()
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
self.getProductInfo()
//SKPaymentQueue.defaultQueue().addTransactionObserver(self)
//self.getProductInfo()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(animated: Bool) {
}
#IBAction func private_policy(sender: AnyObject) {
let openLink = NSURL(string : "http://arsutech.com/private_policy.php")
UIApplication.sharedApplication().openURL(openLink!)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "subscriptionPassed")
{
}
}
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse){
let products = response.products
if products.count != 0
{
product = products[0] as SKProduct
print(product.localizedTitle + "\n" + product.localizedDescription)
}
}
func getProductInfo(){
if (SKPaymentQueue.canMakePayments()){
let productID:NSSet = NSSet(object: "com.sxxxxxxxxxxxxxxxxx")
let request:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self
request.start()
}
}
#IBAction func buy_sub(sender: AnyObject) {
let payment:SKPayment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment)
}
#IBAction func buy_pro(sender: AnyObject) {
let openLink = NSURL(string : "https://itunes.apple.com/us/app/home-workouts-exercises-mma/id1060747118?mt=8")
UIApplication.sharedApplication().openURL(openLink!)
}
#IBAction func exit(sender: AnyObject) {
exit(0)
}
func validateReceipt(){
alert_show()
let mainBundle = NSBundle.mainBundle() as NSBundle;
let receiptUrl = mainBundle.appStoreReceiptURL;
let isPresent = receiptUrl?.checkResourceIsReachableAndReturnError(NSErrorPointer());
if(isPresent == true){
let data = NSData(contentsOfURL: receiptUrl! );
// Create the JSON object that describes the request
let requestContents = NSMutableDictionary();
//let encodeddata = data!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions());
let encodeddata = data!.base64EncodedString();
//print("encodeddata = \(encodeddata)");
requestContents.setObject(encodeddata, forKey: "receipt-data");
requestContents.setObject("c40f23af1aa44e159aezzzzzzzzzzzzzz", forKey: "password");
var requestData : NSData?
do{
requestData = try NSJSONSerialization.dataWithJSONObject(requestContents, options: NSJSONWritingOptions());
}catch{
// NSLog("Error in json data creation at verifyPaymentReceipt");
}
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let file = "\(documentsPath)/requestData"
if(NSFileManager.defaultManager().createFileAtPath(file, contents: data, attributes: nil)){
NSLog("File %# ",file);
}
else{
//NSLog("error File %# ",file);
}
if(requestData != nil){
//let strRequestData = NSString(data: requestData!, encoding: NSUTF8StringEncoding);
//print(" strRequestData = \(strRequestData)");
// Create a POST request with the receipt data.
//https://buy.itunes.apple.com/verifyReceipt
//https://sandbox.itunes.apple.com/verifyReceipt
let storeURL = NSURL(string: "https://sandbox.itunes.apple.com/verifyReceipt");
let storeRequest = NSMutableURLRequest(URL: storeURL!);
storeRequest.HTTPMethod = "POST";
storeRequest.HTTPBody = requestData;
// Make a connection to the iTunes Store on a background queue.
let queue = NSOperationQueue();
NSURLConnection.sendAsynchronousRequest(storeRequest, queue: queue, completionHandler: { (response : NSURLResponse?, data : NSData?, error : NSError?) -> Void in
if(error != nil){
//Handle Error
}
else{
let d = NSString(data: data!, encoding: NSUTF8StringEncoding);
// NSLog("DATA:%#", d!);
let dataA = d!.dataUsingEncoding(NSUTF8StringEncoding)
var jsonResponseInternal: NSMutableDictionary?
do{
jsonResponseInternal = try NSJSONSerialization.JSONObjectWithData(dataA!,options: NSJSONReadingOptions.AllowFragments) as? NSMutableDictionary;
//print(jsonResponseInternal);
}catch{
// NSLog("Parsing issue : verifyPaymentReceipt");
}
var jsonResponse: NSMutableDictionary?
do{
jsonResponse = try NSJSONSerialization.JSONObjectWithData(data!,
options: NSJSONReadingOptions.AllowFragments) as? NSMutableDictionary;
//print(jsonResponse);
}catch{
// NSLog("Parsing issue : verifyPaymentReceipt");
}
if(jsonResponse != nil){
if(jsonResponse != nil){
//NSLog("Expiration Date: %#", jsonResponse!);
//print("Passed")
/*
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "Purchase")
NSUserDefaults.standardUserDefaults().synchronize()
*/
//self.performSegueWithIdentifier("subscriptionPassed", sender: self)
if jsonResponse!["status"] as? Int == 0 {
//print("Sucessfully returned internal receipt data")
if let receiptInfo: NSArray = jsonResponse!["latest_receipt_info"] as? NSArray {
let lastReceipt = receiptInfo.lastObject as! NSDictionary
var trial_period: Bool = false
// Get last receipt
//print("LAST RECEIPT INFORMATION \n",lastReceipt)
print("Last from internal memory",lastReceipt["original_transaction_id"])
//var is_trial = lastReceipt["is_trial_period"] as! Bool
//var date_bought =
//var date_expires =
// Format date
//print(is_trial)
// Format date
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV"
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
// Get Expiry date as NSDate
let subscriptionExpirationDate: NSDate = formatter.dateFromString(lastReceipt["expires_date"] as! String) as NSDate!
print("\n - DATE SUBSCRIPTION EXPIRES = \(subscriptionExpirationDate)")
let currentDateTime = NSDate()
print(currentDateTime)
if var is_trial:Bool = false{
if(lastReceipt["is_trial_period"] as? String == "Optional(\"true\")"){
is_trial = true
trial_period = is_trial
}else if(lastReceipt["is_trial_period"] as? String == "Optional(\"false\")"){
is_trial = false
trial_period = is_trial
}
}
if (subscriptionExpirationDate.compare(currentDateTime) == NSComparisonResult.OrderedDescending) {
self.alrt_close()
print("Pass");
print("The trial period is \(trial_period)")
let keystore = NSUbiquitousKeyValueStore.defaultStore()
keystore.setObject(subscriptionExpirationDate,forKey:"expiration_date")
keystore.synchronize()
self.performSegueWithIdentifier("subscriptionPassed", sender: self)
} else if (subscriptionExpirationDate.compare(currentDateTime) == NSComparisonResult.OrderedAscending) {
print("Not Pass");
print("Subscription expired")
self.alrt_close()
//self.message_alert("Subscription expired")
}
}
}
}
}
}
});
}
}
}
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .Purchasing:
//self.alrt_close()
break
case .Deferred:
self.alert_show()
break
case .Purchased:
alrt_close()
self.validateReceipt()
//self.setExpirationDate()
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
//dismissViewControllerAnimated(false, completion: nil)
break
case .Failed:
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
print("Not called Expired")
message_alert("Error while purchasing!")
break
case .Restored:
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
print("Restored")
break
default:
break
}
}
}
}
//Alrt
func message_alert(let message_A:String){
let alert = UIAlertController(title: "", message: "\(message_A)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
func alert_show(){
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .Alert)
alert.view.tintColor = UIColor.blackColor()
let loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRectMake(10, 5, 50, 50)) as UIActivityIndicatorView
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
presentViewController(alert, animated: true, completion: nil)
}
func alrt_close(){
self.dismissViewControllerAnimated(false, completion: nil)
}
}
Related
I was working on apple subscription and have set two subscription plan on apple store for "monthly" and "yearly" and have set up all the code which is working perfectly on development and adhoc. But after placing build on App Store app crashes.
I have used firebase crashlytic which detected crash I have attached screenshot crash logs.
https://i.stack.imgur.com/e487d.png
Here my code for Subscription:
func getProducts(productIDs: [String]){
//self.showLoader()
print("Start requesting products ...")
//showAlert(text:"Start requesting products ...")
let request = SKProductsRequest(productIdentifiers: Set(productIDs))
request.delegate = self
request.start()
}
func buyProduct(product: SKProduct){
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
if response.products.isEmpty {
NotificationAlert().NotificationAlert(titles: "No subscription plan")
}else{
self.myProducts = response.products
for invalidIdentifier in response.invalidProductIdentifiers {
print(invalidIdentifier)
//NotificationAlert().NotificationAlert(titles:"Invalid Identifire")
}
print("Did receive response")
for fetchedProduct in response.products{
//DispatchQueue.main.async {
print("fetchedProduct:",fetchedProduct)
//showAlert(text:"Product Fetched")
//NotificationAmyProductslert().NotificationAlert(titles: "fetchedProduct:\(fetchedProduct)")
DispatchQueue.main.asyncAfter(deadline: .now()+1.0, execute: {
let price = self.getPrice(item: fetchedProduct)
if price == ""{
} else{
self.myProducts.append(fetchedProduct)
if fetchedProduct.productIdentifier == "com.motivatory.monthly"{
DispatchQueue.main.asyncAfter(deadline: .now()+0.3, execute: {
self.oPriceMonthlyLbl.text = price ?? ""
self.monthlyprice = price ?? ""
print(price ?? "")
self.productIDs.insert("com.motivatory.monthly",at: 0)
self.selectedPrice = price ?? ""
})
} else {
DispatchQueue.main.asyncAfter(deadline: .now()+0.3, execute: {
self.oPriceYearlyLbl.text = price ?? ""
self.selectedPrice = price ?? ""
self.yearlyPrice = price ?? ""
print(price ?? "")
self.productIDs.insert("com.motivatory.yearly",at: 1)
})
}
}
})
}
}
}
func getPrice(item: SKProduct) -> String? {
let numberFormatter = NumberFormatter()
let price = item.price
let locale = item.priceLocale
numberFormatter.numberStyle = .currency
numberFormatter.locale = locale
return numberFormatter.string(from: price)
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
print(transaction)
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .purchased:
purchaseTokenStr = trans.transactionIdentifier ?? ""
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
if self.purchased == true {
print("Product Purchased")
print(purchaseTokenStr)
receiptValidation()
}
break;
case .failed:
purchaseTokenStr = trans.transactionIdentifier ?? ""
NotificationAlert().NotificationAlert(titles: "Payment failed")
print("Purchased Failed");
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
SVProgressHUD.dismiss()
break;
case .restored:
print("Already Purchased")
purchaseTokenStr = trans.transactionIdentifier ?? ""
// receiptValidation()
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
break
default:
break
}
}
}
}
//added by me
// 4:
func finishTransaction(_ transaction: SKPaymentTransaction) -> Bool {
let productId = transaction.payment.productIdentifier
//print("Product \(productId) successfully purchased”)
print("Product \(productId) successfully purchased")
return true
}
func restorePurchases() {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
//If an error occurs, the code will\ go to this function
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
SVProgressHUD.dismiss()
NotificationAlert().NotificationAlert(titles: error.localizedDescription)
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
if queue.transactions.count == 0{
NotificationAlert().NotificationAlert(titles: "Please purchase a subscription plan")
SVProgressHUD.dismiss()
} else{
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
if self.purchaseTokenStr == t.transactionIdentifier {
switch prodID {
case "com.motivatory.yearly":
print("Your Yearly Plan has Restored Successfully")
NotificationAlert().NotificationAlert(titles: "Your Yearly Plan has Restored Successfully")
receiptValidation()
SVProgressHUD.dismiss()
break
case "com.motivatory.monthly":
print("Your Montly Plan has Restored Successfully")
NotificationAlert().NotificationAlert(titles: "Your Montly Plan has Restored Successfully")
receiptValidation()
SVProgressHUD.dismiss()
break
default:
if queue.transactions.count == 0 {
self.alert(message: "There are no restorable purchases. Only previously bought non-consumable products and auto-renewable subscriptions can be restored.", title: "Inspiration Daily Quotes")
break
}
SVProgressHUD.dismiss()
}
}
}
}
}
func receiptValidation(){
self.HitReceiptApi(Url: "https://buy.itunes.apple.com/verifyReceipt")
}
func HitReceiptApi(Url:String){
SVProgressHUD.setDefaultMaskType(.custom)
SVProgressHUD.setBackgroundLayerColor(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.2472442209))
SVProgressHUD.show(withStatus: "Please Wait")
let receiptFileURL = Bundle.main.appStoreReceiptURL
let receiptData = try? Data(contentsOf: receiptFileURL!)
let recieptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
let jsonDict: [String: AnyObject] = ["receipt-data" : recieptString! as AnyObject, "password" : "fad24ce1475b4fdd9662a0ec31ef5a7d" as AnyObject]
print(jsonDict)
UserDefaults.standard.set(recieptString!, forKey: "recieptString")
do {
let requestData = try JSONSerialization.data(withJSONObject: jsonDict, options: JSONSerialization.WritingOptions.prettyPrinted)
let storeURL = URL(string: Url)!
var storeRequest = URLRequest(url: storeURL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = requestData
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: storeRequest, completionHandler: { [weak self] (data, response, error) in
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
print("=======>",jsonResponse)
let json = try JSONSerialization.jsonObject(with: data!) as! Dictionary<String, AnyObject>
DispatchQueue.main.sync {
if json["status"] as? Int == 21007{
print("Sandbox User")
self?.HitReceiptApi(Url: "https://sandbox.itunes.apple.com/verifyReceipt")
return
} else {
print("Production")
if self!.purchaseTokenStr == ""{
} else {
self?.purchaseTokenStr = ""
if let receiptInfo2: NSArray = json["pending_renewal_info"] as? NSArray {
let lastReceipt2 = receiptInfo2.lastObject as! NSDictionary
if let original_transaction_id = lastReceipt2["original_transaction_id"] as? String {
self?.purchaseTokenStr = original_transaction_id
}
}
if let receiptInfo2: NSArray = json["latest_receipt_info"] as? NSArray {
let lastReceipt2 = receiptInfo2.firstObject as! NSDictionary
if let purchase_date = lastReceipt2["purchase_date"] as? String {
if let date = Formatter.customDate.date(from:purchase_date){
var startDate1 = String(describing: date)
startDate1.removeLast(6)
self?.startDate = "\(startDate1)"
}
}
}
self?.buySubscription = true
self?.subscriptionBuy()
}
SVProgressHUD.dismiss()
}
UserDefaults.standard.set(true,forKey:"SubscriptionPurchased")
}
} catch let parseError {
print(parseError)
}
})
task.resume()
} catch let parseError {
print(parseError)
}
}
}
I am not able to load the documents in chat application in Swift IOS using Firestore database, though able to successfully retrieve the data from the Firestore database, I have added the deinit method as well please assist further to resolve the error, I have added the complete view controller , please help me
Error
'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (47) must be equal to the number of rows contained in that section before the update (23), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Code
let kBannerAdUnitID = "ca-app-pub-3940256099942544/2934735716"
#objc(FCViewController)
class FCViewController: UIViewController, UITableViewDataSource, UITableViewDelegate,
UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// Instance variables
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var sendButton: UIButton!
var ref : CollectionReference!
var ref2: DocumentReference!
var messages: [DocumentSnapshot]! = []
var msglength: NSNumber = 10
fileprivate var _refHandle: CollectionReference!
var storageRef: StorageReference!
var remoteConfig: RemoteConfig!
private let db = Firestore.firestore()
private var reference: CollectionReference?
private let storage = Storage.storage().reference()
// private var messages = [Constants.MessageFields]()
//snapshot private var messages: [Constants.MessageFields] = []
private var messageListener: ListenerRegistration?
// var db:Firestore!
#IBOutlet weak var banner: GADBannerView!
#IBOutlet weak var clientTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.clientTable.register(UITableViewCell.self, forCellReuseIdentifier: "tableViewCell")
// clientTable.delegate = self
//clientTable.dataSource = self
//db = Firestore.firestore()
ref = db.collection("messages").document("hello").collection("newmessages").document("2").collection("hellos").document("K").collection("messages")
ref2 = db.collection("messages").document("hello").collection("newmessages").document("2").collection("hellos").document("K").collection("messages").document()
configureDatabase()
configureStorage()
configureRemoteConfig()
fetchConfig()
loadAd()
}
deinit {
if let refhandle = _refHandle {
let listener = ref.addSnapshotListener { querySnapshot, error in
}
listener.remove()
}
}
func configureDatabase() {
db.collection("messages").document("hello").collection("newmessages").document("2").collection("hellos").document("K").collection("messages").addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
/* let name = documents.map { $0["name"]!}
let text = documents.map { $0["text"]!}
let photourl = documents.map { $0["photoUrl"]!}
print(name)
print(text)
print(photourl)*/
self.messages.append(contentsOf: documents)
// self.clientTable.insertRows(at: [IndexPath(row: self.messages.count-1, section: 0)], with: .automatic)
//self.clientTable.reloadData()
}
}
func configureStorage() {
storageRef = Storage.storage().reference()
}
func configureRemoteConfig() {
remoteConfig = RemoteConfig.remoteConfig()
let remoteConfigSettings = RemoteConfigSettings(developerModeEnabled: true)
remoteConfig.configSettings = remoteConfigSettings
}
func fetchConfig() {
var expirationDuration: Double = 3600
// If in developer mode cacheExpiration is set to 0 so each fetch will retrieve values from
// the server.
if self.remoteConfig.configSettings.isDeveloperModeEnabled {
expirationDuration = 0
}
remoteConfig.fetch(withExpirationDuration: expirationDuration) { [weak self] (status, error) in
if status == .success {
print("Config fetched!")
guard let strongSelf = self else { return }
strongSelf.remoteConfig.activateFetched()
let friendlyMsgLength = strongSelf.remoteConfig["friendly_msg_length"]
if friendlyMsgLength.source != .static {
strongSelf.msglength = friendlyMsgLength.numberValue!
print("Friendly msg length config: \(strongSelf.msglength)")
}
} else {
print("Config not fetched")
if let error = error {
print("Error \(error)")
}
}
}
}
#IBAction func didPressFreshConfig(_ sender: AnyObject) {
fetchConfig()
}
#IBAction func didSendMessage(_ sender: UIButton) {
_ = textFieldShouldReturn(textField)
}
#IBAction func didPressCrash(_ sender: AnyObject) {
print("Crash button pressed!")
Crashlytics.sharedInstance().crash()
}
func inviteFinished(withInvitations invitationIds: [String], error: Error?) {
if let error = error {
print("Failed: \(error.localizedDescription)")
} else {
print("Invitations sent")
}
}
func loadAd() {
self.banner.adUnitID = kBannerAdUnitID
self.banner.rootViewController = self
self.banner.load(GADRequest())
}
// UITableViewDataSource protocol methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue cell
let cell = self.clientTable .dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
// Unpack message from Firebase DataSnapshot
let messageSnapshot: DocumentSnapshot! = self.messages[indexPath.row]
guard let message = messageSnapshot as? [String:String] else { return cell }
let name = message[Constants.MessageFields.name] ?? ""
if let imageURL = message[Constants.MessageFields.imageURL] {
if imageURL.hasPrefix("gs://") {
Storage.storage().reference(forURL: imageURL).getData(maxSize: INT64_MAX) {(data, error) in
if let error = error {
print("Error downloading: \(error)")
return
}
DispatchQueue.main.async {
cell.imageView?.image = UIImage.init(data: data!)
cell.setNeedsLayout()
}
}
} else if let URL = URL(string: imageURL), let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage.init(data: data)
}
cell.textLabel?.text = "sent by: \(name)"
} else {
let text = message[Constants.MessageFields.text] ?? ""
cell.textLabel?.text = name + ": " + text
cell.imageView?.image = UIImage(named: "ic_account_circle")
if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage(data: data)
}
}
return cell
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
guard let text = textField.text else { return true }
textField.text = ""
view.endEditing(true)
let data = [Constants.MessageFields.text: text]
sendMessage(withData: data)
return true
}
func sendMessage(withData data: [String: String]) {
var mdata = data
mdata[Constants.MessageFields.name] = Auth.auth().currentUser?.displayName
if let photoURL = Auth.auth().currentUser?.photoURL {
mdata[Constants.MessageFields.photoURL] = photoURL.absoluteString
}
// Push data to Firebase Database
self.ref.document().setData(mdata, merge: true) { (err) in
if let err = err {
print(err.localizedDescription)
}
print("Successfully set newest city data")
}
}
// MARK: - Image Picker
#IBAction func didTapAddPhoto(_ sender: AnyObject) {
let picker = UIImagePickerController()
picker.delegate = self
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera) {
picker.sourceType = .camera
} else {
picker.sourceType = .photoLibrary
}
present(picker, animated: true, completion:nil)
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion:nil)
guard let uid = Auth.auth().currentUser?.uid else { return }
// if it's a photo from the library, not an image from the camera
if #available(iOS 8.0, *), let referenceURL = info[.originalImage] as? URL {
let assets = PHAsset.fetchAssets(withALAssetURLs: [referenceURL], options: nil)
let asset = assets.firstObject
asset?.requestContentEditingInput(with: nil, completionHandler: { [weak self] (contentEditingInput, info) in
let imageFile = contentEditingInput?.fullSizeImageURL
let filePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/\((referenceURL as AnyObject).lastPathComponent!)"
guard let strongSelf = self else { return }
strongSelf.storageRef.child(filePath)
.putFile(from: imageFile!, metadata: nil) { (metadata, error) in
if let error = error {
let nsError = error as NSError
print("Error uploading: \(nsError.localizedDescription)")
return
}
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
})
} else {
guard let image = info[.originalImage] as? UIImage else { return }
let imageData = image.jpegData(compressionQuality:0.8)
let imagePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000)).jpg"
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
self.storageRef.child(imagePath)
.putData(imageData!, metadata: metadata) { [weak self] (metadata, error) in
if let error = error {
print("Error uploading: \(error)")
return
}
guard let strongSelf = self else { return }
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion:nil)
}
#IBAction func signOut(_ sender: UIButton) {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
dismiss(animated: true, completion: nil)
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError.localizedDescription)")
}
}
func showAlert(withTitle title: String, message: String) {
DispatchQueue.main.async {
let alert = UIAlertController(title: title,
message: message, preferredStyle: .alert)
let dismissAction = UIAlertAction(title: "Dismiss", style: .destructive, handler: nil)
alert.addAction(dismissAction)
self.present(alert, animated: true, completion: nil)
}
}
}
Edit
perform this block of code on main thread
for doc in documents {
self.messages.append(doc)
self.clientTable.insertRows(at: [IndexPath(row: self.messages.count-1, section: 0)], with: .automatic)
}
This should work..
initial view of view control where we start the download Once the download started it look like this I were working in a mp3 application where I need to download the mp3 from the list shown when the user click the download button it shows the control for pause, stop and a progress view. my download is working but I could not show the progress for the download all over the application. For example: if I am downloading some "x.mp3" song if the user click x.mp3 in VC1 the progress should also shown in VC3. Then the big problem I am facing is after downloading the download control should hide. Due to tableview reload function I cannot handle the UIrefresh which should show the download control and also the download button accordingly.
I have attached both my ViewController class the cell class and also the design
I have attached the "CELLFORROWATINDEXPATH" function for your reference and I have enclosed all the download functionality in the cell class.
viewcontroller Cellforrowatindexpath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let table_cell = tableView.dequeueReusableCell(withIdentifier: "LecturesVcCellID", for: indexPath) as! LecturesVcCell
let trackdata = searchResults[indexPath.row]
constantsList.playTypeArray.boolArray.append(false)
table_cell.configure(track: trackdata, index: indexPath.row)
table_cell.downloadbtn.tag = indexPath.row
table_cell.pauseButton.tag = indexPath.row
table_cell.cancelButton.tag = indexPath.row
table_cell.progressLabel.tag = indexPath.row
let x : Int = trackdata.classNumber as! Int
let className = String(x)
if index == indexPath.row {
table_cell.img_play.image=#imageLiteral(resourceName: "playing_track")
table_cell.lbl_SubTitle.text = "Now playing"
table_cell.lbl_SubTitle.textColor = #colorLiteral(red: 0.968627451, green: 0.7490196078, blue: 0.4823529412, alpha: 1)
}else {
table_cell.img_play.image=#imageLiteral(resourceName: "play_track")
table_cell.lbl_SubTitle.text = String(format: "Class - %# | %#", className,(trackdata.timeoftrack!))
table_cell.lbl_SubTitle.textColor = UIColor.lightGray
}
str_image = "https://www.imaginetventures.name/swamiji/wp-content/uploads/2019/01/introved.png"
constantsList.playTypeArray.imageLecture = str_image
table_cell.img_Show.sd_setImage(with: URL(string: str_image), placeholderImage: UIImage(named: "placeholder.png"))
table_cell.navigationController = self.navigationController
if str_Type .isEqual(to: "Playlist") {
table_cell.downloadbtn.isHidden = true
// table_cell.playlistAdditionImageview.isHidden = false
if tableView.cellForRow(at: indexPath)?.accessoryType == .checkmark {
table_cell.playlistAdditionImageview.isHidden = true
}else {
table_cell.playlistAdditionImageview.isHidden = false
}
}else {
table_cell.downloadbtn.setImage(UIImage(named: "DATA11"), for: .normal)
// table_cell.playlistAdditionImageview.isHidden = true
}
table_cell.selectionStyle = .none
return table_cell
}
viewcontrollercell classfile:
override func awakeFromNib() {
super.awakeFromNib()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
context = appDelegate.persistentContainer.viewContext
entity = NSEntityDescription.entity(forEntityName: "DownloadList", in: context)
}
#IBAction func pauseBtnAction (_ sender: UIButton) {
if(downloadTask != nil && isDownload) {
downloadTask!.cancel(byProducingResumeData: { (resumeData) -> Void in
self.downloadData = resumeData
})
isDownload = false
downloadTask = nil
pauseButton.setImage(UIImage(named: "start-button"), for: .normal)
}else
if(!isDownload && downloadData != nil) {
if let resumeData = self.downloadData {
downloadTask = urlSession?.downloadTask(withResumeData: resumeData)
} else {
let url:URL = URL(string: "\((constantsList.playTypeArray.arr_subCatagriesLecture.object(at: sender.tag) as AnyObject).value(forKey: "mp3") as! String)")!
downloadTask = downloadsSession?.downloadTask(with: url)
}
downloadTask!.resume()
isDownload = true
pauseButton.setImage(UIImage(named: "x"), for: .normal)
}
}
#IBAction func cancelBtnClicked (_ sender: UIButton) {
downloadTask?.cancel()
downloadData = nil
isDownload = false
downloadTask = nil
progress.progress = 0
progressLabel.text = "0%"
progress.setProgress(0.0, animated: true)
cancelButton.isHidden = true
pauseButton.isHidden = true
downloadbtn.isHidden = false
progressLabel.isHidden = true
progress.isHidden = true
}
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let completionHandler = appDelegate.backgroundSessionCompletionHandler {
appDelegate.backgroundSessionCompletionHandler = nil
completionHandler()
}
}
}
#IBAction func downloadBtnAction(_ sender: UIButton) {
// sender.pulse()
self.reachability = Reachability.init()
if ((self.reachability?.connection) != .none) {
switch self.reachability?.connection {
case .wifi?:
print("Wifi")
print(sender.tag)
constantsList.playTypeArray.typeofnetwork = true
downloadTapped(sender: sender.tag)
case .cellular?:
print("mobile data")
print(sender.tag)
let alert = UIAlertController(title: "Mobile Data usage Alert!", message: "Downloads will be using mobile data!!", preferredStyle: UIAlertController.Style.alert);
let action1 = UIAlertAction(title: "Ok", style: .default) { (action:UIAlertAction) in
self.downloadTapped(sender: sender.tag)
}
let action2 = UIAlertAction(title: "Cancel", style: .default) { (action:UIAlertAction) in
print("Cancelled")
}
alert.addAction(action1)
alert.addAction(action2)
self.navigationController?.present(alert, animated: true, completion: nil)
case .none:
print("Network Not reachable")
case .some(.none):
print("Some")
}
}else {
print("No Internet")
}
}
func downloadTapped(sender : Int) {
constantsList.playTypeArray.arrayDownloadSort.append(sender as NSNumber)
print(constantsList.playTypeArray.arrayDownloadSort)
constantsList.playTypeArray.boolArray[sender] = true
showDownloadControls = true
downloadbtn.isHidden = true
pauseButton.isHidden = false
cancelButton.isHidden = false
progressLabel.isHidden = true
progress.isHidden = false
progressLabel.text = "Downloading..."
var urlConfiguration:URLSessionConfiguration!
urlConfiguration = URLSessionConfiguration.default
let queue:OperationQueue = OperationQueue.main
urlSession = URLSession.init(configuration: urlConfiguration, delegate: self, delegateQueue: queue)
let url:NSURL = NSURL(string: "\((constantsList.playTypeArray.arr_subCatagriesLecture.object(at: sender) as AnyObject).value(forKey: "mp3") as! String)")!
downloadTask = urlSession.downloadTask(with: url as URL)
downloadTask.resume()
}
//DOWNLOADING THE FILE UPDATE THE PROGRESS
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
// 2
let progress1 = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite);
// 3
print(progress1)
// 4
// OperationQueue.main.addOperation
DispatchQueue.main.async (execute: {
self.isDownload = true
// 2
constantsList.playTypeArray.isDownloading = true
self.progress.setProgress(progress1, animated: false)
self.progressLabel.text = String(progress1 * 100) + "%"
constantsList.playTypeArray.setprogress = progress1
let i = 0
if progress1 == 1.0 {
constantsList.playTypeArray.isDownloading = false
print(i + 1)
}
})
}
//AFTER DOWNLOADING SAVE THE FILE TO APP DATA
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("Finish Downloading")
let filemanager = FileManager()
let directoryURL = filemanager.urls(for: .documentDirectory, in: .userDomainMask)[0]
print(directoryURL)
let docDirectoryURL = NSURL(fileURLWithPath: "\(directoryURL)")
print(docDirectoryURL)
let destinationFileName = downloadTask.originalRequest?.url?.lastPathComponent
print(destinationFileName!)
let destinationURL = docDirectoryURL.appendingPathComponent("\(destinationFileName!)")
print(destinationURL!)
if let path = destinationURL?.path {
if filemanager.fileExists(atPath: path) {
do {
try filemanager.removeItem(at: destinationURL!)
}catch let error as NSError{
print(error.localizedDescription)
}
}
}
do {
try filemanager.copyItem(at: location, to: destinationURL!)
print(destinationURL!)
print(downloadbtn.tag)
newUser = NSManagedObject(entity: entity!, insertInto: context)
dic66 = constantsList.playTypeArray.arr_subCatagriesLecture.object(at: downloadbtn.tag) as? NSDictionary
newUser.setValue(dic66?.value(forKey: "classname") as! NSNumber, forKey: "classname")
newUser.setValue("\(String(describing: dic66?.value(forKey: "time") as! String))", forKey: "time")
newUser.setValue("\(String(describing: dic66?.value(forKey: "title") as! String))", forKey: "songtitle")
newUser.setValue("\(destinationURL!.path)", forKey: "mp3Url")
newUser.setValue("\(String(describing: constantsList.playTypeArray.imageLecture!))", forKey: "imageurl")
do {
try context.save()
print(context)
self.cancelButton.isHidden = true
self.pauseButton.isHidden = true
self.downloadbtn.isHidden = true
self.progressLabel.isHidden = true
self.progress.isHidden = true
print("Successful")
} catch {
print("failed")
}
}catch {
print("Error while copying file")
}
downloadData = nil;
}
func configure(track: Track,index: Int) {
var filefound: Bool = false
var shopwControls: Bool = true
if(constantsList.playTypeArray.boolArray[index]) {
filefound = true
shopwControls = false
self.progress.setProgress(constantsList.playTypeArray.setprogress, animated: true)
}
lbl_Title.text = track.songTitle
//CLASS AND VOLUME UPDATION
let x : Int = track.classNumber as! Int
let className = String(x)
lbl_SubTitle.text = String(format: "Class - %# | %#", className,(track.timeoftrack!))
//core Data checking for already downloaded file
request.returnsObjectsAsFaults = false
request.resultType = .dictionaryResultType
do {
let result = try context.fetch(request)
dic66 = constantsList.playTypeArray.arr_subCatagriesLecture.object(at: index) as? NSDictionary
for data in result as! [[String:Any]] {
print(data["classname"] as! Int)
print(dic66?.value(forKey: "classname") as! Int)
if data["classname"] as! Int == (dic66?.value(forKey: "classname") as! Int) {
filefound = true
}
}
} catch {
print("Failed")
}
// If the track is already downloaded, enable cell selection and hide the Download button
selectionStyle = filefound ? UITableViewCell.SelectionStyle.gray : UITableViewCell.SelectionStyle.none
downloadbtn.isHidden = filefound
pauseButton.isHidden = shopwControls
cancelButton.isHidden = shopwControls
progress.isHidden = shopwControls
}
}
Im creating a profile for my app and when Im trying to delete the profile it won't delete from the view if I come back to it, In the other hand if I close the app and open it again the data would be gone. I can see the data been removed from the sqlite Database in my simulator.
override func viewDidLoad() {
super.viewDidLoad()
showDatePicker()
showPickerRanks ()
pickerRank.delegate = self
pickerRank.dataSource = self
sideMenus()
let appDelegate = UIApplication.shared.delegate as! AppDelegate // UIApplication.shared().delegate as! AppDelegate is now UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Profile")
request.returnsObjectsAsFaults = false
var isEmpty : Bool {
do {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Profile")
let count = try context.count(for: request)
return count == 0 ? true : false
} catch {
return true
}
}
if isEmpty == true {
//Check if there is a Profile
performSegue(withIdentifier: "NoProfileSegue", sender: nil)
} else {
print("No Files")
}
print("Database is empty:\(isEmpty)")
do {
let results = try context.fetch(request)
for result in results as! [NSManagedObject] {
if let airmanNameVar = result.value(forKey: "airmanName") as? String {
PersonName.text = airmanNameVar
}
if let airmanRankVar = result.value(forKey: "airmanRank") as? String {
AirmanRankPicker.text = airmanRankVar
}
if let airmanHeigthVar = result.value(forKey: "airmanHeight") as? Double {
HeightTxtLbl.text = String (airmanHeigthVar)
}
if let airmanWeightVar = result.value(forKey: "airmanWeight") as? Int64 {
weightTxtLbl.text = String (airmanWeightVar)
}
if let airmanAFPTScore = result.value(forKey: "airmanLastAFPTScore") as? Double {
LastAFPTScore.text = String(airmanAFPTScore)
}
if let airmanAFPTDateLast = result.value(forKey: "airmanLastAFTPData") as? String {
LastAFPTDate.text = airmanAFPTDateLast
}
and here is my Delete
#IBAction func DeleteBtn(_ sender: Any) {
let actionSheetController: UIAlertController = UIAlertController(title: "AF Fitness", message:"By pressing Delete all data would be erase.", preferredStyle: .actionSheet)
//Create and add the Cancel action
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .cancel) { action -> Void in
}
actionSheetController.addAction(cancelAction)
let AgreeAction: UIAlertAction = UIAlertAction(title: "Delete", style: .default) { action -> Void in
self.resetAllRecords(in: "Profile")
self.deleteHistory()
UserDefaults.standard.set(false, forKey: "ProfileOn") //Bool Data Type
UserDefaults.standard.synchronize()
self.AgeGroupSeg.selectedSegmentIndex = 0
self.GenderSeg.selectedSegmentIndex = 0
UserDefaults.standard.set(0, forKey: "GenderData")
UserDefaults.standard.set(0, forKey: "AgeGroupData")
self.AirmanRankPicker.text = ""
self.PersonName.text = ""
self.weightTxtLbl.text = ""
self.HeightTxtLbl.text = ""
self.LastAFPTDate.text = ""
self.LastAFPTScore.text = ""
self.performSegue(withIdentifier: "NoProfileSegue", sender: nil)
// self.performSegue(withIdentifier: "NoProfileSegue", sender: nil)
}
actionSheetController.addAction(AgreeAction )
//Present the AlertController
self.present(actionSheetController, animated: true, completion: nil)
}
func resetAllRecords(in entity : String) // entity = Your_Entity_Name
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate // UIApplication.shared().delegate as! AppDelegate is now UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Profile")
request.returnsObjectsAsFaults = false
let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
do {
try context.execute(deleteRequest)
try context.save()
} catch {
print ("There was an error")
}
}
Thank you in advance
Everytime I build an adhoc build and runs it on ios 11 it keeps popping up the message saying "Sign-In Required - Enter the password for ***** [Environment:Sandbox]". Thus, this keep activating like a loop and not going away. tried siging out from iCloud also but didn't work.My inAppPurchase code as bellow.
class InAppPurchase: NSObject,SKProductsRequestDelegate,
SKPaymentTransactionObserver {
///Singleton
private static var inappPurChase:InAppPurchase?;
public static var shared:InAppPurchase {
if inappPurChase == nil{
inappPurChase = InAppPurchase()
}
return inappPurChase!
}
override private init() {
//Singleton complete
}
////////////
var productsList = [SKProduct]()
var productToPurchase = SKProduct()
var productID = ""
let PURCHASE_ID_PREFIX = ""//"com.purelightbeta."
var onInAppPurchaseSuccess: (()->Void )? = nil
var onInAppPurchaseError: (()->Void )? = nil
var onNoProductsIDs: (()->Void )? = nil
var appEnvironment: ((String,Bool)->Void )? = nil
var onReceiptVarificationError: (()->Void )? = nil
var onRestorePurchaseError: ((String)->Void )? = nil
var onRestoreProductsIDs: ((NSMutableArray)->Void )? = nil
public func initPayment (productID: String) {
self.productID = PURCHASE_ID_PREFIX + productID
print("Initializing Purchase Product ID: \(self.productID)")
if SKPaymentQueue.canMakePayments() {
let productID: NSSet = NSSet(objects: self.productID)
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self
request.start()
}else {
print("In App Purchases not enabled")
}
}
func buyProduct() {
let payment = SKPayment(product: productToPurchase)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment as SKPayment)
}
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let myProducts = response.products
print(myProducts)
productsList = []
if myProducts.count == 0 {
print("No products")
if(onNoProductsIDs != nil){
onNoProductsIDs!()
}
}
for product in myProducts {
print("Prod Details :\(product.productIdentifier) \(product.price) \(product.localizedDescription)")
productsList.append(product)
}
for product in productsList {
let productID = product.productIdentifier
if productID == self.productID {
productToPurchase = product
buyProduct()
}
}
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
let productIDs: NSMutableArray = []
for trasacton in queue.transactions {
if trasacton.transactionState == SKPaymentTransactionState.restored
{
let t: SKPaymentTransaction = trasacton as SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
print("prodID:",prodID)
//let appStoreId = prodID.components(separatedBy: "com.purelightbeta.")
productIDs.add(prodID)
SKPaymentQueue .default().finishTransaction(trasacton)
}
}
if onRestoreProductsIDs != nil {
print(productIDs)
onRestoreProductsIDs! (productIDs)
}
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction: AnyObject in transactions {
let trans = transaction as! SKPaymentTransaction
print(trans.error ?? "No in app error")
switch trans.transactionState {
case .purchased:
print("buy ok, AIP unlocked for item :", productToPurchase.productIdentifier)
let productID = productToPurchase.productIdentifier
switch productID {
case self.productID:
print("Item found")
// startValidatingReceipt()
default:
print("Item not found")
}
queue.finishTransaction(trans)
if self.onInAppPurchaseSuccess != nil {
self.onInAppPurchaseSuccess! ()
self.startValidatingReceipt()
}
case .failed:
print("buy error")
if self.onInAppPurchaseError != nil {
self.onInAppPurchaseError! ()
}
queue.finishTransaction(trans)
break
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
// break
default:
print("default")
break
}
}
}
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
print("ERROR: \(error.localizedDescription)")
if onRestorePurchaseError != nil {
onRestorePurchaseError! (error.localizedDescription)
}
}
func restorePurchase() {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
func startValidatingReceipt() {
if let isExists = try? self.getReceiptURL()?.checkResourceIsReachable(), isExists == true {
do {
let data = try Data(contentsOf: self.getReceiptURL()!)
// self.startValidatingData(data: data)
self.sendReceiptToLocalServer(data: data)
}catch {
let appReceiptRefreshRequest = SKReceiptRefreshRequest(receiptProperties: nil)
appReceiptRefreshRequest.delegate = self
appReceiptRefreshRequest.start()
}
}else {
print("No receipt for this purchase")
}
}
func getReceiptURL() -> URL? {
return Bundle.main.appStoreReceiptURL
}
enum receiptValidationURLs:String {
case sandbox = "https://sandbox.itunes.apple.com/verifyReceipt"
case production = "https://buy.itunes.apple.com/verifyReceipt"
static var url:URL{
if isDebug {
return URL.init(string: self.sandbox.rawValue)!
}else{
return URL.init(string: self.production.rawValue)!
}
}
}
func sendReceiptToLocalServer(data:Data) {
let base64encodedReceipt = data.base64EncodedString()
if self.appEnvironment != nil {
self.appEnvironment! (base64encodedReceipt,isDebug)
}
}
func startValidatingData(data:Data){
let base64encodedReceipt = data.base64EncodedString()
print(base64encodedReceipt)
let requestDictionary = ["receipt-data":base64encodedReceipt]
guard JSONSerialization.isValidJSONObject(requestDictionary) else { print("requestDictionary is not valid JSON"); return }
do {
let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
// let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt" // this works but as noted above it's best to use your own trusted server
NSLog("The Environment is: %#", receiptValidationURLs.url.absoluteString)
guard let validationURL = URL(string: receiptValidationURLs.url.absoluteString) else { print("the validation url could not be created, unlikely error"); return }
let session = URLSession(configuration: URLSessionConfiguration.default)
var request = URLRequest(url: validationURL)
request.httpMethod = "POST"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.uploadTask(with: request, from: requestData) { (data, response, error) in
if let data = data , error == nil {
do {
let appReceiptJSON = try JSONSerialization.jsonObject(with: data)
print((appReceiptJSON as AnyObject).count)
print("success. here is the json representation of the app receipt: \(appReceiptJSON)")
NSLog("Receipt is: %#", appReceiptJSON as! NSDictionary)
if let environment = (appReceiptJSON as? NSDictionary)?["environment"]{
if self.appEnvironment != nil {
self.appEnvironment! (environment as? String ?? "No value",isDebug)
}
}
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
} else {
print("the upload task returned an error: \(error)")
}
}
task.resume()
} catch let error as NSError {
print("json serialization failed with error: \(error)")
}
}
// func requestDidFinish(_ request: SKRequest) {
// // a fresh receipt should now be present at the url
// do {
//
//
//
// } catch {
// // still no receipt, possible but unlikely to occur since this is the "success" delegate method
// }
// }
func request(_ request: SKRequest, didFailWithError error: Error) {
print("app receipt refresh request did fail with error: \(error)")
// for some clues see here: https://samritchie.net/2015/01/29/the-operation-couldnt-be-completed-sserrordomain-error-100/
if self.onReceiptVarificationError != nil {
self.onReceiptVarificationError! ()
}
}
}
This may not be the answer but would be a workaround. Best is to create a new sandbox user, sign out the old one and put the new.