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

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.

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.

How to write custom ppx decorator to rescript?

I need to generate a value with a different type from my passed type. This is the first time I write on ocaml-like, and for example, in a familiar me haskell I would use Data.Generics.
How I have understood I need to use decorator and ppx. I wrote simple example
let recordHandler = (loc: Location.t, _recFlag: rec_flag, _t: type_declaration, fields: list(label_declaration)) => {
let (module Builder) = Ast_builder.make(loc);
let test = [%str
let schema: Schema = { name: "", _type: String, properties: [] }
]
let moduleExpr = Builder.pmod_structure(test);
[%str
module S = [%m moduleExpr]
]
}
let str_gen = (~loc, ~path as _, (_rec: rec_flag, t: list(type_declaration))) => {
let t = List.hd(t)
switch t.ptype_kind {
| Ptype_record(fields) => recordHandler(loc, _rec, t, fields);
| _ => Location.raise_errorf(~loc, "schema is used only for records.");
};
};
let name = "my_schema";
let () = {
let str_type_decl = Deriving.Generator.make_noarg(str_gen);
Deriving.add(name, ~str_type_decl) |> Deriving.ignore;
};
And
open Ppxlib;
let _ = Driver.run_as_ppx_rewriter()
But in using in rescript code
module User = {
#deriving(my_schema)
type my_typ = {
foo: int,
};
};
I caught:
schema is not supported
. And I made myself sure me to connect it right when I had changed #deriving(my_schema) for #deriving(abcd) and #deriving(sschema).
I got different error
Ppxlib.Deriving: 'abcd' is not a supported type deriving generator.
And my last experiment was to copy past existing library deriving accessors .
ppx_accessor
I copied-pasted it and renamed for accessors_2. And I got same error such as experiment.
accessors_2 is not supported
Also I haven't found examples "ppx rescript". Can you please help me.
What am I doing wrong (ALL , I know)
I have found answer in the article
Dropping support for custom PPXes such as ppx_deriving (the deriving
attribute is now exclusively interpreted as bs.deriving)

cast from float to nsdecimalnumber always fails swift

I am reading my data through a web service that returns a percentage as a string and I need to format it properly in my app. Currently I receive an error message that reads "Argument labels '(_:)' do not match any available overloads".
Suggestions on how to resolve this issue?
if let dUnInsured = result[0]["UnInsured"] as? String, let doubleNum = Double(dUnInsured) {
let sUnInsured = dollarFormatter.string(from: (NSDecimalNumber(Decimal(doubleNum))))!
self.inUninsured.text = sUnInsured
}
Try like this way.
if let dUnInsured = result[0]["UnInsured"] as? String, let doubleNum = Double(dUnInsured) {
let sUnInsured = dollarFormatter.string(from: (NSNumber(value: doubleNum)))!
self.inUninsured.text = sUnInsured
}

Force NSTextField to Only Accept Decimal (#.#) Numbers and Periods

I'm trying to make it so an NSTextField will only accept numbers and periods like 12.4 and 3.6 in a Mac app.
I feel like I'm getting pretty close after reviewing other SO questions, but I can't quite get it. The below code works except that it won't allow . characters. It returns true and doesn't beep at me when I type a . but it won't let the character appear in the field.
class decimalFormatter: NumberFormatter {
override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
//Allows the text to be deleted
if partialString.isEmpty {
return true
}
//Check for #.# numbers
let charSet = NSCharacterSet(charactersIn: "1234567890.").inverted
if partialString.rangeOfCharacter(from: charSet) != nil{
NSBeep()
return false
}else{
return true
}
}
}
Any idea what I'm doing wrong?
I found a simpler way to do it. Inside controlTextDidChange I just did this:
let charSet = NSCharacterSet(charactersIn: "1234567890.").inverted
let chars = fieldDuration.stringValue.components(separatedBy: charSet)
fieldDuration.stringValue = chars.joined()
It works great!
#Clifton Labrum solution is really great but it doesn't reduce the field to Decimal (#.#), you can stil put some inputs as 1.2.4 which would lead to an error when trying tu cast it to Float.
Here is a draft of an extension that worked fine for me ( In Swift 4 )
public override func controlTextDidChange(_ obj: Notification) {
if let textfield = obj.object as? NSTextField,
textfield == self.quantityTextField {
var stringValue = textfield.stringValue
// First step : Only '1234567890.' - #Clifton Labrum solution
let charSet = NSCharacterSet(charactersIn: "1234567890.").inverted
let chars = stringValue.components(separatedBy: charSet)
stringValue = chars.joined()
// Second step : only one '.'
let comma = NSCharacterSet(charactersIn: ".")
let chuncks = stringValue.components(separatedBy: comma as CharacterSet)
switch chuncks.count {
case 0:
stringValue = ""
case 1:
stringValue = "\(chuncks[0])"
default:
stringValue = "\(chuncks[0]).\(chuncks[1])"
}
// replace string
textfield.stringValue = stringValue
}
}
This prevent multiple occurences of . , even if I know that's not the best algorithmic way to do this. For instance 1.2.4 becomes 1.2 when pasted, and by keyboard you can't add another .

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: