iOS pass parameter swift 3 - swift3

Hi I want to simplify my code so I put in following way
private func heightChangedHandler(query: HKObserverQuery!, completionHandler: HKObserverQueryCompletionHandler!, error: Error!) {
// Here you need to call a function to query the height change
DispatchQueue.main.async {
let sampleType = self.setSampleType(identifier: HKQuantityTypeIdentifier.height)
self.getData(sampleType: sampleType, category: "Height")
}
completionHandler()
}
//This function will call queryAndObserve function
func startObservingHeightChanges() {
self.queryAndObserve(category: "Height", identifier: HKQuantityTypeIdentifier.height, categoryUpdateHandler: self.heightChangedHandler ,frequency: HKUpdateFrequency.immediate)
}
So in queryAndObserve function
private func queryAndObserve(category:String,identifier:HKQuantityTypeIdentifier, categoryUpdateHandler: ????,frequency:HKUpdateFrequency){
}
My problem here is I don't know what data type should I give for categoryUpdateHandler in observeAndQuery(). Initially I give AnyObject, it compiles good but crash during runtime.
Log is
Could not cast value of type '_SwiftValue' (0x1015fc598) to '(__ObjC.HKObserverQuery, () -> (), Swift.Optional) -> ()' (0x1015fc560).
2017-12-15 14:55:09.235334+0530 App name[886:238336] Could not cast value of type '_SwiftValue' (0x1015fc598) to '(__ObjC.HKObserverQuery, () -> (), Swift.Optional) -> ()' (0x1015fc560).
Any idea will be help full. Thank you.

Related

How do I save the results of a MKLocalSearch to an Array?

I am experiencing a bit of trouble, while working on my app in SwiftUI.
I want to append relevant data, summarized in an object, to an array and return this.
While returning the array, I could see by debugging, that it is empty. Debugging in the for loop showed me, that location objects are created and appended, but are not being "saved" in the array. The "mapItems" array on the other hand has lots of members.
What am I missing?
Here is the method I came up with:
func searchForLocation(searchTerm: String) -> Array<Location>{
var locations = [Location]
let searchReqeust = MKLocalSearch.Request()
searchRequest.region = region //region is a published variable and is determined before
searchRequest.naturalLanguageQuery = searchTerm
let search = MKLocalSearch(request: searchRequest)
search.start{response, error in
//error handling
.
.
.
//empty response.mapItems handling
.
.
.
for item in response!mapItems{
let location = createLocationFromItem(item: item)
locations.append(location)
}
}
return locations
}
My locations class if following:
class Location: Identifiable{
var id= UUID()
var coordinates: CLLocationCoordinate2d
//And its proper init method
}
Your searchForLocation has an asynchronous function inside (search.start{...}),
and your code returns before it has finished getting the results.
To "wait" for the results use a completion/closure handler,
something like this:
func searchForLocation(searchTerm: String, completion: #escaping ([Location]) -> ()) {
var locations = [Location]() // <-- here
// ....
search.start{response, error in // <-- here asynchronous function
//... todo deal with errors, eg return completion([])
for item in response!mapItems {
let location = createLocationFromItem(item: item)
locations.append(location)
}
completion(locations) // <- here return when finished
}
}
and call the function like this:
searchForLocation(searchTerm: "Tokyo") { results in
print("\(results)") // <-- here results available, not before
}
I suggest you read-up on how to create and use asynchronous functions, these are important concepts to master to code effectively in Swift.

Swift 2 to 3 conversion error with Value of type '[Any]' has no member

I'm having a problem with a Swift 2 to 3 conversion piece of work and some of the remains syntax giving: Value of type '[Any]' has no member errors.
I was hoping someone could point me at a good solution.
Swift 2 code
Swift 2 code
func search() {
epsonPrinters = [Printer]()
starPrinters = [Printer]()
epson_startSearching()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { [unowned self] in
let devices = SMPort.searchPrinter()
self.starPrinters = devices.map { portInfo -> Printer in
let p = Printer(
id: portInfo.modelName,
make: "Star Micronics",
model: portInfo.modelName,
portName: portInfo.portName)
if let name = portInfo.modelName as? String {
p.emulation = name.containsString("TSP143") ? "StarGraphics" : "StarLine"
}
return p
}
}
}
Swift 3 Code (I've added comments above areas with errors)
func search() {
epsonPrinters = [Printer]()
starPrinters = [Printer]()
epson_startSearching()
DispatchQueue.global(qos: .background).async { [unowned self] in
let devices = SMPort.searchPrinter()
self.starPrinters = [devices.map { portInfo -> Printer in
// id, model and portName in below fails with messages like:
// Value of type '[Any]' has no member 'modelName'
let p = Printer(
id: portInfo.modelName,
make: "Star Micronics",
model: portInfo.modelName,
portName: portInfo.portName)
// error on portInfo.modelName
// Value of type '[Any]' has no member 'modelName'
if let name = portInfo.modelName as? String {
p.emulation = name.containsString("TSP143") ? "StarGraphics" : "StarLine"
}
return p
}!]
}
}
I know that I can replace the 'id:...' part with the likes of:
id: ((portInfo[0] as AnyObject).modelName) ?? "",
But this isn't correct because PortInfo can have none, 1 or multiples depending on the number of printers we find.
I'd appreciate any suggestions for refactoring this in an elegant way that is good Swift 3 syntax and likely to survive into Swift 4.
I'm working in Xcode 8.3.2
When you get some errors about types, you'd better check what type each variable has. When you select devices in the line let devices = ..., Quick Help of Xcode will show you something like this:
Declaration let devices: [Any]?
First, it's an Optional and you need to unwrap it, before using the actual content.
Second, the type of the elements in devices is Any, to which you cannot apply any methods (including property accessors). You need to cast it to an appropriate type at an appropriate place.
To solve the two things above, you can write something like this:
guard let devices = SMPort.searchPrinter() as? [PortInfo] else {
fatalError("This may never happen")
}
With guard statement above, Quick Help will show you:
Declaration let devices: [PortInfo]
Non-Optional, simple Array of PortInfo, so you can use any methods of PortInfo for the elements of this devices.
I would translate your Swift 2 code into Swift 3 like this:
func search() {
epsonPrinters = []
starPrinters = []
epson_startSearching()
DispatchQueue.global(qos: .default).async {
guard let devices = SMPort.searchPrinter() as? [PortInfo] else {
fatalError("This may never happen")
}
self.starPrinters = devices.map { portInfo -> Printer in
let p = Printer(
id: portInfo.modelName,
make: "Star Micronics",
model: portInfo.modelName,
portName: portInfo.portName)
if let name = portInfo.modelName {
p.emulation = name.contains("TSP143") ? "StarGraphics" : "StarLine"
}
return p
}
}
}
You may need some fixes (not many, I believe) to use this code, as you are not showing all relevant things such as the definition of Printer.

respondToSelector / performSelector with parameter from a string in Swift 3

I have to call method of a class. This method potentially exists or not.
I'm looking for a solution from 2 hours.
The following works without parameter sayHello()
I have the class :
class myClass: NSObject {
func say(something:String){
print("I say : \(something)")
}
func sayHello(){
print("Hello")
}
}
1st step : Create a selector from a string with a parameter
let selector = NSSelectorFromString("sayHello" )
let selectorWithParam = NSSelectorFromString("say:" )
2nd step : Test if the method exist
self.bot?.responds(to: selector)// true
self.bot?.responds(to: selectorWithParam) // false
it doesn't work with a parameter!
Beside, i tried with the Swift3 #selector , but I found no way to enter a method from a string
The Swift method
func say(something:String)
is mapped to Objective-C as
- (void)sayWithSomething:(NSString * _Nonnull)something;
and therefore the correct selector would be
let selectorWithParam = NSSelectorFromString("sayWithSomething:" )
Using #selector is less error-prone:
let selectorWithParam = #selector(myClass.say(something:))

Convert NSUUID to UnsafePointer<UInt8>

Following the update to Swift 3, it appears both getUUIDBytes and getBytes are not available on the UUID object.
let uuid = UIDevice.current.identifierForVendor
let mutableUUIDData = NSMutableData(length:16)
uuid.getBytes(UnsafeMutablePointer(mutableUUIDData!.mutableBytes))
// ^^^ compiler error, value of type UUID? has no member getBytes
I get this error even when getBytes is listed as a method on UUID in the documentation: https://developer.apple.com/reference/foundation/nsuuid/1411420-getbytes
One right way:
let uuid = UIDevice.current.identifierForVendor!
var rawUuid = uuid.uuid
withUnsafePointer(to: &rawUuid) {rawUuidPtr in //<- `rawUuidPtr` is of type `UnsafePointer<uuid_t>`.
rawUuidPtr.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<uuid_t>.size) {bytes in
//Use `bytes` only in this closure. (Do NEVER export `bytes` out of the closure.)
print(bytes[0],bytes[1])
//...
}
}
Another right way:
withUnsafePointer(to: &rawUuid) {rawUuidPtr in //<- `rawUuidPtr` is of type `UnsafePointer<uuid_t>`.
let bytes = UnsafeRawPointer(rawUuidPtr).assumingMemoryBound(to: UInt8.self)
//Use `bytes` only in this closure. (Do NEVER export `bytes` out of the closure.)
print(bytes[0],bytes[1])
//...
}
As already commented by Rob, exporting the pointer passed to the closure argument of withUnsafeBytes is completely NOT guaranteed. A slight change of the context (32-bit/64-bit, x86/ARM, Debug/Release, adding seemingly unrelated code...) would make your app a crasher.
And one more important thing is that UTF-8 Data of the uuidString and the byte sequence of NSUUID.getBytes are completely different:
let nsUuid = uuid as NSUUID //<-Using the same `UUID`
let mutableUUIDData = NSMutableData(length:16)!
nsUuid.getBytes(mutableUUIDData.mutableBytes.assumingMemoryBound(to: UInt8.self))
print(mutableUUIDData) //-><1682ed24 09224178 a279b44b 5a4944f4>
let uuidData = uuid.uuidString.data(using: .utf8)!
print(uuidData as NSData) //-><31363832 45443234 2d303932 322d3431 37382d41 3237392d 42343442 35413439 34344634>
You are thinking too complicated:
func getUUID ( ) -> Data {
let uuid = NSUUID()
var bytes = [UInt8](repeating: 0, count: 16)
uuid.getBytes(&bytes)
return Data(bytes: bytes)
}
Why does that work?
Consider you have:
func printInt(atAddress p: UnsafeMutablePointer<Int>) {
print(p.pointee)
}
then you can in fact do this:
var value: Int = 23
printInt(atAddress: &value)
// Prints "23"
but you can also do this:
var numbers = [5, 10, 15, 20]
printInt(atAddress: &numbers)
// Prints "5"
It's a form of "implicit bridging". To quote from Swiftdoc.org:
A mutable pointer to the elements of an array is implicitly created
when you pass the array using inout syntax.
This implicit bridging only guarantees valid pointers until the current function returns. Such pointers must never "escape" the current function context, but using them as an inout argument is always safe, as inout arguments were always only guarantee to be valid until the called function returns and the called function must return prior to the current one, so this cannot go wrong.
And for those that don't know, casting UUID to NSUUID (... as NSUUID) and the other way round (... as UUID) is guaranteed to always succeed. But if you insist on using UUID, the easiest way is:
private
func getUUID ( ) -> Data {
var uuid = UUID().uuid
return withUnsafePointer(to: &uuid) {
return Data(bytes: $0, count: MemoryLayout.size(ofValue: uuid))
}
}

iOS 8 prepare for segue Casting object. Gives Error

So my code below shows me how I am doing prepare for segue. I'm not sure whats going on but its giving me some weird error I've used this same way before and it works but it just won't work in this application.
Code
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!)
{
if segue.identifier == "select"
{
println("helloworld")
var index = tableView.indexPathForCell(sender as UITableViewCell!)
var object = self.objects[index!.row] as HMAccessory // object is a NSmutableArray its crashing on this next line where im casting it
//var dest = segue!.destinationViewController? as CharacristicsViewController!
// dest.detailItem = object
}
}
can anybody see anything wrong with this?
This is the error I'm getting.
HomeKit`HomeKit.AccessoryTableViewController.prepareForSegue
(HomeKit.AccessoryTableViewController)(Swift.ImplicitlyUnwrappedOptional,
sender : Swift.ImplicitlyUnwrappedOptional) -> () at
AccessoryTableViewController.swift:23: