I want to ping different hosts (e.g. google.com, apple.com, amazon.com) and according to the result (successful or unsuccessful), I want an image to be shown as same sort of connection status view.
It works all fine when I only ping for one host status. However, since I will send multiple hosts I need multiple UIImageViews for each host. So I need suppose I need a parameter to be defined in the callback function or I need to create the UIImageView and return it by the function.
I am just not sure how to do that since I use SimplePing framework which is written in objective-c.:
swift 4
import UIKit
class ServerVC: BaseViewController, SimplePingDelegate {
var displayString = ""
var pinger: SimplePing?
let imageError = UIImage(named: "error")
let imageConnected = UIImage(named: "check")
var statusImage: UIImage!
#IBOutlet weak var serverLbl: UILabel!
var ServerImg = UIImageView()
#IBOutlet weak var updateBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
serverImg = UIImageView(frame: CGRect(x: self.view.center.x, y: 200, width: 50, height: 50))
serverImg.image = UIImage(named: "error")
self.view.addSubview(serverImg)
start(hostname: "www.apple.com")
// INFO: here I want statusImage which is defines below in the callback being set as the image of my UIImageView "serverImg":
serverImg.image = statusImage
}
#objc func updateBtn(sender: UIButton!){
print("refresh server status")
start(hostname: "www.apple.com")
serverImg.image = statusImage
}
func start(hostname: String) {
displayString.removeAll()
NSLog("start")
let pinger = SimplePing(hostName: hostname)
self.pinger = pinger
pinger.addressStyle = .icmPv4
pinger.delegate = self
pinger.start()
}
func stop() {
NSLog("stop")
self.pinger?.stop()
self.pinger = nil
}
func sendPing() {
self.pinger!.send(with: nil)
}
func simplePing(_ pinger: SimplePing, didStartWithAddress address: Data) {
NSLog("pinging %#", ServerVC.displayAddressForAddress(address))
self.sendPing()
}
public func simplePing(pinger: SimplePing, didStartWithAddress address: NSData) {
pinger.send(with: nil)
}
func simplePing(_ pinger: SimplePing, didFailWithError error: Error) {
NSLog("failed: %#", ServerVC.shortErrorFromError(error as NSError))
displayString.append("failed: \(ServerVC.shortErrorFromError(error as NSError))\n")
// INFO: here I which image has to be set as "statusImage":
statusImage = imageError!
self.stop()
}
func simplePing(_ pinger: SimplePing, didSendPacket packet: Data, sequenceNumber: UInt16) {
NSLog("#%u sent", sequenceNumber)
displayString.append("#\(sequenceNumber) sent\n")
}
func simplePing(_ pinger: SimplePing, didFailToSendPacket packet: Data, sequenceNumber: UInt16, error: Error) {
NSLog("#%u send failed: %#", sequenceNumber, ServerVC.shortErrorFromError(error as NSError))
displayString.append("#\(sequenceNumber) send failed: \(ServerVC.shortErrorFromError(error as NSError))\n")
// INFO: here I which image has to be set as "statusImage":
statusImage = imageError!
}
private func simplePing(_ pinger: SimplePing, didReceivePingResponsePacket packet: Data, sequenceNumber: UInt16) {
NSLog("#%u received, size=%zu", sequenceNumber, packet.count)
displayString.append("#\(sequenceNumber) received, size=\(packet.count)\n")
// INFO: here I which image has to be set as "statusImage":
statusImage = imageConnected!
}
func simplePing(_ pinger: SimplePing, didReceiveUnexpectedPacket packet: Data) {
NSLog("unexpected packet, size=%zu", packet.count)
displayString.append("unexpected packet, size=\(packet.count)\n")
// INFO: here I which image has to be set as "statusImage":
statusImage = imageError!
}
static func displayAddressForAddress(_ address: Data) -> String {
var hostStr = [Int8](repeating: 0, count: Int(NI_MAXHOST))
let success = getnameinfo(
(address as NSData).bytes.bindMemory(to: sockaddr.self, capacity: address.count),
socklen_t(address.count),
&hostStr,
socklen_t(hostStr.count),
nil,
0,
NI_NUMERICHOST
) == 0
let result: String
if success {
result = String(cString: hostStr)
} else {
result = "?"
}
return result
}
static func shortErrorFromError(_ error: NSError) -> String {
if error.domain == kCFErrorDomainCFNetwork as String && error.code == Int(CFNetworkErrors.cfHostErrorUnknown.rawValue) {
if let failureObj = error.userInfo[kCFGetAddrInfoFailureKey as String] {
if let failureNum = failureObj as? NSNumber {
if failureNum.int32Value != 0 {
let f = gai_strerror(failureNum.int32Value)
if f != nil {
return String(cString: f!)
}
}
}
}
}
if let result = error.localizedFailureReason {
return result
}
return error.localizedDescription
}
}
Related
This is my first experience with creating purchases. The app I'm working on hasn't been released yet. I've been testing subscriptions locally using the Configuration.storekit file. Everything worked fine.
I recently encountered a problem - my subscriptions are no longer displayed in the project. I got an error like this in the terminal:
UPD:
I decided to check the application on the emulator and everything works there. As far as I remember everything broke after installing xcode 14 and updating to ios 16.
On the physical device, the problem remains.
I didn't change the code in those places. I tried to create new .storekit files, but it still doesn't work.
I tried to load the .storekit file with the synchronization. In it the price is pulled up and displayed correctly, as on the site, but in the terminal again writes the same error.
Here is the file that works with purchases:
import StoreKit
typealias RequestProductsResult = Result<[SKProduct], Error>
typealias PurchaseProductResult = Result<Bool, Error>
typealias RequestProductsCompletion = (RequestProductsResult) -> Void
typealias PurchaseProductCompletion = (PurchaseProductResult) -> Void
class Purchases: NSObject {
static let `default` = Purchases()
private let productIdentifiers = Set<String>(
arrayLiteral: "test.1month", "test.6month", "test.12month"
)
private var products: [String: SKProduct]?
private var productRequest: SKProductsRequest?
private var productsRequestCallbacks = [RequestProductsCompletion]()
fileprivate var productPurchaseCallback: ((PurchaseProductResult) -> Void)?
func initialize(completion: #escaping RequestProductsCompletion) {
requestProducts(completion: completion)
}
private func requestProducts(completion: #escaping RequestProductsCompletion) {
guard productsRequestCallbacks.isEmpty else {
productsRequestCallbacks.append(completion)
return
}
productsRequestCallbacks.append(completion)
let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
productRequest.delegate = self
productRequest.start()
self.productRequest = productRequest
}
func purchaseProduct(productId: String, completion: #escaping (PurchaseProductResult) -> Void) {
guard productPurchaseCallback == nil else {
completion(.failure(PurchasesError.purchaseInProgress))
return
}
guard let product = products?[productId] else {
completion(.failure(PurchasesError.productNotFound))
return
}
productPurchaseCallback = completion
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
}
public func restorePurchases(completion: #escaping (PurchaseProductResult) -> Void) {
guard productPurchaseCallback == nil else {
completion(.failure(PurchasesError.purchaseInProgress))
return
}
productPurchaseCallback = completion
SKPaymentQueue.default().restoreCompletedTransactions()
}
}
extension Purchases: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
guard !response.products.isEmpty else {
print("Found 0 products")
productsRequestCallbacks.forEach { $0(.success(response.products)) }
productsRequestCallbacks.removeAll()
return
}
var products = [String: SKProduct]()
for skProduct in response.products {
print("Found product: \(skProduct.productIdentifier)")
products[skProduct.productIdentifier] = skProduct
}
self.products = products
productsRequestCallbacks.forEach { $0(.success(response.products)) }
productsRequestCallbacks.removeAll()
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("Failed to load products with error:\n \(error)")
productsRequestCallbacks.forEach { $0(.failure(error)) }
productsRequestCallbacks.removeAll()
}
}
extension Purchases: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased, .restored:
if finishTransaction(transaction) {
SKPaymentQueue.default().finishTransaction(transaction)
productPurchaseCallback?(.success(true))
UserDefaults.setValue(true, forKey: "isPurchasedSubscription")
} else {
productPurchaseCallback?(.failure(PurchasesError.unknown))
}
case .failed:
productPurchaseCallback?(.failure(transaction.error ?? PurchasesError.unknown))
SKPaymentQueue.default().finishTransaction(transaction)
default:
break
}
}
productPurchaseCallback = nil
}
}
extension Purchases {
func finishTransaction(_ transaction: SKPaymentTransaction) -> Bool {
let productId = transaction.payment.productIdentifier
print("Product \(productId) successfully purchased")
return true
}
}
There is also a file that is responsible for displaying available subscription options:
//
// PremiumRatesTVC.swift
// CalcYou
//
// Created by Admin on 29.08.2022.
//
import StoreKit
import UIKit
class PremiumRatesTVC: UITableViewController {
var oneMonthPrice = ""
var sixMonthPrice = ""
var twelveMonthPrice = ""
#IBOutlet weak var oneMonthPriceLabel: UILabel!
#IBOutlet weak var oneMothDailyPriceLabel: UILabel!
#IBOutlet weak var sixMonthPriceLabel: UILabel!
#IBOutlet weak var sixMonthDailyPriceLabel: UILabel!
#IBOutlet weak var twelveMonthPriceLabel: UILabel!
#IBOutlet weak var twelveMonthDailyPriceLabel: UILabel!
#IBOutlet weak var tableViewCellOneMonth: UITableViewCell!
#IBOutlet weak var tableViewCellSixMonth: UITableViewCell!
#IBOutlet weak var tableViewCellTwelveMonth: UITableViewCell!
#IBAction func cancelButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
// MARK: ViewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
hideSubscriptions()
navigationItem.title = "Premium PRO version"
Purchases.default.initialize { [weak self] result in
guard let self = self else { return }
switch result {
case let .success(products):
guard products.count > 0 else {
let message = "Failed to get a list of subscriptions. Please try again later."
self.showMessage("Oops", withMessage: message)
return
}
self.showSubscriptions()
DispatchQueue.main.async {
self.updateInterface(products: products)
}
default:
break
}
}
}
// MARK: Functions()
private func updateInterface(products: [SKProduct]) {
updateOneMonth(with: products[0])
updateSixMonth(with: products[1])
updateTwelveMonth(with: products[2])
}
private func hideSubscriptions() {
DispatchQueue.main.async {
self.tableViewCellOneMonth.isHidden = true
self.tableViewCellSixMonth.isHidden = true
self.tableViewCellTwelveMonth.isHidden = true
}
}
private func showSubscriptions() {
DispatchQueue.main.async {
self.tableViewCellOneMonth.isHidden = false
self.tableViewCellSixMonth.isHidden = false
self.tableViewCellTwelveMonth.isHidden = false
}
}
func showMessage(_ title: String, withMessage message: String) {
DispatchQueue.main.async {
let alert = UIAlertController(title: title,
message: message,
preferredStyle: UIAlertController.Style.alert)
let dismiss = UIAlertAction(title: "Ok",
style: UIAlertAction.Style.default,
handler: nil)
alert.addAction(dismiss)
self.present(alert, animated: true, completion: nil)
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if indexPath.section == 0 && indexPath.row == 0 {
guard let premiumBuyVC = storyboard.instantiateViewController(identifier: "PremiumBuyVC") as? PremiumBuyVC else { return }
premiumBuyVC.price = oneMonthPrice
premiumBuyVC.productId = "1month"
premiumBuyVC.period = "per month"
show(premiumBuyVC, sender: nil)
}
if indexPath.section == 1 && indexPath.row == 0 {
guard let premiumBuyVC = storyboard.instantiateViewController(identifier: "PremiumBuyVC") as? PremiumBuyVC else { return }
premiumBuyVC.price = sixMonthPrice
premiumBuyVC.productId = "6month"
premiumBuyVC.period = "per 6 month"
show(premiumBuyVC, sender: nil)
}
if indexPath.section == 2 && indexPath.row == 0 {
guard let premiumBuyVC = storyboard.instantiateViewController(identifier: "PremiumBuyVC") as? PremiumBuyVC else { return }
premiumBuyVC.price = twelveMonthPrice
premiumBuyVC.productId = "12month"
premiumBuyVC.period = "per 12 month"
show(premiumBuyVC, sender: nil)
}
}
}
extension SKProduct {
public var localizedPrice: String? {
let numberFormatter = NumberFormatter()
numberFormatter.locale = self.priceLocale
numberFormatter.numberStyle = .currency
return numberFormatter.string(from: self.price)
}
}
// MARK: Обновление информации
// в cell для 1, 6, 12 месяцев
extension PremiumRatesTVC {
func updateOneMonth(with product: SKProduct) {
let withCurrency = "\(product.priceLocale.currencyCode ?? " ")"
let daily = dailyPrice(from: Double(truncating: product.price), withMonth: 1.0)
oneMonthPriceLabel.text = "\(product.price) \(withCurrency)"
oneMothDailyPriceLabel.text = "\(daily) \(withCurrency)"
oneMonthPrice = "\(product.price) \(withCurrency)"
}
func updateSixMonth(with product: SKProduct) {
let withCurrency = "\(product.priceLocale.currencyCode ?? " ")"
let daily = dailyPrice(from: Double(truncating: product.price), withMonth: 6.0)
sixMonthPriceLabel.text = "\(product.price) \(withCurrency)"
sixMonthDailyPriceLabel.text = "\(daily) \(withCurrency)"
sixMonthPrice = "\(product.price) \(withCurrency)"
}
func updateTwelveMonth(with product: SKProduct) {
let withCurrency = "\(product.priceLocale.currencyCode ?? " ")"
let daily = dailyPrice(from: Double(truncating: product.price), withMonth: 12.0)
twelveMonthPriceLabel.text = "\(product.price) \(withCurrency)"
twelveMonthDailyPriceLabel.text = "\(daily) \(withCurrency)"
twelveMonthPrice = "\(product.price) \(withCurrency)"
}
func dailyPrice(from value: Double, withMonth: Double) -> String {
let days = withMonth * 30
let result = value / days
return String(format: "%.2f", result)
}
}
This image shows the testConfiguration.storekit file:
Also the image from the edit scheme:
also the file testConfiguration.storekit in the left menu with a question mark.
I hope I described the problem I encountered in detail and correctly. Many thanks to everyone who took the time.
I had this problem too. Try with a device on iOS 15.X.
Built with Xcode 14.0.1 iPhone 13 iOS 16.0: Skipping product because no price was available
Built with Xcode 14.0.1 iPhone 11 iOS 15.5: everything works.
I had the same problem and the same answers as #Vjardel, that this occurs on iOS 16 when started with Xcode. In my case I tested it with an iPad mini 5th generation on iOS 16 Beta 10.
Although, I discovered that this issues does not happen on the same device, if you try it with a TestFlight build. Therefore, you can test it with TestFlight, plus I assume that if the app is in the App Store the issue won't happen, as well.
My boss didn't have the Paid Apps field filled in. Be sure to look to make sure it is active.
Check this answer
I am building a chat client for iOS using ejabberd and Swift. When I try to retrieve the roster for a user it returns an empty set despite this user having many buddies. Where exactly am I going wrong? I have found similar questions but they seem dated.
Here is my code:
class XMPPController: NSObject {
var hostName: String
var hostPort: UInt16
var password: String
var userJID: XMPPJID
var xmppStream: XMPPStream
var xmppRoster: XMPPRoster!
var xmppRosterStorage: XMPPRosterCoreDataStorage!
init(inputHostName: String, inputUserJIDString: String, inputHostPort: UInt16, inputPassword: String) throws {
guard let formattedUserJID = XMPPJID(string: inputUserJIDString) else {
throw XMPPControllerError.wrongUserJID
}
self.hostName = inputHostName
self.hostPort = inputHostPort
self.password = inputPassword
self.userJID = formattedUserJID
self.xmppStream = XMPPStream()
self.xmppStream.hostName = hostName
self.xmppStream.hostPort = hostPort
self.xmppStream.myJID = userJID
self.xmppRosterStorage = XMPPRosterCoreDataStorage()
self.xmppRoster = XMPPRoster(rosterStorage: xmppRosterStorage)
super.init()
xmppStream.addDelegate(self, delegateQueue: DispatchQueue.main)
xmppRoster.addDelegate(self, delegateQueue: DispatchQueue.main)
xmppRoster.autoFetchRoster = true;
xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = true;
xmppRoster.activate(xmppStream)
print(xmppRosterStorage.jids(for: xmppStream))
}
func connect() {
if self.xmppStream.isDisconnected {
}
do {
try self.xmppStream.connect(withTimeout: 5)
} catch {
print("Error Connecting")
}
}
func disconnect(){
self.xmppStream.disconnect()
}
}
extension XMPPController: XMPPStreamDelegate {
func xmppStreamDidConnect(_ sender: XMPPStream) {
print("Stream: Connected")
try! sender.authenticate(withPassword: password)
}
func xmppStreamDidDisconnect(_ sender: XMPPStream, withError error: Error?) {
print("Stream: Disconnected")
}
func xmppStreamDidAuthenticate(_ sender: XMPPStream) {
let presence = XMPPPresence()
self.xmppStream.send(presence)
print("Stream: Authenticated")
NotificationCenter.default.post(name: Notification.Name(rawValue: authenticatedNotificationKey), object: self)
}
func xmppStream(_ sender: XMPPStream, didNotAuthenticate error: DDXMLElement) {
print("Wrong credentials")
}
}
Thank you.
Answering my own questions.
Two problems.
I did not define the superclass XMPPRosterDelegate for XMPPController.
I did not call
func xmppRosterDidEndPopulating(_ sender: XMPPRoster) {
print(xmppRosterStorage.jids(for: xmppStream))
}
something that could only be done having declared XMPPRosterDelegate.
I cannot see to your add any user your roster. You can try this :
func sendSubscribePresenceFromUserRequest ( _ username : String) {
let otherUser = XMPPJID(string: "\(username)#\(xmppDomain)")!
self.roster.addUser(otherUser, withNickname: "Other user name" , groups: nil, subscribeToPresence: true)
}
Then You call setup XMPPRoster method after 'xmppStreamDidAuthenticate' method just like this :
func rosterSetup() {
let storage = XMPPRosterCoreDataStorage.sharedInstance()
roster = XMPPRoster(rosterStorage: storage!, dispatchQueue: DispatchQueue.main)
if let rosterXmpp = roster {
rosterXmpp.setNickname("Your name" , forUser: stream.myJID!)
rosterXmpp.activate(stream)
rosterXmpp.addDelegate(self, delegateQueue: DispatchQueue.main)
rosterXmpp.autoFetchRoster = true
rosterXmpp.autoClearAllUsersAndResources = true
rosterXmpp.autoAcceptKnownPresenceSubscriptionRequests = true
rosterXmpp.fetch()
}
}
I am new (very new!!) to swift and straggling to make my UI to send a string over the serial port. I've managed to open the port and read/parse the incoming traffic but when it comes to send a string, nothing is sent.
What I need to do is typing in the sendTextField and when press the SendButton to send the string to serial port. Also, when I print the data which is what I want to send over serial port, it prints the number of bytes I try to send (i.e. 5 bytes). Shouldn't this be the string "Hello" that I try to send to serial port?
I am using Xcode Version 11.2 (11B52) and Swift 5.
Any help will be really appreciated. Thank you in advance!
This is how I call the "send" function:
#IBAction func SendButton(_ sender: Any) {
let TxData = sendTextField.stringValue
SFSerialIn.SendSerialData(TxData)
}
My main program is below:
import ORSSerial
import IOKit
import IOKit.serial
let SFSerialRegexp =
"(?<SFmode>[A-Z]+),\\s*" + "(?<prox>[0-1]),\\s*"
class SFSerialIn: NSObject, ORSSerialPortDelegate {
let path = "/dev/cu.usbserial-AI0484S9"
let baudRate: NSNumber = 115200
var serialPort: ORSSerialPort?
var delegate: SFSerialDelegate?
var stringBuffer = ""
var regex: NSRegularExpression!
var receivedBufferStart = false
override init() {
regex = try! NSRegularExpression(pattern: SFSerialRegexp)
}
deinit {
disconnect()
}
func SendSerialData(_ TxData: String){
let data = Data(TxData.utf8)
serialPort?.send(data)
print(TxData)
print(data)
}
func connect() {
if let serialPort = ORSSerialPort(path: path) {
serialPort.baudRate = baudRate
serialPort.delegate = self
serialPort.open()
} else {
print("Failed to open serial port")
}
}
func disconnect() {
serialPort?.close()
print("closing port...")
}
func serialPort(_ serialPort: ORSSerialPort, didReceive data: Data) {
guard let string = String(data: data, encoding: .utf8)
else {
return
}
stringBuffer += string
parseBuffer()
}
func parseBuffer() {
let lines = stringBuffer.split { $0.isNewline }
guard lines.count > 1 else {
return
}
let nextLines = lines[1...].joined()
if !receivedBufferStart {
stringBuffer = nextLines
receivedBufferStart = true
return
}
let line = String(lines[0])
if let matchResult = regex.firstMatch(in: line, range: NSRange(..<line.endIndex, in: line)) {
let sensorFrame = SFFrame(matchResult: matchResult, string: line)
delegate?.receive(sensorFrame: sensorFrame)
stringBuffer = nextLines
return
}
print("Failed to parse line :(")
stringBuffer = nextLines
}
func serialPort(_ serialPort: ORSSerialPort, didEncounterError error: Error) {
print("Serial port encountered error", error)
}
func serialPortWasOpened(_ serialPort: ORSSerialPort) {
print("Serial port opened")
}
func serialPortWasClosed(_ serialPort: ORSSerialPort) {
print("Serial port closed")
}
func serialPortWasRemovedFromSystem(_ serialPort: ORSSerialPort) {
print("Serial port was removed from system")
}
}
protocol SFSerialDelegate {
func receive(sensorFrame: SFFrame)
}
extension StringProtocol {
var data: Data { .init(utf8) }
}
It doesn't look to me like you're ever storing the opened serial port in your serialPort instance property. So, when you do serialPort?.send(data), serialPort is nil, and the ? (optional chaining) operator means that send() isn't called.
Try storing the serial port in your property after opening it:
func connect() {
if let serialPort = ORSSerialPort(path: path) {
serialPort.baudRate = baudRate
serialPort.delegate = self
serialPort.open()
self.serialPort = serialPort
} else {
print("Failed to open serial port")
}
}
I have an embedded CPU sending strings through BLE UART to a iPhone. I can connect to the device, setup the UART characteristics, but it only reads two characters and didUpdateValueFor is called no more. I have to be setting something up wrong. There there seems to be some buffer overflow or it's not rest for each read.
I want to be able to setup a UART connection between the board and the iPhone. I can take care of parsing.
Right not I'm sending the character '0' every 250 ms from the board. It picks up two '0's and the event is not called anymore.
// ViewController.swift
import UIKit
import CoreBluetooth
class ScannerVC: UIViewController, CBCentralManagerDelegate {
var centralManager: CBCentralManager!
var peripheral: CBPeripheral!
var scannedGrowZeboDevices: [CBPeripheral] = []
var timer: Timer!
var connectionVC: ConnectionVC!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var msg = ""
switch (central.state) {
case .poweredOff:
msg = "Bluetooth is powered off"
case .poweredOn:
msg = "Bluetooth is powered on"
timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(ScannerVC.stopScanning), userInfo: nil, repeats: false)
centralManager.scanForPeripherals(withServices: nil, options: nil)
case .unsupported:
msg = "Bluetooth is unsopported"
case .resetting:
msg = "The BLE Manager is resetting; a state update is pending"
case .unknown:
msg = "BThe state of the BLE Manager is unkown"
default:
break
}
print("Status: \(msg)")
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
if peripheralName.contains("Grow") || peripheralName.contains("paul") {
print("Peripheral Name: \(peripheralName)")
for existing in scannedGrowZeboDevices {
if existing.identifier == peripheral.identifier {
print("Peripheral already exists, returning")
return
}
}
// adding peripheral to the array //
scannedGrowZeboDevices.append(peripheral)
self.peripheral = peripheral
print("There are \(scannedGrowZeboDevices.count) peripherals in the array")
}
}
}
#objc func stopScanning() {
timer.invalidate()
centralManager.stopScan()
print("Scanning stopped")
performSegue(withIdentifier: "toNavigation", sender: scannedGrowZeboDevices)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toNavigation" {
if let navController = segue.destination as? UINavigationController {
let connectionVC = navController.viewControllers.first as! ConnectionVC
centralManager.delegate = connectionVC
connectionVC.centralManager = self.centralManager
connectionVC.growzeboDevices = sender as! [CBPeripheral?]
}
}
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
//*********************************************************************************
// Added May 28, 2018
// This shows there is a connection to the peripheral
//*********************************************************************************
print("[DEBUG] Connected to peripheral \(peripheral.identifier.uuidString)")
// self.activePeripheral = peripheral
peripheral.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
let defaults = UserDefaults.standard
if let services = peripheral.services as [CBService]? {
//print("Discovered Services for: \(String(describing: peripheral.name))")
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
print("Found serial port service")
}
//**********************************************************************
// Added May 28, 2018
// Add the TX and RX for the discovered services
//*********************************************************************
print("[DEBUG] Found services for peripheral: \(peripheral.identifier.uuidString)")
// for service in peripheral.services! {
// let theCharacteristics = [CBUUID(serialPortServiceUuid), CBUUID(serialPortServiceUuid)]
// }
if let name = peripheral.name {
print("Connected to: \(name)")
if let _ = defaults.dictionary(forKey: peripheral.name!) {
// To do if the card has already been connected before
} else {
// To do if the card getting connected first time
settingsToSave["name"] = peripheral.name!
settingsToSave["connected"] = true
defaults.set(settingsToSave, forKey: peripheral.name!)
let detailsVC = storyboard?.instantiateViewController(withIdentifier: "detailsVC") as! DetailsVC
centralManager.delegate = detailsVC
let cm = centralManager
detailsVC.centralManager = cm
detailsVC.peripheral = peripheral
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.navigationController?.pushViewController(detailsVC, animated: true)
}
}
}
growzebosTableView.reloadData()
}
}
//************************** Functionality Starts here *******************************//
override func viewDidLoad() {
super.viewDidLoad()
vegSettings = GrowzeboBoxModel()
bloomSettings = GrowzeboBoxModel()
customSettings = GrowzeboBoxModel()
setupPickerViews()
//*************************************************
// Added May 15, 2018
// Thiz sets up the object for picking AUX values
setupPumpPickerViews()
let defaults = UserDefaults.standard
savedSettings = defaults.dictionary(forKey: peripheral.name!)!
deviceName.text = peripheral.name
// state.text = String("\(peripheral.state)")
let buf = [UInt8]()
// let buf: [UInt8] = [10000]
dataRxCredits = Data.init(buf)
let buf2: [UInt8] = [0xFF]
disconnectCredit = Data.init(bytes: buf2)
// adding targets to sliders //
func open() -> Bool {
var ok = false
if peripheral.state == .connected {
state.text = "Connected"
// nTxCredits = 0
// pendingData = nil
pendingCredits = false
peripheral.delegate = self
initServicesAndCharacteristics()
openSerialPortService()
ok = true
}
else{
state.text = "Disconnected"
}
return ok
}
// Initialize all the required services and characteristics //
func initServicesAndCharacteristics() {
var s: CBService?
var c: CBCharacteristic?
service = nil
creditsCharacteristic = nil
fifoCharacteristic = nil
deviceIdService = nil
fwVersionCharacteristic = nil
modelNumberCharacteristic = nil
for i in 0..<peripheral.services!.count {
s = peripheral.services![i]
if let data = s?.uuid.data {
let dataInBytes = [UInt8](data)
if (s?.uuid.data.count == 16) && (memcmp(dataInBytes, serialPortServiceUuid, 16) == 0) {
service = s
}
else if (s?.uuid.data.count == 2) && (memcmp(dataInBytes, deviceIdServiceUuid, 2) == 0) {
deviceIdService = s
}
}
}
if service != nil {
if service!.characteristics != nil {
for i in 0..<service!.characteristics!.count {
c = service!.characteristics![i]
if let data = c?.uuid.data {
let dataInBytes = [UInt8](data)
if (c?.uuid.data.count == 16) && (memcmp(dataInBytes, serialPortFifoCharactUuid, 16) == 0) {
fifoCharacteristic = c
}
else if (c?.uuid.data.count == 16) && (memcmp(dataInBytes, creditsCharactUuid, 16) == 0) {
creditsCharacteristic = c
}
}
}
}
}
if deviceIdService != nil {
if deviceIdService!.characteristics != nil {
for i in 0..<deviceIdService!.characteristics!.count {
c = deviceIdService!.characteristics![i]
if (c?.uuid.data.count == 2) {
modelNumberCharacteristic = c
}
else if (c?.uuid.data.count == 2) {
fwVersionCharacteristic = c
}
}
}
}
}
// Opening serial port //
func openSerialPortService() {
// serialPortVersion = 2
peripheral.setNotifyValue(true, for: creditsCharacteristic!)
peripheral.setNotifyValue(true, for: fifoCharacteristic!)
peripheral.writeValue(dataRxCredits!, for: creditsCharacteristic!, type: .withoutResponse)
}
// Closing serial port //
func closeSerialPortService() {
peripheral.setNotifyValue(false, for: creditsCharacteristic!)
peripheral.setNotifyValue(false, for: fifoCharacteristic!)
peripheral.writeValue(disconnectCredit!, for: creditsCharacteristic!, type: .withoutResponse)
}
// Send a serial message to the card //
#objc func sendSerialMessage(_ message: String) {
if (peripheral.state != .connected)
{
SVProgressHUD.dismiss()
navigationController?.popToRootViewController(animated: true)
print("Disconected")
}
let msgToSend = message.appendingFormat("\r")
//let msg = "show\r"
let buf: [UInt8] = Array(msgToSend.utf8)
let data = Data(buf)
peripheral.writeValue(data, for: fifoCharacteristic!, type: CBCharacteristicWriteType.withoutResponse)
print("\(message) sent")
commandname = message
}
I have a app that has a UILabel that I would like to be updated by another swift class? The class is a service class and has no relation to the view controller with the UILabel but I would still like that service class to be able to update the label.
I think that this answer was what I needed but it does not work for Swift 3 and the text the label changes to is hardcoded which is not what I wanted either. I could not find anything else helpful about this.
BTService.swift:
import Foundation
import CoreBluetooth
let BLEServiceUUID = CBUUID(string: "025A7775-49AA-42BD-BBDB-E2AE77782966")
let PositionCharUUID = CBUUID(string: "A9CD2F86-8661-4EB1-B132-367A3434BC90") //RX let
let WriteCharUUID = CBUUID(string: "F38A2C23-BC54-40FC-BED0-60EDDA139F47") //TX let F38A2C23-BC54-40FC-BED0-60EDDA139F47
let BLEServiceChangedStatusNotification = "kBLEServiceChangedStatusNotification"
class BTService: NSObject, CBPeripheralDelegate {
var peripheral: CBPeripheral?
var positionCharacteristic: CBCharacteristic?
var writeCharacteristic: CBCharacteristic?
init(initWithPeripheral peripheral: CBPeripheral) {
super.init()
self.peripheral = peripheral
self.peripheral?.delegate = self
}
deinit{
self.reset()
}
func startDiscoveringServices() {
self.peripheral?.discoverServices([BLEServiceUUID])
}
func reset() {
if peripheral != nil {
peripheral = nil
}
// Deallocating therefore send notification
self.sendBTServiceNotificationWithIsBluetoothConnected(false)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
let uuidsForBTService: [CBUUID] = [PositionCharUUID]
let uuidsForBTService1: [CBUUID] = [WriteCharUUID]
if (peripheral != self.peripheral) { // Wrong Peripheral
return
}
if (error != nil) {
return
}
if ((peripheral.services == nil) || (peripheral.services?.count == 0)) {
// No Services
return
}
for service in peripheral.services! {
if service.uuid == BLEServiceUUID {
peripheral.discoverCharacteristics(uuidsForBTService, for: service
)
peripheral.discoverCharacteristics(uuidsForBTService1, for: service )
} }
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if (peripheral != self.peripheral) { // Wrong Peripheral
return }
if (error != nil) {
return
}
for characteristic in service.characteristics! { if characteristic.uuid == PositionCharUUID {
self.positionCharacteristic = (characteristic )
peripheral.setNotifyValue(true, for: characteristic )
self.sendBTServiceNotificationWithIsBluetoothConnected(true)
}
else if characteristic.uuid == WriteCharUUID {
self.writeCharacteristic = (characteristic )
peripheral.setNotifyValue(true, for: characteristic )
self.sendBTServiceNotificationWithIsBluetoothConnected(true)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){
let it = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
if(it != nil){
print("Val: \(it)") //Stackoverflow: This is what needs to be sent to the ViewController to be updated!!!
}
}
func writePosition(position: UInt8) {
if self.writeCharacteristic == nil {
return
}
var positionValue = position
let data = NSData(bytes: &positionValue, length: MemoryLayout<UInt8>.size)
self.peripheral?.writeValue(data as Data, for: self.writeCharacteristic!, type: CBCharacteristicWriteType.withResponse)
}
func readPosition() -> NSString? {
if self.positionCharacteristic == nil {
return nil }
self.peripheral?.readValue(for: self.positionCharacteristic!)
if ((self.positionCharacteristic?.value) != nil) {
print(self.positionCharacteristic!.value!)
return NSString(data: self.positionCharacteristic!.value!, encoding:
String.Encoding.utf8.rawValue) }
return nil
}
func sendBTServiceNotificationWithIsBluetoothConnected(_ isBluetoothConnected: Bool) {
let connectionDetails = ["isConnected": isBluetoothConnected]
NotificationCenter.default.post(name: Notification.Name(rawValue: BLEServiceChangedStatusNotification), object: self, userInfo: connectionDetails)
}
}
The ViewController:
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var imgBluetoothStatus: UIImageView!
#IBOutlet weak var positionSlider: UISlider!
#IBOutlet weak var BottomLabel: UILabel!
var timerTXDelay: Timer?
var allowTX = true
var lastPosition: UInt8 = 255
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Rotate slider to vertical position
// Watch Bluetooth connection
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.connectionChanged(_:)), name: NSNotification.Name(rawValue: BLEServiceChangedStatusNotification), object: nil)
// Start the Bluetooth discovery process
_ = btDiscoverySharedInstance
}
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: BLEServiceChangedStatusNotification), object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.stopTimerTXDelay()
}
#IBAction func positionSliderChanged(_ sender: UISlider) {
self.sendPosition(UInt8(sender.value))
}
func connectionChanged(_ notification: Notification) {
// Connection status changed. Indicate on GUI.
let userInfo = (notification as NSNotification).userInfo as! [String: Bool]
DispatchQueue.main.async(execute: {
// Set image based on connection status
if let isConnected: Bool = userInfo["isConnected"] {
if isConnected {
self.imgBluetoothStatus.image = UIImage(named: "Bluetooth_Connected")
// Send current slider position
self.sendPosition(UInt8( self.positionSlider.value))
} else {
self.imgBluetoothStatus.image = UIImage(named: "Bluetooth_Disconnected")
}
}
});
}
func sendPosition(_ position: UInt8) {
// Valid position range: 0 to 180
if !allowTX {
return
}
// Validate value
if position == lastPosition {
return
}
else if ((position < 0) || (position > 180)) {
return
}
// Send position to BLE Shield (if service exists and is connected)
if let bleService = btDiscoverySharedInstance.bleService {
bleService.writePosition(position: position)
lastPosition = position;
// Start delay timer
allowTX = false
if timerTXDelay == nil {
timerTXDelay = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(ViewController.timerTXDelayElapsed), userInfo: nil, repeats: false)
}
}
}
func timerTXDelayElapsed() {
self.allowTX = true
self.stopTimerTXDelay()
// Send current slider position
self.sendPosition(UInt8(self.positionSlider.value))
}
func stopTimerTXDelay() {
if self.timerTXDelay == nil {
return
}
timerTXDelay?.invalidate()
self.timerTXDelay = nil
}
}
In the BTService, there is a didUpdateValueFor function and I would like the it variable to be sent to the ViewController and have it set as the BottomLabel.
it should be similar to your existing BLEServiceChangedStatusNotification:
define the name:
let BLEServicePeripheralChangedNotification = "kBLEServicePeripheralChangedNotification"
add the observer in viewDidLoad:
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.peripheralChanged(_:)), name: NSNotification.Name(rawValue: BLEServicePeripheralChangedNotification), object: nil)
remove the observer in deinit:
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: BLEServicePeripheralChangedNotification), object: nil)
add the function:
func peripheralChanged(_ notification: Notification) {
// peripheral status changed. Indicate on GUI.
let userInfo = (notification as NSNotification).userInfo as! [String: NSString]
if let it: NSString = userInfo["it"] {
//BottomLabel should be bottomLabel in declaration!
BottomLabel.text = it
}
}
add the post to notification center:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){
let it = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
if(it != nil){
print("Val: \(it)")
let itDetails = ["it": it]
NotificationCenter.default.post(name: Notification.Name(rawValue: BLEServicePeripheralChangedNotification), object: self, userInfo: itDetails)
}
}
PS: BottomLabel should be bottomLabel in declaration.