Nearby Bluetooth devices using Swift 3.0 - ios

I'm looking for a way to programmatically list any nearby Bluetooth devices (discoverable) that my device finds. I have not been able to find any information or tutorials regarding performing this call in Swift 3.0. This Q-A post discusses finding these devices using Swift 1.0 and building in Xcode 6, rather than the latest version 8.
I did my best to try to make my code into the 3.0 Syntax from the 1.0, but while running the following code, nothing is returned in the Playground:
import Cocoa
import IOBluetooth
import PlaygroundSupport
class BlueDelegate : IOBluetoothDeviceInquiryDelegate {
func deviceInquiryComplete(_ sender: IOBluetoothDeviceInquiry, error: IOReturn, aborted: Bool) {
aborted
print("called")
let devices = sender.foundDevices()
for device : Any? in devices! {
if let thingy = device as? IOBluetoothDevice {
thingy.getAddress()
}
}
}
}
var delegate = BlueDelegate()
var inquiry = IOBluetoothDeviceInquiry(delegate: delegate)
inquiry?.start()
PlaygroundPage.current.needsIndefiniteExecution = true

Using IOBluetooth the Correct Way
The following code works flawlessly in Xcode Version 8.2.1 (8C1002), Swift 3.0. There are a few lines that aren't required, such as the entire method of deviceInquiryStarted.
Update: These usages still work as of Xcode 9.2 (9B55) and Swift 4.
Playground
import Cocoa
import IOBluetooth
import PlaygroundSupport
class BlueDelegate : IOBluetoothDeviceInquiryDelegate {
func deviceInquiryStarted(_ sender: IOBluetoothDeviceInquiry) {
print("Inquiry Started...")
//optional, but can notify you when the inquiry has started.
}
func deviceInquiryDeviceFound(_ sender: IOBluetoothDeviceInquiry, device: IOBluetoothDevice) {
print("\(device.addressString!)")
}
func deviceInquiryComplete(_ sender: IOBluetoothDeviceInquiry!, error: IOReturn, aborted: Bool) {
//optional, but can notify you once the inquiry is completed.
}
}
var delegate = BlueDelegate()
var ibdi = IOBluetoothDeviceInquiry(delegate: delegate)
ibdi?.updateNewDeviceNames = true
ibdi?.start()
PlaygroundPage.current.needsIndefiniteExecution = true
Project-Application Usage
import Cocoa
import IOBluetooth
import ...
class BlueDelegate : IOBluetoothDeviceInquiryDelegate {
func deviceInquiryStarted(_ sender: IOBluetoothDeviceInquiry) {
print("Inquiry Started...")
}
func deviceInquiryDeviceFound(_ sender: IOBluetoothDeviceInquiry, device: IOBluetoothDevice) {
print("\(device.addressString!)")
}
}
//other classes here:
//reference the following outside of any class:
var delegate = BlueDelegate()
var ibdi = IOBluetoothDeviceInquiry(delegate: delegate)
//refer to these specifically inside of any class:
ibdi?.updateNewDeviceNames = true
ibdi?.start() //recommended under after an action-button press.
Explanation
The issue I was originally faced with was trying to access the information as the inquiry was still in process.
When I accessed it, under many different occasions my playground would hang and I would be forced to force quit both Xcode.app, and com.apple.CoreSimulator.CoreSimulatorService from the Activity Monitor. I lead myself to believe that this was just a Playground bug, only to learn that my application would crash once the inquiry finished.
As Apple's API Reference states:
Important Note: DO NOT perform remote name requests on devices from delegate methods or while this object is in use. If you wish to do your own remote name requests on devices, do them after you have stopped this object. If you do not heed this warning, you could potentially deadlock your process.
Which entirely explained my issue. Rather than directly asking for the IOBluetoothDevice information from the sender.foundDevices() method (which I believe may not have been updating..?) I simply used the parameters built into the function to mention that it was indeed an IOBluetoothDevice object, and simply to ask for that information to be printed.
Final Note
I hope that this Q/A I've created helps others in need when using IOBluetooth in Swift. The lack of any tutorials and the high amounts of outdated, Objective-C code made finding this information very challenging. I'd like to thank #RobNapier for the support on trying to find the answer to this riddle in the beginning. I'd also like to thank NotMyName for the reply on my post on the Apple Developer Forums.
I will be exploring the usage of this in an iOS device more sooner than later!

Related

Firebase crashlytics does not telling me the exact crash problem with swift

I've already read a lot of questions about this on stack, but is there any solution to fix this to get the missing part of the crashreport?
I understand that these crashes were caused by some reason of nil or unwrapped options or something else, but crashes from objc are much better:
My project is based on objetive-c (started a few years ago) but the new big parts are written in swift. All crashes which are coming from the objc part are well detailed but those which are coming from the swift part not.
So the question is, how will I get readable crash reports instead of EXC_BREAKPOINT 0x00000001028817ac
For such cases, I would recommend adding advanced analytics of user behavior in the app and then try to repeat the same steps as the user. This will help to find the root of such crashes. I can share the code on the foundation of which you may add the necessary functionality on the screens of your app:
import Foundation
import Crashlytics
class EventLogger {
/// Log a handled non-fatal exception
class func logException(_ exception: NSException) {
print("Catched exception: \(exception.debugDescription)")
let frames = exception.callStackSymbols.map { symbol in CLSStackFrame(symbol: symbol) }
Crashlytics.sharedInstance().recordCustomExceptionName(exception.name.rawValue, reason: exception.reason, frameArray: frames)
}
private class func log(_ string: String) {
CLSLogv(string, getVaList([]))
}
class func log(screen: String) {
log("User is located at \(screen) Screen")
}
/// Log a status of feature
class func log(feature: String, status: String) {
log("Feature \(feature) was \(status)")
}
/// Log an event
class func log(event: String) {
log("Event: \(event)")
}
}
and impement it like here:
EventLogger.log(event: "Pressed login button")
After the crash, you can see the action log exactly for this crash

How do I see contents of this Swift class object in Xcode after breakpoint?

I'm new to both Xcode and Swift (...and serious Swift programming) and am hoping someone can help me figure out how to view / access the values of this class object.
I have this code in my ViewController.swift for invoking my REST API (via AWS API Gateway) and am attempting to print result to the console. Clearly, all I'm doing here is printing the address of the class object:
#IBAction func userInvokeApi(_ sender: UIButton) {
print("You clicked invoke api...")
let client = SVTLambdaGateClient.default()
client.calcGet(operand2: "3", _operator: "+", operand1: "5").continueWith{ (task: AWSTask?) -> AnyObject? in
if let error = task?.error {
print("Error occurred: \(error)")
return nil
}
if let result = task?.result {
// Do something with result
print("The result is... \(result)")
}
return nil
}
}
Here's what prints:
You clicked invoke api...
The result is... <AmplifyRestApiTest.Empty: 0x600002020770> {
}
(where AmplifyRestApiTest is the name of my Xcode project. Though I'm NOT using AWS Amplify to build this project; mainly because I've run into problems using it.)
I do have this Empty class in Empty.swift that is part of the API Gateway generated iOS Swift SDK:
import Foundation
import AWSCore
#objcMembers
public class Empty : AWSModel {
public override static func jsonKeyPathsByPropertyKey() -> [AnyHashable : Any]!{
var params:[AnyHashable : Any] = [:]`
return params
}
}
Now, when I set a breakpoint on the print statement this is what I see:
Can someone please tell me why I don't see the values relating to this object? What's the strategy for unpacking this API response??
I know that I'm invoking the REST API successfully because I can see (via Cloudwatch logs) that it's returning the result to the Client. So this post is just my attempt to access the corresponding Object.
Another detail: I'm using an API Gateway generated iOS Swift SDK and I followed all of the tutorial instruction for using the SDK in my project.
use lldb command po to print the object.
(lldb) po #"lunar"
lunar
(lldb) p #"lunar"
(NSString *) $13 = 0x00007fdb9d0003b0 #"lunar"
I would suggest going over the docs here...
https://cocoapods.org/pods/AWSCore#getting-started-with-swift
Did you import appropriate headers?
Hope this points you in the right direction.

Razor pay is not redirecting to checkout page, why?

I am using,
*Xcode - 10.2
*Swift language version - swift 5
*RazorPay framework version - 1.1.1 (pod 'razorpay-pod', '1.1.1')
My problem is when I am calling this,
razorpay.open(options, displayController: self)
It gives me an unexpected error (code - 1) with
/Users/travis/build/razorpay/razorpay-ios/RazorpayIOS/CheckoutOtpelf/Classes/RazorpayCheckoutVC.swift deinitialized
Solutions I tried were,
Github community says to hide navigation bar before calling open function(https://github.com/razorpay/razorpay-pod/issues/42).
Used multiple framework version of razor pay
Tried with swift 4.2 also
I have cleaned my project, deleted derived data and rebuilt it.
Here's my code
import Razorpay
class controller: RazorpayPaymentCompletionProtocol{
private var razorpay: Razorpay!
override func viewDidLoad() {
super.viewDidLoad()
razorpay = Razorpay.initWithKey("test_key", andDelegate: self)
}
func openRazorPay(){
let options = [
"amount" : "12.00"
]
self.navigationController?.isNavigationBarHidden = true
razorpay.open(options, displayController: self)
}
func onPaymentSuccess(_ payment_id: String) {
print("success")
}
func onPaymentError(_ code: Int32, description str: String) {
print("Failure")
}
}
This framework supports Android, but not for iOS. I want to get the payment flow. If anyone has any solution, share with me.
As I face the same issue......
What I did differently is I take a static amount value 100 and pass the other data as it is.....and I make payment process...it works...open the payment window.
If your amount value less than 100 then it happens...as per doc, the amount we are assigning in the param consider it as a paisa....Try with this one and you will get expected result..
Here what the amount you are passing, make a multiply with 100 e.g 1 x 100. The internal processing will be count it as INR 1, but their calculation is consider it as paisa. So you passed value 100 means 100 paisa and in payment window it will show INR 1.
I was facing the same issue And All of the parameters which i was sending to the options was right.
But the issue was when we try to open RazorPay screen for payment was getting the issue of the RazorpayCheckoutVC.swift deinitialized
In my case my parent viewController was presented so i just changed the navigation code of present to the push and then it worked.
So as per my understanding it should be as
NavigationController -> Push ViewController -> RazorPaycheckoutVC.

Using CocoaAsyncSocket to receive UDP in Swift iOS

I am trying to receive broadcast UDP packets on iOS. I've verified that the packets are being sent (I can see them in another iOS app on the same device).
CocoaAsyncSocket seemed like the most straightforward library to do this with. I added the framework to my project using Carthage. Following the example in Objective-C, I have an iOS app with not much else in it, and this view being loaded:
import UIKit
import CocoaAsyncSocket
class FirstViewController: UIViewController
var sock:AsyncUdpSocket?
override func viewDidLoad() {
super.viewDidLoad()
if (sock == nil){
sock = AsyncUdpSocket(delegate: self)
}
do{
try sock!.bindToPort(54545)
try sock!.enableBroadcast(true) // Also tried without this line
sock!.receiveWithTimeout(10,tag: 0)
} catch {
print("error")
}
print("View Loaded...")
}
func onUdpSocket(cbsock:AsyncUdpSocket!,
didReceiveData data: NSData!){
print("Recv...")
print(data)
cbsock.receiveWithTimeout(10, tag: 0)
}
}
In either the Simulator, or on a device, in the debug console, I see the "View loaded..." message, but nothing from the callback, neither the "Recv..." or the data. It seems the callback isn't being called, but I am not getting any errors either. I've tried other timeouts, and various tweaks like making functions and classes public, but no luck.
I've written a receiver in Python, but this has me stumped.
EDIT: sock now used as a property at paulw's suggestion

Check if a function is available in Swift?

I would like to detect if the user has enabled Reduce Transparency. It's simple you just call the func UIAccessibilityIsReduceMotionEnabled() and it returns a Bool. But my app targets iOS 7 and 8 and this function isn't available on iOS 7.
In Objective-C, this is how I checked to see if that function exists:
if (UIAccessibilityIsReduceMotionEnabled != NULL) { }
In Swift, I can't figure out how to check if it exists or not. According to this answer, you can simply use optional chaining and if it's nil then it doesn't exist, but that is restricted to Obj-C protocols apparently. Xcode 6.1 doesn't like this:
let reduceMotionDetectionIsAvailable = UIAccessibilityIsReduceMotionEnabled?()
It wants you to remove the ?. And of course if you do so it will crash on iOS 7 because that function doesn't exist.
What is the proper way to check if these types of functions exist?
A proper check for availability has been added in Swift 2. This is recommended over other options mentioned here.
var shouldApplyMotionEffects = true
if #available(iOS 8.0, *) {
shouldApplyMotionEffects = !UIAccessibilityIsReduceMotionEnabled()
}
If you're okay with being a little bit cheeky, you can always open the UIKit binary using the library loader and see if it can resolve the symbol:
let uikitbundle = NSBundle(forClass: UIView.self)
let uikit = dlopen(uikitbundle.executablePath!, RTLD_LAZY)
let handle = dlsym(uikit, "UIAccessibilityIsReduceMotionEnabled")
if handle == nil {
println("Not available!")
} else {
println("Available!")
}
The dlopen and dlsym calls can be kinda expensive though so I would recommend keeping the dlopen handle open for the life of the application and storing somewhere the result of trying to dlsym. If you don't, make sure you dlclose it.
As far as I know this is AppStore safe, since UIAccessibilityIsReduceMotionEnabled is a public API.
You could check to see if you're running in iOS 8 or higher --
var reduceMotionEnabled = false
if NSProcessInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion(majorVersion: 8, minorVersion: 0, patchVersion: 0)) {
reduceMotionEnabled = UIAccessibilityIsReduceMotionEnabled()
}
I don't think there's another way to tell. So in theory, if you were able to check, trying to access the function name without the () would give you nil in iOS 7 and the () -> Bool function in iOS 8. However, in order for that to happen, UIAccessibilityIsReduceMotionEnabled would need to be defined as (() -> Bool)?, which it isn't. Testing it out yields a function instance in both versions of iOS that crashes if called in iOS 7:
let reduceMotionDetectionIsAvailable = UIAccessibilityIsReduceMotionEnabled
// reduceMotionDetectionIsAvailable is now a () -> Bool
reduceMotionDetectionIsAvailable()
// crashes in iOS7, fine in iOS8
The only way I can see to do it without testing the version is simply to define your own C function to check in your bridging header file, and call that:
// ObjC
static inline BOOL reduceMotionDetectionIsAvailable() {
return (UIAccessibilityIsReduceMotionEnabled != NULL);
}
// Swift
var reduceMotionEnabled = false
if reduceMotionDetectionIsAvailable() {
reduceMotionEnabled = UIAccessibilityIsReduceMotionEnabled()
}
From the Apple Developer docs (Using Swift with Cocoa and Objective-C (Swift 3) > Interoperability > Adopting Cocoa Design Patterns > API Availability):
Swift code can use the availability of APIs as a condition at
run-time. Availability checks can be used in place of a condition in a
control flow statement, such as an if, guard, or while
statement.
Taking the previous example, you can check availability in an if
statement to call requestWhenInUseAuthorization() only if the method
is available at runtime:
let locationManager = CLLocationManager()
if #available(iOS 8.0, macOS 10.10, *) {
locationManager.requestWhenInUseAuthorization()
}
Alternatively, you can check availability in a guard statement,
which exits out of scope unless the current target satisfies the
specified requirements. This approach simplifies the logic of handling
different platform capabilities.
let locationManager = CLLocationManager()
guard #available(iOS 8.0, macOS 10.10, *) else { return }
locationManager.requestWhenInUseAuthorization()
Each platform argument consists of one of platform names listed below,
followed by corresponding version number. The last argument is an
asterisk (*), which is used to handle potential future platforms.
Platform Names:
iOS
iOSApplicationExtension
macOS
macOSApplicationExtension
watchOS
watchOSApplicationExtension
tvOS
tvOSApplicationExtension

Resources