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")
}
}
Related
I'm studying swift 5.6 Network framework. For this I have a Java-based server, that waits a udp packet of size 64 at localhost port 10000 and sends it back to localhost port 20000. Here is my implementation for Swift :
import Foundation
import Network
class UdpConnection {
private var connection: NWConnection?
private var isConnectionReady = false
init?(host: String, port: UInt16) {
self.connection = NWConnection(
host: NWEndpoint.Host(host),
port: NWEndpoint.Port(integerLiteral: port),
using: .udp
)
let connectionEstablishWaiter = DispatchSemaphore(value: 0)
self.connection?.stateUpdateHandler = { [weak self] (newState) in
switch (newState) {
case .ready:
self?.isConnectionReady = true
default :
self?.isConnectionReady = false
}
connectionEstablishWaiter.signal()
}
self.connection?.start(queue: .global())
switch connectionEstablishWaiter.wait(timeout: .now() + 1) {
case .timedOut:
return nil
default:
()
}
}
func sendUDP(content: Data) {
let sema = DispatchSemaphore(value: 0)
self.connection?.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
if (NWError == nil) {
print("Data was sent to UDP")
} else {
print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
}
sema.signal()
})))
sema.wait()
}
func receiveUDP() {
self.connection?.receiveMessage { (data, context, isComplete, error) in
if (isComplete) {
if let data = data {
print("Receive is complete : \(data.count)")
} else {
print("Data == nil")
}
}
}
}
}
And here is my test app :
import Foundation
if let udpRequestConnection = UdpConnection(host: "127.0.0.1", port: 10_000) {
print("connection established OK")
if let udpResponseConnection = UdpConnection(host: "127.0.0.1", port: 20_000) {
let data = Data(count: 64)
udpResponseConnection.receiveUDP()
udpRequestConnection.sendUDP(content: data)
print("sent")
}
} else {
print("connection establishing FAILURE")
}
I see no packet received and moreover I see a strange picture in Wireshark :
What am I doing wrong? Why is there an ICMP packet ? What am I missing to get this UDP ?
Ok, it seems that I got the misunderstanding -> NWConnection ctor is responsible for setting outbound connection params. So to listen to UDP stream I need the following implementation :
import Foundation
import Network
import Combine
class UDPListener: ObservableObject {
var listener: NWListener?
var connection: NWConnection?
var queue = DispatchQueue.global(qos: .userInitiated)
/// New data will be place in this variable to be received by observers
#Published private(set) public var messageReceived: Data?
/// When there is an active listening NWConnection this will be `true`
#Published private(set) public var isReady: Bool = false
/// Default value `true`, this will become false if the UDPListener ceases listening for any reason
#Published public var listening: Bool = true
/// A convenience init using Int instead of NWEndpoint.Port
convenience init(on port: Int) {
self.init(on: NWEndpoint.Port(integerLiteral: NWEndpoint.Port.IntegerLiteralType(port)))
}
/// Use this init or the one that takes an Int to start the listener
init(on port: NWEndpoint.Port) {
let params = NWParameters.udp
params.allowFastOpen = true
self.listener = try? NWListener(using: params, on: port)
self.listener?.stateUpdateHandler = { update in
switch update {
case .ready:
self.isReady = true
case .failed, .cancelled:
// Announce we are no longer able to listen
self.listening = false
self.isReady = false
default:
()
}
}
self.listener?.newConnectionHandler = { connection in
self.createConnection(connection: connection)
}
self.listener?.start(queue: self.queue)
}
private func createConnection(connection: NWConnection) {
self.connection = connection
self.connection?.stateUpdateHandler = { (newState) in
switch (newState) {
case .ready:
self.receive()
case .cancelled, .failed:
// Cancel the listener, something went wrong
self.listener?.cancel()
// Announce we are no longer able to listen
self.listening = false
default:
()
}
}
self.connection?.start(queue: .global())
}
func receive() {
self.connection?.receiveMessage { data, context, isComplete, error in
if error != nil {
return
}
guard isComplete, let data = data else {
return
}
self.messageReceived = data
print("Rx data size = \(data.count)")
if self.listening {
self.receive()
}
}
}
func cancel() {
self.listening = false
self.connection?.cancel()
}
}
With this I am able to get the UDP response I expect.
I'm trying to create an app that listens for incoming data on a UDP port using NWListener. This works fine until 248 messages are received - at which point the app crashes with the error message nw_listener_inbox_accept_udp socket() failed [24: Too many open files].
This (I think) relates to the file descriptor limit and so I tried resetting the NWListener within a safe count of 100, but the problem still persists. This seems clumsy and I'm not sure it's actually possible within a receive.
How can I truly reset it such that it releases any open files?
Full code below, which is instantiated within a SwiftUI content view using:
.onAppear() {
udpListener.start(port: self.udpPort)
}
Full class:
import Foundation
import Network
class UdpListener: NSObject, ObservableObject {
private var listener: NWListener?
private var port: NWEndpoint.Port?
#Published var incoming: String = ""
#Published var messageCount: Int = 0
func start(port: NWEndpoint.Port) {
self.port = port
do {
let params = NWParameters.udp
params.allowLocalEndpointReuse = true
self.listener = try NWListener(using: params, on: port)
self.listener?.stateUpdateHandler = {(newState) in
switch newState {
case .ready:
print("ready")
default:
break
}
}
self.listener?.newConnectionHandler = {(newConnection) in
newConnection.stateUpdateHandler = {newState in
switch newState {
case .ready:
self.receive(on: newConnection)
default:
break
}
}
newConnection.start(queue: DispatchQueue(label: "new client"))
}
} catch {
print("unable to create listener")
}
self.listener?.start(queue: .main)
}
func receive(on connection: NWConnection) {
connection.receiveMessage { (data, context, isComplete, error) in
if let error = error {
print(error)
return
}
guard let data = data, !data.isEmpty else {
print("unable to receive data")
return
}
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss.SSSS"
DispatchQueue.main.async {
self.messageCount = self.messageCount + 1
self.incoming = formatter.string(from: date) + " " + String(decoding: data, as: UTF8.self) + "\n" + self.incoming
}
if self.messageCount == 100 {
print("Resetting")
self.listener?.stateUpdateHandler = nil
self.listener?.newConnectionHandler = nil
self.listener?.cancel()
self.listener = nil
self.start(port: self.port!)
}
}
}
}
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 creating an onboarding portion of an app which gets the user's contacts to check which already have the app to add as friends. I'm using the CNContact framework. I have created several methods I'm using to get a full list of the users' contacts, check if they have the app, and enumerate them in a UITableView. However, when the view loads, the app crashes with the error "A property was not requested when contact was fetched." I already make a fetch request with the keys CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, ad CNContactImageDataKey. I've included all my code here:
import Foundation
import Contacts
import PhoneNumberKit
struct ContactService {
static func createContactArray() -> [CNContact] {
var tempContacts = [CNContact]()
let store = CNContactStore()
store.requestAccess(for: .contacts) { (granted, error) in
if let _ = error {
print("failed to request access to contacts")
return
}
if granted {
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactImageDataKey]
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
request.sortOrder = CNContactSortOrder.familyName
do {
try store.enumerateContacts(with: request, usingBlock: { (contact, stop) in
tempContacts.append(contact)
})
} catch {
print("unable to fetch contacts")
}
print("created contact list")
}
}
return tempContacts
}
static func createFetchedContactArray(contactArray: [CNContact], completion: #escaping ([FetchedContact]?) -> Void) -> Void {
var temp = [FetchedContact]()
getNumsInFirestore { (nums) in
if let nums = nums {
for c in contactArray {
let f = FetchedContact(cnContact: c, numsInFirebase: nums)
temp.append(f)
}
return completion(temp)
} else {
print("Error retrieving numbers")
}
}
return completion(nil)
}
static func getNumsInFirestore(_ completion: #escaping (_ nums : [String]?) -> Void ) -> Void {
var numsInFirebase = [String]()
let db = FirestoreService.db
db.collection(Constants.Firestore.Collections.users).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting user documents: \(err)")
completion(nil)
} else {
for doc in querySnapshot!.documents {
let dict = doc.data()
numsInFirebase.append(dict[Constants.Firestore.Keys.phone] as! String)
}
completion(numsInFirebase)
}
}
}
static func getPhoneStrings(contact: CNContact) -> [String] {
var temp = [String]()
let cnPhoneNums = contact.phoneNumbers
for n in cnPhoneNums {
temp.append(n.value.stringValue)
}
return temp
}
static func hasBump(contact: CNContact, completion: #escaping (_ h: Bool) -> Void ) -> Void {
let contactNums = ContactService.getPhoneStrings(contact: contact)
ContactService.getNumsInFirestore { (nums) in
if let nums = nums {
return completion(contactNums.contains(where: nums.contains))
} else {
print("Error retrieving numbers from firestore")
return completion(false)
}
}
}
static func anyContactsWithBump(completion: #escaping (_ yes: Bool) -> Void) {
let contacts = createContactArray()
var tempBool = false
let workgroup = DispatchGroup()
for c in contacts {
workgroup.enter()
hasBump(contact: c) { (has) in
if has {
tempBool = true
}
workgroup.leave()
}
}
workgroup.notify(queue: .main) {
completion(tempBool)
}
}
}
Then I call the methods in the view controller class:
import UIKit
import Contacts
import FirebaseDatabase
import Firebase
import FirebaseFirestore
class AddContactsVC: UIViewController {
var fetchedContactsWithBump: [FetchedContact] = []
override func viewDidLoad() {
let cnContacts = ContactService.createContactArray()
var contactsWithBump = [CNContact]()
let workgroup = DispatchGroup()
for contact in cnContacts {
workgroup.enter()
ContactService.hasBump(contact: contact) { (has) in
if has {
contactsWithBump.append(contact)
}
workgroup.leave()
}
}
workgroup.notify(queue: .main) {
print("Number of contacts with Bump: \(contactsWithBump.count)")
ContactService.createFetchedContactArray(contactArray: contactsWithBump) { (fetchedContacts) in
if let fetchedContacts = fetchedContacts {
self.fetchedContactsWithBump = fetchedContacts
} else {
print("Error creating fetchedContacts array.")
}
}
}
I also get the message "Error creating fetchedContacts array" in the console, so I know something is going wrong with that method, I'm just not sure what. Any help is appreciated!
Edit: The exception is thrown at 3 points: 1 at the first line of my FetchedContact init method, which looks like this:
init(cnContact: CNContact, numsInFirebase: [String]) {
if cnContact.imageDataAvailable { self.image = UIImage(data: cnContact.imageData!) }
It also points to the line let f = FetchedContact(cnContact: c, numsInFirebase: nums) in createFetched contact array, and finally at my completion(numsInFirebase) call in getNumsInFirestore.
To start with
let contacts = createContactArray()
will always return an empty array.
This function has a return statement outside the closure so will immediately return an empty array.
Change createContactArray to use a completion handler like the other functions you have to populate contacts from inside the closure.
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
}
}