Removing Ad with IAP Swift - swift

So I am trying to put In App Purchase in my app to remove advertisement, but my ad is in another view, and I think I am removing it the wrong way, because it is breaking exactly in the removeallAds function:
func removeallAds() {
ViewController().bannerAd.removeFromSuperview()
}
It is giving me the following error:
Fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
This is my code if you want to take a look:
import UIKit
import StoreKit
class IapViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
#IBOutlet weak var removeAds: UIButton!
#IBOutlet weak var restorePurchase: UIButton!
#IBAction func removeAdsAct(sender: AnyObject) {
for product in list{
var prodId = product.productIdentifier
if (prodId == "com.hazeApps.removeAds"){
p = product
buyProduct()
break;
}
}
}
#IBAction func resPurchaseAct(sender: AnyObject) {
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
override func viewDidLoad() {
super.viewDidLoad()
removeAds.enabled = false
//IAP Setup
if(SKPaymentQueue.canMakePayments()){
println("IAP is up and running")
var productId: NSSet = NSSet(object: "com.hazeApps.removeAds")
var request: SKProductsRequest = SKProductsRequest(productIdentifiers: productId)
request.delegate = self
request.start()
} else {
println("enable IAPs")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var list = [SKProduct]()
var p = SKProduct()
func buyProduct(){
println("buy " + p.productIdentifier)
var pay = SKPayment(product: p)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().addPayment(pay as SKPayment)
}
func removeallAds() {
ViewController().bannerAd.removeFromSuperview()
}
func productsRequest(request: SKProductsRequest!, didReceiveResponse response: SKProductsResponse!) {
println("Product Request")
var myProducts = response.products
for product in myProducts{
println("product added")
println(product.productIdentifier)
println(product.localizedTitle)
println(product.localizedDescription)
println(product.price)
list.append(product as SKProduct)
}
removeAds.enabled = true
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue!) {
println("Transaction restored")
var purchasedItemIDS = []
for transaction in queue.transactions{
var t: SKPaymentTransaction = transaction as SKPaymentTransaction
let prodId = t.payment.productIdentifier as String
switch prodId{
case "com.hazeApps.removeAds":
println("Remove Adds")
removeallAds()
default:
println("IAP not setup")
}
}
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
println("add payment")
for transaction: AnyObject in transactions{
var trans = transaction as SKPaymentTransaction
println(trans.error)
switch trans.transactionState{
case .Purchased:
println("Unlock IAP here")
println(p.productIdentifier)
let productId = p.productIdentifier as String
switch productId{
case "com.hazeApps.removeAds":
println("Remove Adds")
removeallAds()
default:
println("IAP not setup")
}
queue.finishTransaction(trans)
break;
case .Failed:
println("buy error")
queue.finishTransaction(trans)
break;
default:
println("Default")
break;
}
}
}
func finishTransaction(trans: SKPaymentTransaction){
println("Finish Trans")
}
func paymentQueue(queue: SKPaymentQueue!, removedTransactions transactions: [AnyObject]!) {
println("Removed Trans")
}
}
Other thing, I have some line of code that handle in case of wireless connections to make the bannerAd hide, If the Ad is removed from superview it might get some error in this lines?

Related

In-App Purchase 'Thread 1: Fatal error: Index out of range'

I have no problem when I click on restore purchase. But when I click on unlockProButt or buy100CoinsButt, I get the error "Thread 1: Fatal error: Index out of range". What is the problem? How can I make the purchase smoothly? I have always received errors in all the in-app purchase examples I have tried. I always have problems with this line of code.
Error Line: purchaseMyProduct(validProducts[productIndex])
Error: Thread 1: Fatal error: Index out of range
import UIKit
import StoreKit
class ViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
#IBOutlet weak var buy100coinsButton: UIButton!
#IBOutlet weak var unlockProButton: UIButton!
#IBOutlet weak var restorePurchaseButton: UIButton!
var productsRequest = SKProductsRequest()
var validProducts = [SKProduct]()
var productIndex = 0
// viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
buy100coinsButton.isHidden = true
unlockProButton.isHidden = true
fetchAvailableProducts()
}
func fetchAvailableProducts() {
let productIdentifiers = NSSet(objects:
"..", // 0
"..." // 1
)
productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
}
func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {
if (response.products.count > 0) {
validProducts = response.products
// 1st IAP Product
let prod100coins = response.products[0] as SKProduct
let prodUnlockPro = response.products[1] as SKProduct
print("1st rpoduct: " + prod100coins.localizedDescription)
print("2nd product: " + prodUnlockPro.localizedDescription)
buy100coinsButton.isHidden = false
unlockProButton.isHidden = false
}
}
func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
return true
}
func canMakePurchases() -> Bool { return SKPaymentQueue.canMakePayments() }
func purchaseMyProduct(_ product: SKProduct) {
if self.canMakePurchases() {
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment)
} else { print("Purchases are disabled in your device!") }
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
if productIndex == 0 {
print("You've bought 100 coins!")
buy100coinsButton.setTitle("Buy another 100 Coins Chest", for: .normal)
} else {
print("You've unlocked the Pro version!")
unlockProButton.isEnabled = false
unlockProButton.setTitle("PRO version purchased", for: .normal)
}
break
case .failed:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
print("Payment has failed.")
break
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
print("Purchase has been successfully restored!")
break
default: break
}}}
}
func restorePurchase() {
SKPaymentQueue.default().add(self as SKPaymentTransactionObserver)
SKPaymentQueue.default().restoreCompletedTransactions()
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("The Payment was successfull!")
}
// Buttons -------------------------------------
#IBAction func buy100CoinsButt(_ sender: UIButton) {
productIndex = 0
purchaseMyProduct(validProducts[productIndex])
}
#IBAction func unlockProButt(_ sender: UIButton) {
productIndex = 1
purchaseMyProduct(validProducts[productIndex])
}
#IBAction func restorePurchaseButt(_ sender: UIButton) {
restorePurchase()
}
}
you should create a completion handler so you know when your app actually receives the inApp products, so when the delegate "productsRequest" has completed its operation, for this add to your class the appropriate protocol => "SKRequestDelegate" (all these requests are made asynchronously, I hope you know the differences between synchronous and asynchronous, I recommend you to search on google) Now that you have added the SKRequestDelegate protocol dependency to your class, you can implement the two functions that will inform you of the completion or failure of your request: "requestDidFinish" and "request ... didFailWithError", I'll give you an example with some code below, I hope I've been of help to you, for other clarifications please ask :)
extension ViewController : SKRequestDelegate {
// fine della richiesta in app
func requestDidFinish(_ request: SKRequest) {
debugPrint("[IAP] ---- REQUEST COMPLETED ---- ")
}
// Error Request
func request(_ request: SKRequest, didFailWithError error: Error) {
debugPrint("[IAP] Error: ", error)
}
}
Ah I forgot: check that "self.validProducts.count" is greater than 0 otherwise it means that your array is empty and so when you go to => "purchaseMyProduct(validProducts[productIndex])" your application will crash because the "productIndex" position in your "validProducts" array doesn't exist...

How to make a correct authorization

i am making an app Redmine, i have a website with a user with pass and there are issues. But i can't understand what i should do to make authorization.
Here I have Router, Request, AuthViewController. I also wanted to ask how i have to make AuthRequest? What has to be there?
AuthViewController
import UIKit
class AuthViewController: UIViewController {
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
#IBOutlet weak var signInBotton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func signInDidTap(_ sender: Any) {
login()
}
fileprivate func login(){
AuthRequest.login(email: emailField.text!, password: passwordField.text!){(user, error) in DispatchQueue.main.async {
[unowned self] in self.openIssues()
}
}
}
}
//extension AuthViewController: Router{
// func prepare() {
// Here error with 'seque' if(seque.identifier = Seque.issues.rawValue){
// print("It's OK")
// guard let controller = seque.destination as? IssuesViewController else { print("Wrong destination"); return}
// //controller.presenter = IssuesPresenter();
// }
// }
//
// func openIssues() {
// print("kek")
// }
//
//
// enum Seque: String {
// case issues = "IssuesSeque"
// }
//
//}
There is also an error in if-block in extension.

Is my restore purchases code correct and if so where should I put the action to change user defaults?

I am writing my first iOS app in Swift and have been having some problems with the in-app purchases functionality. I think I've got the actual buy function working but I'm not sure about the restore functionality. I'm just providing one product which is a 'pro' version of the app. This is stored in the User Defaults location and having it unlocks the extra functionality. My restore function is confusing me, can someone have a look at it and tell me where I should be setting my User Defaults value? Should it be within the paymentQueueRestoreCompletedTransactionsFinished function or the paymentQueue function?
import UIKit
import StoreKit
class StoreController: UIViewController, SKProductsRequestDelegate,
SKPaymentTransactionObserver {
// Properties
var defaults = UserDefaults.standard
var list = [SKProduct]()
var p = SKProduct()
// Methods
override func viewDidLoad() {
super.viewDidLoad()
// Get list of products for the store
localTitle.isEnabled = false
localDescription.isEnabled = false
price.isEnabled = false
buy.isEnabled = false
restore.isEnabled = false
getProducts()
}
// Outlets
#IBOutlet weak var localTitle: UILabel!
#IBOutlet weak var localDescription: UILabel!
#IBOutlet weak var price: UILabel!
#IBOutlet weak var buy: UIButton!
#IBOutlet weak var restore: UIButton!
// Actions
#IBAction func buy(_ sender: UIButton) {
print("buy pressed")
for product in list {
let prodID = product.productIdentifier
if(prodID == "com.squidgylabs.pro") {
p = product
buyProduct()
}
}
}
#IBAction func restore(_ sender: UIButton) {
print("restore pressed")
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
// Methods
func getProducts() {
print("starting getproducts function")
if(SKPaymentQueue.canMakePayments()) {
print("IAP is enabled, loading")
let productID: NSSet = NSSet(objects: "com.squidgylabs.pro")
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self
request.start()
} else {
print("please enable IAPS")
}
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print("starting productsrequest")
let myProduct = response.products
for product in myProduct {
print("product added")
list.append(product)
}
localTitle.isEnabled = true
localDescription.isEnabled = true
restore.isEnabled = true
buy.isEnabled = true
price.isEnabled = true
// Update labels
localTitle.text = list[0].localizedTitle
localDescription.text = list[0].localizedDescription
// Format the price and display
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .currency
if let formattedPrice = formatter.string(from: list[0].price){
price.text = formattedPrice
}
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("starting paymentQueueResoreCompletedTransactionsFnished")
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "com.squidgylabs.pro":
print("case is correct product ID in payment...finished")
default:
print("IAP not found")
}
}
}
func buyProduct() {
print("buy " + p.productIdentifier)
let pay = SKPayment(product: p)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(pay as SKPayment)
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("entering func paymentQueue")
for transaction: AnyObject in transactions {
let trans = transaction as! SKPaymentTransaction
switch trans.transactionState {
case .purchased:
print("buy ok, unlock IAP HERE")
print(p.productIdentifier)
let prodID = p.productIdentifier
switch prodID {
case "com.squidgylabs.pro":
print("setting pro in defaults...")
defaults.set(true, forKey: "pro")
default:
print("IAP not found")
}
queue.finishTransaction(trans)
break
case .failed:
print("buy error")
queue.finishTransaction(trans)
break
case .restored:
print("case .restored in paymentQ")
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
queue.finishTransaction(trans)
break
default:
print("Default")
break
}
}
}
}
It may be done like this.
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
completePurchaseForTransaction(transaction)
case .restored:
completePurchaseForTransaction(transaction)
default:
break
}
}
private func completePurchaseForTransaction(_ transaction: SKPaymentTransaction) {
let productIdentifier = transaction.payment.productIdentifier
// Update UserDefaults here
SKPaymentQueue.default().finishTransaction(transaction)
}
You might consider using other class for StoreKit access instead of a UIViewController. Because
Your application should always expect to be notified of completed
transactions.
https://developer.apple.com/documentation/storekit/skpaymentqueue/1506042-add
And View controllers come and go. It might even crash the app if you don't remove object you added as an observer to the SKPaymentQueue.
I have simple, well-documented StoreKit library on GitHub that you might check out to get the idea -- https://github.com/suvov/VSStoreKit

Swift Beacon doesn't work now using Swift 3.0

I have been following a guide to using Swift to create a Beacon proximity app, but since updating Xcode and updating the code to Swift 3.0 I am getting a fatal error.
Going through the functions I think there is an issue with the startScanning function, when it fires I get fatal error message.
Any hints at to what could help would be greatly appreciated:
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var distanceLabel: UILabel!
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
view.backgroundColor = UIColor.gray
print("did load")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == CLAuthorizationStatus.authorizedAlways{
print("status authorized")
if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self){
print("is monitoring")
if CLLocationManager.isRangingAvailable() {
print("scanning")
startScanning()
}
}
}
}
func startScanning() {
print("start Scanning")
let uuid = NSUUID(uuidString: "695e5f08824c785cadc72e1dde23be04")
let beaconRegion = CLBeaconRegion(proximityUUID: uuid as! UUID, identifier: "MyBeacon")
locationManager.startMonitoring(for: beaconRegion)
locationManager.startRangingBeacons(in: beaconRegion)
}
func updateDistance(distance: CLProximity){
UIView.animate(withDuration: 1) { [unowned self] in
switch distance {
case .unknown:
self.view.backgroundColor = UIColor.gray
self.distanceLabel.text = "UNKNOWN"
print("distance Unknown")
case .far:
self.view.backgroundColor = UIColor.blue
self.distanceLabel.text = "FAR"
print("distance Far")
case .near:
self.view.backgroundColor = UIColor.orange
self.distanceLabel.text = "NEAR"
print("distance Near")
case .immediate:
self.view.backgroundColor = UIColor.red
self.distanceLabel.text = "BOOM!"
print("distance Immediate")
}
}
}
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
if beacons.count > 0 {
let beacon = beacons.first! as CLBeacon
updateDistance(distance: beacon.proximity)
print("found more than one beacon")
} else {
updateDistance(distance: .unknown)
print("found only one beacon")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The problem is that your UUID is in the wrong format, so this line fails to parse it, assigning nil to the variable uuid:
let uuid = NSUUID(uuidString: "695e5f08824c785cadc72e1dde23be04")
The program will then crash using the uuid as! UUID operation because ! will crash if there is a nil value.
To fix this, you need to add dashes at the appropriate places in the UUID string. You should also avoid using the ! operator to force unwrap optional variables in Swift, as it can cause crashes like this. Try this:
if let uuid = NSUUID(uuidString: "695e5f08-824c-785c-adc7-2e1dde23be04") {
let beaconRegion = CLBeaconRegion(proximityUUID: uuid, identifier: "MyBeacon")
locationManager.startMonitoring(for: beaconRegion)
locationManager.startRangingBeacons(in: beaconRegion)
}
else {
NSLog("Invalid UUID format")
}
When running check that the code does not go down the "Invalid UUID format" path.

Does not conform to protocol SKPaymentTransactionObserver

I am trying to implement in app purchases in a Swift app, of a consumable in-app purchase, with product ID productID
Research
Using this answer, I learned about the basic pattern for in app purchases, and tried to adapt it, like so. I do not need to use the NSUserDefaults method of having the app remember if a purchase is made, as I am handling that with a different solution.
Code
class ViewController: UICollectionViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
viewDidLoad() {
//Unimportant stuff left out
product_id = "productID"
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
}
#IBAction func thisISTheButtonToTriggerTheInAppPurchases(sender: AnyObject) {
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("Fetching Products");
}else{
print("can't make purchases");
}
}
func buyProduct(product: SKProduct){
print("Sending the Payment Request to Apple");
var payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment);
}
func productsRequest (request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
var count : Int = response.products.count
if (count>0) {
var validProducts = response.products
var validProduct: SKProduct = response.products[0] as SKProduct
if (validProduct.productIdentifier == self.product_id) {
print(validProduct.localizedTitle)
print(validProduct.localizedDescription)
print(validProduct.price)
buyProduct(validProduct);
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}
func request(request: SKRequest!, didFailWithError error: NSError!) {
print("Error Fetching product information");
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
print("Received Payment Transaction Response from Apple");
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .Purchased:
print("Product Purchased");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
break;
case .Failed:
print("Purchased Failed");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
break;
case .Restored:
print("Already Purchased");
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
default:
break;
}
}
}
}
}
Error
Type 'ViewController' does not conform to protocol 'SKPaymentTransactionObserver'
I researched this error a bit, and sources like this suggest that such an error is due to not having required functions.
According to Apple, paymentQueue:updatedTransactions: is the only one required, but I seem to be implementing that. A possible theory of mine is that I am not implementing it correctly - if true, how do I fix that?
Expected Behavior
The app presents, and if given favorable user input, performs the IAP with the ID productID.
Thanks for helping, and let me know if you need any more information!
It's very simple. The error is about SKPaymentTransactionObserver. Its only required method is:
func paymentQueue(queue: SKPaymentQueue,
updatedTransactions transactions: [SKPaymentTransaction])
The signature for your method doesn't match that:
func paymentQueue(queue: SKPaymentQueue!,
updatedTransactions transactions: [AnyObject]!)
...so it isn't the same method, so you don't conform.