I am trying to send data (image) between 2 devices (both iPhones).
This code is for the CBPeripheralManager that advertises:
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
switch peripheral.state {
case .unknown:
print("central.state is .unknown")
case .resetting:
print("central.state is .resetting")
case .unsupported:
print("central.state is .unsupported")
case .unauthorized:
print("central.state is .unauthorized")
case .poweredOff:
print("central.state is .poweredOff")
case .poweredOn:
print("central.state is .poweredOn")
updateAdvertisingData()
}
}
func updateAdvertisingData() {
if (cameraPeripheralManager.isAdvertising) {
cameraPeripheralManager.stopAdvertising()
}
let advertisementData = String(format: "%#", "advertisementData")
char = CBMutableCharacteristic(type: CHAR_UUID, properties: [.notify], value: nil, permissions: [.readable])
myRoei = CBMutableService(type: RX_UUID, primary: true)
myRoei.characteristics = [char]
cameraPeripheralManager.add(myRoei)
cameraPeripheralManager.startAdvertising([CBAdvertisementDataServiceUUIDsKey:[RX_UUID], CBAdvertisementDataLocalNameKey: advertisementData])
}
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
print("didSubscribeTo")
if let img = UIImage(named: "maiden") {
let data = UIImagePNGRepresentation(img)
self.sendData(data: data!)
}
}
func sendData(data: Data){
data.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
let mutRawPointer = UnsafeMutableRawPointer(mutating: u8Ptr)
let uploadChunkSize = 20
let totalSize = data.count
var offset = 0
while offset < totalSize {
let chunkSize = offset + uploadChunkSize > totalSize ? totalSize - offset : uploadChunkSize
let chunk = Data(bytesNoCopy: mutRawPointer+offset, count: chunkSize, deallocator: Data.Deallocator.none)
offset += chunkSize
print("The offset is: + \(offset)")
print("Total size is: + \(totalSize)")
cameraPeripheralManager.updateValue(chunk, for: char, onSubscribedCentrals: nil)
}
}
}
In the CBCentralManager I am using didUpdateValueFor characteristic and the it is always nil.
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print(characteristic.value as Any)
switch characteristic.uuid {
case CHAR_UUID:
print("Char value: \(String(describing: characteristic.value))")
case RX_UUID: break
//
default:
print("Unhandled Characteristic UUID: \(characteristic.uuid)")
}
}
What am I doing wrong?
Related
I am having an issue that is making no sense. Everything that I am doing I would think is pretty standard for BLE. Anyways, I have a BLE module that I am sending string data from to an iOS application and I am displaying this data on an interface. Since this is my first run using BLE code on an iOS application, I am using the tutorial from adafruit as a guide which can be found here: Adafruit tutorial
As I said, pretty standard stuff. Nothing special here. The code that I have written is able to perform all of the BLE steps. But, when it gets the data, the data is /0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0. At first, I thought the source on the transmitting side was incorrect. But I opened another test app that lets me see sent BLE data and the data coming in is correctly formatted. It must be an issue with my code. I am not sure why this is happening or what the issue is. I could really use another pair of eyes to see if there is any issue with my code:
My code init code:
required init(parent: ViewController) {
self.parent = parent
self.bluetoothOn = false;
bodyTemperature = 0
airPressure = 0
lightLevel = 0
fanSpeed = 0
Humidity = 0
otherBatteryLevel = 0
wifiStatus = "Good"
super.init()
self.centralManager = CBCentralManager(delegate: self, queue: nil)
// self.periphal = CBPeripheralManager(delegate: self, queue: nil)
}
My didupdateState code:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if #available(iOS 10.0, *) {
if centralManager.state == CBManagerState.poweredOn{
bluetoothOn = true
self.centralManager.scanForPeripherals(withServices: [ATP_SERVICE_UUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey : false])
}
} else {
// Fallback on earlier versions
}
}
My didDiscover code:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
stopScan()
self.blePeripheral = peripheral
self.RSSI = RSSI
self.blePeripheral.delegate = self
centralManager?.connect(blePeripheral, options: nil)
print("Found BLE module")
// blePeripheral.discoverServices([ATP_SERVICE_UUID])
}
my didConnect code:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to BLE device")
centralManager?.stopScan()
blePeripheral.delegate = self
blePeripheral.discoverServices([ATP_SERVICE_UUID])
}
My didDiscoverServices code:
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else{
return
}
for service in services{
peripheral.discoverCharacteristics([ATP_UART_WRITE14_UUID, ATP_UART_READ13_UUID, ATP_NOTIFY_UUID], for: service)
}
print("Discovered Services: \(services)")
}
My didDiscoverCharacteristics code:
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else{
return
}
print ("Found \(characteristics.count) characteristics")
for characteristic in characteristics{
if characteristic.uuid.isEqual(ATP_UART_READ13_UUID){
readCharacteristic = characteristic
// blePeripheral.setNotifyValue(true, for: readCharacteristic!)
blePeripheral.readValue(for: characteristic)
print("Rx Characteristic: \(characteristic.uuid)")
}
if(characteristic.uuid.isEqual(ATP_UART_WRITE14_UUID)){
writeCharacteristic = characteristic
print("Tx characteristic: \(characteristic.uuid)")
}
}
}
My didupdateNotificationState code:
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if(error != nil){
print("Error changing notification state:\(String(describing: error?.localizedDescription))")
} else {
print("Characteristic's value subscribed")
}
if(characteristic.isNotifying){
print("Subscribed. Notification has begun for: \(characteristic.uuid)")
}
}
And lastly, my didUpdateValueFor:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic == readCharacteristic{
// let ASCIIString = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
var stringFromData = String(data: characteristic.value!, encoding: String.Encoding.ascii)
if stringFromData != "" {
let textArray = stringFromData!.components(separatedBy: "\n\r")
for line in textArray{
if line.hasPrefix("bodyTemperature:") {
var splitString = line.characters.split(separator: ":").map(String.init)
bodyTemperature = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: bodyTemperature)
} else if line.hasPrefix("enclosurePressure:") {
var splitString = line.characters.split(separator: ":").map(String.init)
airPressure = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: airPressure)
} else if line.hasPrefix("lightLevel:") {
var splitString = line.characters.split(separator: ":").map(String.init)
lightLevel = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: lightLevel)
} else if line.hasPrefix("humidity:") {
var splitString = line.characters.split(separator: ":").map(String.init)
Humidity = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: Humidity)
} else if line.hasPrefix("BATT:") {
var splitString = line.characters.split(separator: ":").map(String.init)
otherBatteryLevel = Float(splitString[1])!
self.delegate?.PQEData(didRecieveData: otherBatteryLevel)
} else if line.hasPrefix("fanSpeed:") {
var splitString = line.characters.split(separator: ":").map(String.init)
fanSpeed = Int(splitString[1])!
self.delegate?.PQEData(didRecieveData: fanSpeed)
} else if line.hasPrefix("wifiStatus:") {
var splitString = line.characters.split(separator: ":").map(String.init)
wifiStatus = splitString[1]
self.delegate?.PQEData(didRecieveData: wifiStatus)
}
}
}
}
}
Additional Notes:
1) In the didDiscoverCharaceristics function, I have commented out the call to blePeripheral.setNotifyValue function. That is because on the line for the call to peripheral.discoverCharacteristics call, I am already discovering for the NOTIFY characteristics. I have also tested with the setNotifyValue uncommented and the results are the same.
2) I have a local variable called blePeriphal that stores a reference to the periphal for the ble device. Same applies for writeCharacteristic and readCharacteristic
3) When the blePeripheral.setNotifyValue function was uncommented, the didUpdateNotificationStateFor function was executed. However, the if(error != nil) statement would be true and the error that I would get would be: Error changing notification state:Optional("The request is not supported."). I am not sure if this is related. However, the didUpdateValueFor function would still get executed.
4) The code that is giving me the issue is found in the didUpdateValueFor function and the line is: var stringFromData = String(data: characteristic.value!, encoding: String.Encoding.ascii)
5) Yes, I did try var stringFromData = String(data: characteristic.value!, encoding: String.Encoding.utf8) and var stringFromData = String(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue) and the results were the same. StringFromData is all /0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0
Update:
6) So after some testing, I have come across an issue that may be related. The didUpdateValueFor delegate is only being called once and after that, never again. I have verified this by constantly sending a string once every 1/4 second.
In my iOS app, I want to search device using swift 3. How do I implement this functionality in my app using core Bluetooth?
this below code i what i wrote from rearching in internet and help of my friends to list bluetooth devices and connect to heart rate measure service
class HRMViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var cbCentralManager: CBCentralManager?
var cbPeripheral: CBPeripheral?
var discoveredPeripherals = [CBPeripheral]()
var connected = ""
var bodyData = ""
var manufacturer = ""
var deviceData = ""
var heartRate: UInt16 = 0
var localNameArray = [String]()
var pulseTimer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Do any additional setup after loading the view.
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.backgroundColor = UIColor.lightGray
tableView.layer.borderColor = UIColor.lightGray.cgColor
tableView.layer.borderWidth = 1.0
tableView.layer.cornerRadius = 2.0
tableView.layer.masksToBounds = true
let centralManager = CBCentralManager(delegate: self, queue: nil)
self.cbCentralManager = centralManager
}
}
extension HRMViewController : CBCentralManagerDelegate, CBPeripheralDelegate {
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String ?? ""
if localName.count > 0 {
if RSSI.intValue > signalStrengthMax {
if let index = self.discoveredPeripherals.index(of: peripheral) {
self.discoveredPeripherals.remove(at: index)
self.localNameArray.remove(at: index)
self.tableView.reloadData()
}
return
}
if RSSI.intValue < signalStrengthMin {
if let index = self.discoveredPeripherals.index(of: peripheral) {
self.discoveredPeripherals.remove(at: index)
self.localNameArray.remove(at: index)
self.tableView.reloadData()
}
return
}
print("Discovered: \(peripheral.name ?? "a device") with udid: \(peripheral.identifier) at \(RSSI)")
if !(self.discoveredPeripherals.contains(peripheral)) {
self.discoveredPeripherals.append(peripheral)
self.localNameArray.append(localName)
self.tableView.reloadData()
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
self.connected = "Connected: \((peripheral.state == .connected) ? "Yes" : "No")"
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state != .poweredOn {
return
}
if central.state == .poweredOn {
let deviceService = CBUUID(string: HRMConstants.Services.deviceInfoUUID)
let heartRateService = CBUUID(string: HRMConstants.Services.heartRateUUID)
let services = [deviceService,heartRateService]
cbCentralManager?.scanForPeripherals(withServices: services, options: nil)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services ?? [CBService]() {
peripheral.discoverCharacteristics(nil, for: service)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if service.uuid == CBUUID(string: HRMConstants.Services.heartRateUUID) {
for character in service.characteristics ?? [CBCharacteristic]() {
if character.uuid == CBUUID(string: HRMConstants.Characteristics.measurementUUID) {
self.cbPeripheral?.setNotifyValue(true, for: character)
}
else if character.uuid == CBUUID(string: HRMConstants.Characteristics.bodyLocationUUID) {
self.cbPeripheral?.readValue(for: character)
}
}
}
if service.uuid == CBUUID(string: HRMConstants.Services.deviceInfoUUID) {
for character in service.characteristics ?? [CBCharacteristic]() {
if character.uuid == CBUUID(string: HRMConstants.Characteristics.manufacturerNameUUID) {
self.cbPeripheral?.readValue(for: character)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid == CBUUID(string: HRMConstants.Characteristics.measurementUUID) {
self.getHeartRateData(for: characteristic, error: error)
}
if characteristic.uuid == CBUUID(string: HRMConstants.Characteristics.manufacturerNameUUID) {
self.getManufacturerName(for: characteristic)
}
else if characteristic.uuid == CBUUID(string: HRMConstants.Characteristics.bodyLocationUUID) {
self.getBodyLocation(for: characteristic)
}
}
}
extension HRMViewController {
func getHeartRateData(for characteristic: CBCharacteristic, error: Error?) {
if let _ = error {
print("Error: \(error?.localizedDescription ?? "Unknown")")
return
}
guard let value = characteristic.value else { return }
let bpm : UInt16
if (value[0] & 0x01) == 0 {
bpm = UInt16(value[1])
} else {
bpm = UInt16(littleEndian: value.subdata(in: 1..<3).withUnsafeBytes { $0.pointee } )
}
heartRate = bpm
doHeartBeat()
pulseTimer = Timer.scheduledTimer(timeInterval: 60.0 / Double(heartRate), target: self, selector: #selector(self.doHeartBeat), userInfo: nil, repeats: false)
/*
var reportData = [UInt8](value)
var bpm: UInt16 = 0
if (reportData[0] & 0x01) == 0 {
bpm = UInt16(reportData[1])
}
else {
bpm = CFSwapInt16LittleToHost(UInt16(reportData[1]))
}
heartRate = bpm
doHeartBeat()
pulseTimer = Timer.scheduledTimer(timeInterval: 60.0 / Double(heartRate), target: self, selector: #selector(self.doHeartBeat), userInfo: nil, repeats: false)
*/
}
func getManufacturerName(for characteristic: CBCharacteristic) {
guard let value = characteristic.value else { return }
let manufacturerName = String(data: value, encoding: .utf8)
}
func getBodyLocation(for characteristic: CBCharacteristic) {
guard let value = characteristic.value else { return }
var location = ""
// 1
var bodyData = [UInt8](value)
if !bodyData.isEmpty {
let bodyLocation: UInt8 = bodyData[0]
// 2
location = "Body Location: \(bodyLocation == 1 ? "Chest" : "Undefined")"
// 3
}
else {
// 4
location = "Body Location: N/A"
}
print(location)
}
#objc func doHeartBeat() {
}
}
extension HRMViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = "\(localNameArray[indexPath.row]) \(discoveredPeripherals[indexPath.row].identifier )"
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newPeripheral = discoveredPeripherals[indexPath.row]
if let _ = cbPeripheral { // had a device selected already
if newPeripheral != cbPeripheral! { // clean up old peripheral
self.cleanUp()
}
}
cbPeripheral = newPeripheral
self.cbCentralManager?.stopScan()
cbPeripheral?.delegate = self
self.cbCentralManager?.connect(cbPeripheral!, options: nil)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return discoveredPeripherals.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 45.0
}
func cleanUp() {
if !(cbPeripheral!.state == .connected) { // Don't do anything if we're not connected
return
}
if let services = self.cbPeripheral!.services {
for eachService in services {
if let characteristics = eachService.characteristics {
for eachCharacteristic in characteristics {
if eachCharacteristic.uuid.isEqual(CBUUID(string: HRMConstants.Characteristics.bodyLocationUUID)) {
if eachCharacteristic.isNotifying {
self.cbPeripheral?.setNotifyValue(false, for: eachCharacteristic)
}
}
if eachCharacteristic.uuid.isEqual(CBUUID(string: HRMConstants.Characteristics.manufacturerNameUUID)) {
if eachCharacteristic.isNotifying {
self.cbPeripheral?.setNotifyValue(false, for: eachCharacteristic)
}
}
if eachCharacteristic.uuid.isEqual(CBUUID(string: HRMConstants.Characteristics.measurementUUID)) {
if eachCharacteristic.isNotifying {
self.cbPeripheral?.setNotifyValue(false, for: eachCharacteristic)
}
}
}
}
}
}
self.cbCentralManager?.cancelPeripheralConnection(self.cbPeripheral!)
self.cbPeripheral = nil
}
}
and for constants
struct HRMConstants {
struct Services {
static let deviceInfoUUID = "180A"
static let heartRateUUID = "180D"
}
struct Characteristics {
static let measurementUUID = "2A37"
static let bodyLocationUUID = "2A38"
static let manufacturerNameUUID = "2A29"
}
}
with this you will get the basic idea and then you can try it for yourself
see for ble doc for different bluetooth services
Trying to retrieve readable information from an characteristics by using the function:
peripheral.discoverDescriptors(for: characteristic)
Later the delegate method:
func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?)
is called but how can I get the string description? When I read the value from the descriptors it's always nil.
let descriptors = characteristic.descriptors! as [CBDescriptor]
for descriptor in descriptors {
print("\(#function): descriptor = \(descriptor) UUID = \(descriptor.uuid) value = \(descriptor.value)")
}
However, if I'm browsing and connecting with an BLE scanner it can read the characteristic human readable descriptors.
Reading descriptors is a two-step process, much like discovering and then reading characteristics.
Try:
public func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
guard let descriptors = characteristic.descriptors else { return }
for descr in descriptors {
peripheral.readValue(for: descr)
}
}
And then fill in the blanks of:
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) {
switch descriptor.uuid.uuidString {
case CBUUIDCharacteristicExtendedPropertiesString:
guard let properties = descriptor.value as? NSNumber else {
break
}
print(" Extended properties: \(properties)")
case CBUUIDCharacteristicUserDescriptionString:
guard let description = descriptor.value as? NSString else {
break
}
print(" User description: \(description)")
case CBUUIDClientCharacteristicConfigurationString:
guard let clientConfig = descriptor.value as? NSNumber else {
break
}
print(" Client configuration: \(clientConfig)")
case CBUUIDServerCharacteristicConfigurationString:
guard let serverConfig = descriptor.value as? NSNumber else {
break
}
print(" Server configuration: \(serverConfig)")
case CBUUIDCharacteristicFormatString:
guard let format = descriptor.value as? NSData else {
break
}
print(" Format: \(format)")
case CBUUIDCharacteristicAggregateFormatString:
print(" Aggregate Format: (is not documented)")
default:
break
}
}
The string constants and the associated data types came from the overview table here.
In my (limited) experience, descriptors don't reveal anything particularly interesting.
I am doing simple in app purchase application. At the time of product load I simply restore all my previous purchase, so I start loading indicator when I start loading product but in some device I already used inapp id so every time I load the product in that device some type of alert come and if I press cancel then nothing happen and my loading indicator goes infinite.
Here I have screenshot too.
extension IAPHelper: SKPaymentTransactionObserver {
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
if productIdentifiers.contains(transaction.payment.productIdentifier) {
switch (transaction.transactionState) {
case .purchased:
complete(transaction: transaction)
break
case .failed:
fail(transaction: transaction)
break
case .restored:
restore(transaction: transaction)
break
case .deferred:
print("denied")
break
case .purchasing:
break
}
}
}
if productsRestoreCompletionHandler != nil
{
productsRestoreCompletionHandler = nil
}
}
private func complete(transaction: SKPaymentTransaction) {
print("complete...")
// deliverPurchaseNotificationFor(identifier: transaction.payment.productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
productsPurchaseCompletionHandler?(true, transaction)
if productsPurchaseCompletionHandler != nil
{
productsPurchaseCompletionHandler = nil
}
}
private func restore(transaction: SKPaymentTransaction) {
guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }
print("restore... \(productIdentifier)")
// deliverPurchaseNotificationFor(identifier: productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
productsRestoreCompletionHandler?(true, transaction)
}
private func fail(transaction: SKPaymentTransaction) {
print("fail...")
if let transactionError = transaction.error as? NSError {
if transactionError.code != SKError.paymentCancelled.rawValue {
print("Transaction Error: \(transaction.error?.localizedDescription)")
}
}
SKPaymentQueue.default().finishTransaction(transaction)
if productsRestoreCompletionHandler != nil
{
productsRestoreCompletionHandler?(false, transaction)
productsRestoreCompletionHandler = nil
}
if productsPurchaseCompletionHandler != nil
{
productsPurchaseCompletionHandler?(false, transaction)
productsPurchaseCompletionHandler = nil
}
}
private func deliverPurchaseNotificationFor(identifier: String?) {
guard let identifier = identifier else { return }
purchasedProductIdentifiers.insert(identifier)
// UserDefaults.standard.set(true, forKey: identifier)
// UserDefaults.standard.synchronize()
// NotificationCenter.default.post(name: NSNotification.Name(rawValue: IAPHelper.IAPHelperPurchaseNotification), object: identifier)
}
}
When product load
//MARK:
//MARK: load the product
func getProduct()
{
let userData = getUserDataFromFile()
obj = IAPHelper(productIds: NetFishProducts.productIdentifiers)
if checkInternetConnection()
{
SINGLETON.startLoadingActivity(self.view)
obj.requestProducts { (isSuccess, arrProduct) in
if isSuccess == true
{
self.setProduct = Set<SKProduct>(arrProduct! as [SKProduct])
self.btnSignup.isUserInteractionEnabled = true
for p in arrProduct! {
print("Found product: \(p.productIdentifier) \(p.localizedTitle) \(p.price.floatValue)")
if p.productIdentifier == NetFishProducts.monthlyPlan
{
self.setupLblMonth(withMothPrice: "\(p.price.floatValue)")
}
else
{
self.setupLblYear(withYearPrice: "\(p.price.floatValue)")
}
}
self.obj.requestRestoreProducts(completionHandler: { (isSuccess, transation) in
SINGLETON.stopLoadingActivity(self.view)
if isSuccess == true
{
let orignalTI = transation?.original?.transactionIdentifier
let userEmail = transation?.payment.applicationUsername
print("orignalTI:\(orignalTI)")
print("username:\(userEmail)")
if userEmail != nil && userData?.userEmail != userEmail
{
self.btnSignup.isUserInteractionEnabled = false
self.showAlertToUser("Information", MsgDesc: "You are login with different appstore account so login with correct appstore account")
return
}
}
else
{
self.btnSignup.isUserInteractionEnabled = false
}
},withUserName: (userData?.userEmail)!)
}
else
{
SINGLETON.stopLoadingActivity(self.view)
self.btnSignup.isUserInteractionEnabled = false
}
}
}
}
I'm trying to add a restore button to my app, I have a non-consumable inApp purchase and the purchase part works fine. I have a button that calls this two methods:
product_id = "RemoveAdsIAP";
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
and this the purchasing code:
func buyConsumable(){
succssessIAP = false
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID);
productsRequest.delegate = self;
productsRequest.start();
} else {
displayErrorAlert()
}
}
func buyProduct(product: SKProduct){
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) {
buyProduct(validProduct);
} else { }
} else {
displayErrorAlert()
}
}
func request(request: SKRequest!, didFailWithError error: NSError!) {
activityIndicatorShop.stopAnimating()
UIApplication.sharedApplication().endIgnoringInteractionEvents()
displayErrorAlert()
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
succssessIAP = false
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .Purchased, .Restored:
if (product_id == "GetNextFactIAP" && succssessIAP == false) {
startController.getData()
startController.hideContainerView()
succssessIAP = true
}
if (product_id == "RemoveAdsIAP" && succssessIAP == false) {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "viewAdsBool")
NSUserDefaults.standardUserDefaults().synchronize()
startController.removeOnce()
disableAds(true)
succssessIAP = true
}
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
break;
case .Failed:
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
activityIndicatorShop.stopAnimating()
displayErrorAlert()
UIApplication.sharedApplication().endIgnoringInteractionEvents()
break;
default:
break;
}
}
}
}
When I press the button nothing happens, I've added a log statement to check if the updatedTransactions method was called and it wasn't, What's the problem?
you didn't add the button as an IBAction.
Read this...
http://rshankar.com/different-ways-to-connect-ibaction-to-uibutton/
Try This Code
import UIKit
import StoreKit
class ViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver{
var list = [SKProduct]()
var p = SKProduct()
// 2
func buyProduct() {
println("buy " + p.productIdentifier)
var pay = SKPayment(product: p)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().addPayment(pay as SKPayment)
}
//3
func productsRequest(request: SKProductsRequest!, didReceiveResponse response: SKProductsResponse!) {
println("product request")
var myProduct = response.products
for product in myProduct {
println("product added")
println(product.productIdentifier)
println(product.localizedTitle)
println(product.localizedDescription)
println(product.price)
list.append(product as SKProduct)
}
outRemoveAds.enabled = true
outAddCoins.enabled = true
}
// 4
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue!) {
println("transactions restored")
var purchasedItemIDS = []
for transaction in queue.transactions {
var t: SKPaymentTransaction = transaction as SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "seemu.iap.removeads":
println("remove ads")
removeAds()
case "seemu.iap.addcoins":
println("add coins to account")
addCoins()
default:
println("IAP not setup")
}
}
}
// 5
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
println("add paymnet")
for transaction:AnyObject in transactions {
var trans = transaction as SKPaymentTransaction
println(trans.error)
switch trans.transactionState {
case .Purchased:
println("buy, ok unlock iap here")
println(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID {
case "seemu.iap.removeads":
println("remove ads")
removeAds()
case "seemu.iap.addcoins":
println("add coins to account")
addCoins()
default:
println("IAP not setup")
}
queue.finishTransaction(trans)
break;
case .Failed:
println("buy error")
queue.finishTransaction(trans)
break;
default:
println("default")
break;
}
}
}
// 6
func finishTransaction(trans:SKPaymentTransaction)
{
println("finish trans")
}
//7
func paymentQueue(queue: SKPaymentQueue!, removedTransactions transactions: [AnyObject]!)
{
println("remove trans");
}