Function with Enumeration Parameter (and Default value) - ios

I wanted to create an extension method function called toCurrencyString that has one parameter that is actually an integer enumeration of type CurrencyFormatType. The enum and code would be:
enum CurrencyFormatType: Int {
/// Formats a standard currency string (localized) such as $45.35 or *45,00
case Standard = 1,
///Rounded currency format (rounds up last decimals and does not return any decimals in string)
Rounded,
///Will not include the thousands separator (, or .) in a string. Retains localized currency symbol.
WithoutThousandsSeparator
}
func toCurrencyString(currencyFormat: CurrencyFormatType = 1) -> String
{
..my switch code w/ default to the first option if nothing passed in
}
I'd like to do this so that I can call .toCurrencyString on NSDecimalNumbers without having to pass in the optional parameter (most of the time I won't need it), but on the few times I do, i'd like to use an enum to select other options.
It complains at me telling me that my enum doesn't conform to IntegerLiteralConvertible. I did a little reading on how this would work but couldn't figure out the code.
Could anyone shed some light on how to get this working?
thanks for your help!

My apologies, no sooner did I ask it than I figured it out. I should have waited a bit longer.
In hopes of helping others out, just a reminder about the 'Raw' aspect of enums:
Adding this to my function signature helped:
CurrencyFormatType.Raw = 1
Thanks!
alternately you could have done:
func toCurrencyString(currencyFormat: CurrencyFormatType = CurrencyFormatType.Standard) -> String
{
...

Related

A few questions about Dart generics and type safety

I have the following Dart 2 code with null-safety.
extension Foo<T> on List<T> {
List<U> bar<U>({
U Function(T)? transform,
}) {
final t = transform ?? _identityTransform;
return map(t).toList();
}
}
U _identityTransform<T, U>(T t) => t as U; // #1, #2
void main() {
final strings = ['a', 'b', 'c'].bar<String>(); // #3
final ints = ['1', '2', '3'].bar(transform: int.parse);
print(strings);
print(ints);
}
It is an extension method on List<T> with a custom method that is basically a map with the
difference that it can return a new list of the same type if no transform is specified. (My real code is more complex than this, but this example is enough to present my questions.)
I want to be able to call bar() on a List with transform or without; if called without it, _identityTransform should be used.
The code above works, but I have a few reservations as to its quality, and questions, as I haven't really come to terms with Dart generics yet:
In the line marked #1 - the _identityTransform takes two generic parameters as I need access to them, but when the function is used the generic types are not used because I don't think it is possible to write something like _identityTransform<T, U> there. Is there a better way of defining _identityTransform? Am I losing any type safety with my current code?
In the line marked #2 I need a cast as U for the code to compile, I haven't managed to make the code work without it. Is there a way to do it without the cast?
In the line marked #3, when I call the extension method without any transform (i.e. I want the identity transform to kick in) I need to explicitly pass the generic type, otherwise the compiler complains about missing generic type (in strong mode) or infers strings to be List<dynamic> (strong mode turned off). Is some generics magic possible to be able to call .bar() and still have strings be inferred to List<String>?
I would make _identityTransform a nested function of bar so that you can remove its type arguments and instead use the same T and U as bar:
extension Foo<T> on List<T> {
List<U> bar<U>({
U Function(T)? transform,
}) {
U _identityTransform(T t) => t as U;
final t = transform ?? _identityTransform;
return map(t).toList();
}
}
Alternatively if you want to explicitly use _identityTransform<T, U>, then you could use a closure: t = transform ?? (arg) => _identityTransform<T, U>(arg), but that seems like overkill.
You need the cast. T and U are independent/unrelated types. Since you don't know that you want T and U to be the same until bar checks its argument at runtime, you will need the explicit cast to satisfy static type checking.
If the caller passes nothing for the transform argument, there is nothing to infer U from, so it will be dynamic. I can't think of any magical way make U default to T in such a case (again, that would be known only at runtime, but generics must satisfy static analysis).

Localizable strings shows key instead of value every other time

I have a weird inconsistency with my localizable strings where sometimes the value is shown and sometimes thee key is shown. It's about every other time I restart the app. I use a lot of localizable strings in the project and they all work fine except for the ones I use in conjunction with this enum, which of course makes me think there is something wrong with it:
enum ParameterDescription {
case parameterDescription(id: Int)
var description: String {
switch self {
case .parameterDescription(let id):
return "parameterDescription_\(id)"
}
}
}
This returns the string I expect, like parameterDescription_0 or parameterDescription_3. And then I hook on localized:
print(ParameterDescription.parameterDescription(id: 0).description.localized())
extension String {
func localized() -> String {
return NSLocalizedString(self, comment: "")
}
}
And sometimes I get the correct associated value, but if I build and run the code again without changing anything, I (might) get the key printed as a string instead, which I guess means that Xcode couldn't find the string.
Is there something wrong with the enum or some reason it can't be used like this? Or is it something else? I have checked through the project folders so that there aren't any conflicting files, and as I previously said, I only experience this problem using this enum and/or this key. And there are thousands of localized strings in the project.
I have checked similar threads, but there doesn't seem to be any answer that is relevant for this.

Xcode 8 :function types cannot have argument label breaking my build

It seems that for some reason Swift have chosen to make coding in it less readable by forcing users to remove completion handler parameter labels. I have read the Swift discussion and still think it's a mistake. At least they could have made it optional.
When building using Xcode 8 - is there a way to force the compiler to use Swift 2.3 so I don't get these errors anymore?
I have updated the option to use legacy Swift (under build settings)
but I still seem to get this error:
Function types cannot have argument label 'isloggedIn'; use '_'
instead
How can I keep my labels in my completion handlers?
The Swift designers decided to prohibit argument labels for function types.
The reasoning is explained here: https://github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md
This is a frustrating and questionable choice, as prohibiting argument labels makes it much easier to incorrectly invoke closures, which seems more important than simplifying the language's type system.
Usability > ideology.
A workaround to consider. You can't do:
func doStuff(completion: (foo: Int, bar: String) -> Void) {
...
completion(foo: 0, bar: "")
}
... but you can do:
func doStuff(completion: ((foo: Int, bar: String)) -> Void) {
...
completion((foo: 0, bar: ""))
}
i.e. have a single unnamed argument to your closure which is a tuple, in this case (foo: Int, bar: String).
It's ugly in its own way, but at least you retain the argument labels.
Based on the information above - it appears that the only way to really fix this and ensure that its performant is to raise a proposal to
Make argument labels optional with a view to :
improving the speed of development ( without argument labels it requires us to scroll up to the top of the method each time we put in the completion handler.
Reduce Errors : ( I have already had several errors caused due to incorrect completion handler entries especially with those that expect boolean values)
Make code more readable across team members. Not everyone has only one team member and thus being able to easily pick up other peoples code is a must have.
Lastly good programming practice means that the solution should look as much like the actual item being developed. completionhandler: (newvalues, nil) looks less like the item being managed than completionhandler(results: newValue, error:nil)
I would love for people reading this to share their feedback/ comments
on this below before I submit it so I can show there are others that
support this.
Edit:
I have submitted the pitch here :
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161010/028083.html
which appears to have been agreed. It looks like its going to happen, however the discussion is whether this is submitted as a Swift 4 improvement ( highly probable)
You have to use _ to make your parameters unnamed, and that is unfortunate. Instead of tacking _ on to each parameter and then blindly calling your function I would suggest making a wrapper object.
Since losing named parameters for function types introduces more risk that you will call the function with the wrong values, I would suggest wrapping the parameters in a struct and having that be the one and only parameter to your function.
This way the fields of you struct are named, and there is only one type of value to pass into your function. It is more cumbersome than if we were able to name the parameters of the function, but we can't. At least this way you'll be safer and you'll feel less dirty.
struct LineNoteCellState {
var lineNoteText: String?
var printOnInvoice = false
var printOnLabel = false
}
Here is an example of it being used:
cell.configure(editCallback: { (_ state: LineNoteCellState) in
self.lineNoteText = state.lineNoteText
self.printOnInvoice = state.printOnInvoice
self.printOnLabel = state.printOnLabel
})
Semi-workaround, note the _
completion: (_ success: Bool) -> Void

What is the difference between '(String)' and 'String' in a Swift error message

I wrote a function that takes a non-optional String as a parameter.
I declared a variable property of type String, which is also not an optional.
When I try to call my function with this property as an argument, I get the following error.
Cannot invoke 'localesForCountryCode' with an argument list of type '(String)'
Notice that the error lists the type as '(String)' not 'String'. What do the parens signify? I thought they meant that the type was optional, but nothing is declared as an optional anywhere.
My Function (An extension of NSLocale):
func localesForCountryCode(countryCode: String) -> [NSLocale] {
let localeIdentifiers = localeIdentifiersForCountryCode(countryCode)
var locales = [NSLocale]()
for localeIdentifier in localeIdentifiers {
let localeForIdentifier = NSLocale(localeIdentifier: localeIdentifier)
locales.append(localeForIdentifier)
}
return locales
}
Code That Calls My Function
let currentCountryCode = "US"
var localesForCurrentCountry = [NSLocale]()
func updateWithNewLocation(newLocation: CLLocation) {
geoCoder.reverseGeocodeLocation(newLocation, completionHandler: { (placemarks, error) -> Void in
if placemarks.count > 0 {
let placemark = placemarks.first as! CLPlacemark
self.currentCountry = placemark.country
self.localesForCurrentCountry = NSLocale.localesForCountryCode(self.currentCountryCode)
}
})
}
Update 1
When I move my function code from the NSLocale extension to the view controller from which I am calling the function, the error goes away. Any ideas why this may be the case? Absolutely no changes to the function were made.
Update 2
I continue to run into this problem. The following is another example. Again, it seems to happen only when a function is called as a class method.
I was rereading your question and realized that your question is not really your question. Your problem has nothing to do with parentheses (see below about why). You're just calling the wrong method. NSDateComponentsFormatter is a class. stringFromTimeInterval() is an instance method. You have to crate an actual date formatter to work with. If you want a default one, you can do this:
return NSDateComponentsFormatter().stringFromTimeInterval(unwrappedTimespan)
Note the extra set of parentheses. Your probably don't want the default one, though. You probably want to configure one. See Date Formatters for an introduction to that topic. Note that date formatters can be pretty expensive to create, so you usually want to reuse them.
You're doing the same thing with localesForCountryCode. This is an instance method, not a class method. You have to create an NSLocale first.
This does open up an interesting topic, though. Why does NSDateComponentsFormatter.stringFromTimeInterval() act like a function that you're just passing the wrong arguments to? Why doesn't it say "hey, you're calling a class method?" It's because it is a function that you're just passing the wrong arguments to.
Every method is really just a curried function that takes the target object as the first parameter. See Ole Begemann's quick intro to the topic: Instance Methods are Curried Functions in Swift.
Some more on your explicit question about parentheses:
As others have noted, a (String) is a one-tuple of String. The important point is that in Swift, any type is trivially convertible to a one-tuple of that type, so the extra parentheses here are unimportant. There is no meaningful distinction between String and (String) in Swift.
All Swift functions technically take one value and return one value. So one can correctly think of:
func f(x: Int, y: Int) -> Int
as a function that takes a tuple (Int,y:Int) and returns an Int (or possibly (Int); I believe Swift actually does the former, though). This is subtly connected to how currying works in Swift. In a simpler case:
func f(x: Int)
This is a function that technically takes (Int) and returns (). That's why sometimes you will see (Type) show up in error messages. But it is not a meaningful distinction.
String - it's simple String type.
But (String) - it's a compound type called Tuple.
That means you passing to you function Tuple, not String.
A compound type is a type without a name, defined in the Swift language itself. There are two compound types: function types and tuple types. A compound type may contain named types and other compound types. For instance, the tuple type (Int, (Int, Int)) contains two elements: The first is the named type Int, and the second is another compound type (Int, Int).
In general the error message says (String) because that is the tuple / list of parameters. If you method would expect a String and afterwards an Int an error message might mention (String, paramName: Int)
So basically so far everything looks fine. You need to show us the code for us to be able to fix your exact problem. Because String and (String) normally should match in the given scenario.
Consider the following example:
func k (a:String, b:Int) {}
k(12, b:"123")
which will cause the error
Cannot invoke 'k' with an argument list of type (Int, b:String)
While
k("12", b:123)
does what you would expect.

Function not working in Swift

So I've been trying to learn a bit of Swift and I've been looking at functions with return types. I added this function, and it should work, but I still get the error "Could not find member 'convertFromStringInterpolationSegment.' I've looked at other stack overflow questions and have tried to apply their answers, but to no avail. Some help would be greatly appreciated.
func getGasPrices2(price1: Double, price2:Double, price3:Double)
{
return "The prices are \(String(price1)), \(String(price2)), \(String(price3))."
}
getGasPrices2(3.59, 3.69, 3.79)
You forgot to declare a return type. Since you want to return a String, the function needs to declare that:
func getGasPrices2(price1: Double, price2:Double, price3:Double) -> (String)
I'm sure that Apple will work on these wonderful error messages. :)
As Alvin stated, you were simply missing the return type
-> (String)
...in the function's declaration.
I find myself doing the same thing, forgetting the return type at the end, as I'm still getting used to writing in Swift while having the function-type-declaration-first of Objective-C habit
...
Basically your function in Swift...
func getGasPrices2(price1: Double, price2:Double, price3:Double) -> (String)
...is equivalent* to
-(NSString*)getGasPrices2: (double)price1 price2:(double)price2 price3:(double)price3
...in Objective-C*
*Strings in Swift aren't exactly the same as NSStrings but both functions would be able to accomplish what you are looking to do; one in Objective-C, the other in Swift (as long as you make sure to actually return a String/NSString at the end :-)

Resources