Swift typealias for enum case - ios

Alright, I've done my homework and read what I could find but I can't seem to find if this is possible to accomplish with Swift. I've got an enum that I use all over the place: SomeEnum and one if it's cases is a lengthy SomeEnum.SomeLengthyCaseName and I'm tired of seeing it all over my code. I don't want to refactor because I like the descriptive nature of the case for when newcomer's maintain my code.
So here's the question: Is it possible to create a typealias for SomeEnum.SomeLengthyCaseName? And if so, how? Here's what I've tried:
enum SomeEnum {
case SomeLengthyCaseName
}
typealias SLCN = SomeEnum.SomeLengthyCaseName
That's the syntax but Xcode gives a compiler error saying that "SomeLenghtyCaseName is not a member of SomeEnum."
Ready, set, go!

That's a misleading error message.
The real problem is that SomeLengthyCaseName is not a type. Therefore you can't use typealias, which is only for aliases of types. (For example, you could say typealias SE = SomeEnum.)
Instead, you can just use a global constant:
let SLCN = SomeEnum.SomeLengthyCaseName
Or, better, a static constant on the enum itself:
enum SomeEnum {
case SomeLengthyCaseName
static let SLCN = SomeEnum.SomeLengthyCaseName
}
let x: SomeEnum = .SLCN

It's not possible since SomeEnum.SomeLengthyCaseName is not a type, it's a value for the SomeEnum type.

Related

Property redefinition extension Swift

I have a project A in which I have defined a struct, that only exists in project A, as follows
public struct Foo: RawRepresentable {
public typealias RawValue = String
public var rawValue: RawValue
public init?(rawValue: RawValue) {
self.rawValue = rawValue
}
public static let bar = Foo(rawValue: "bar")
}
Now, when I create an extension in project A that redefines the bar property, this will generate an error, which is logical.
extension Foo {
// Invalid redeclaration of 'bar'
static let bar = Foo(rawValue: "foo")
}
My real question comes when I define a new project B, that depends on A. I declared the same extension there, but no compiler error was given. Why is that?
import A
extension Foo {
// No error, but shouldn't it give me one?
static let bar = Foo(rawValue: "foo")
}
Well, there's two ways to look at this. One is that it's a bug, the other is that it isn't. :) The canonical report is probably the one at https://bugs.swift.org/browse/SR-3228 [WARNING: that URL may go bad when Apple moves over to GitHub Issues for its Swift bug reporting]. But read also the discussion of the underlying mechanism from Jordan Rose in the comments to https://bugs.swift.org/browse/SR-8010, where it seems that the real issue is how to maintain Objective-C compatibility.
So yes, Apple knows about this, and they know it would be nice to issue a warning about doing something iffy, but you are in fact allowed to create what amounts to a same-named but different method/property in a different module, so on your head be it if you do so.

How to declare variable and enum declaration in one line?

Is there a way to simplify this into one line in Swift 2.0?
enum Direction {
case Up
case Down
}
var panDirection: Direction?
Something like this, which doesn't work:
var panDirection = enum Direction {
case Up
case Down
}
Even if you could do it, those are not the same at all. enum is an object type, like class. In your first example, panDirection is an instance of the Direction enum. In your second example, if it could compile and run, panDirection would end up as the enum itself (the type, not an instance of the type) — which is not at all what you want.
Thus, what you are trying to do is to declare a type in the middle of a line. You can't do that. The rules for where you can declare a type are very clear and very strict.
Note, however, that you can declare a type within another type, or even purely locally, e.g. within a function's code. Thus, for example, you can declare the type temporarily as a way of passing data around inside a function. Nutty but legal:
func myCoolFunction(up:Bool) {
enum Direction : String {
case Up
case Down
}
let dir : Direction = (up ? .Up : .Down)
print("user wants \(dir)")
}
No, an enum type must be declared separately before it can be used as a variable's type.

How to cast an typed array to one of the types protocols?

I have this in my playground:
func foo(printables: [Printable]) {
// do something
}
enum MenuOptions: String, Printable {
case ChangePickup = "Change Pickup Time"
case RequestSupport = "Request Support"
var description: String {
get {
return self.rawValue
}
}
}
var menuOptions: [MenuOptions] = [.ChangePickup]
foo(menuOptions)
let printables = menuOptions as [Printable]
Both last lines give a compiler error. I would expect menuOptions to be implicitly casted to [Printable] but the compiler complains with a [MenuOptions] is not convertible to [Printable] error. Am I missing something?
If I do menuOptions as! AnyObject as! [Printable] the compiler doesn't complain and the code works properly, but this seems dirty. Funnily enough, doing foo(menuOptions.map { $0 }) also works! Is this just a compiler bug?
You are trying to create a generic function constrained to objects that conform to the Printable protocol. Just define it differently:
func foo<T: Printable>(printables: [T]) {
// do something
}
The function will now work in your context. It will take an array of any object type that conforms to Printable.
Here are the relevant docs on Generics, specifically Type Constraints.
As to why this works, but your definition doesn't - this is just the Swift syntax. Printable is a protocol, but T is a type (generic, but still a type). You want your function to accept any type (T) that conforms to a protocol (<T: Protocol>). This is just how the language has been designed.
Using menuOptions as! AnyObject as! [Printable] may work in this example, but it is a bad practice. Using as! is like telling the compiler "trust me, this will work." This is fine in limited cases, but sets you up for trouble in the long run. Using as! turns off the compiler's type checking, which means if there is a problem it will happen at runtime, and your app will crash.
Again, I highly suggest reading the docs on Type Constraints, which I linked above. It explains it in detail.
I found that this is working. It seems that Swift compiler is not that smart.
var menuOptions: [MenuOptions] = [MenuOptions.ChangePickup]
let printables:[Printable] = menuOptions as! [Printable]
foo(printables)
I don't know why these are not working
foo(menuOptions)
foo(menuOptions as! [Printable])

Injecting parameter in Typhoon Swift

I am using the following code to inject a enum parameter in Typhoon in Swift:
public dynamic func introPageViewController() -> AnyObject {
return TyphoonDefinition.withClass(UIPageViewController.self) {
(definition) in
definition.useInitializer("initWithTransitionStyle:navigationOrientation:options:"){
(initializer) in
initializer.injectParameterWith(UIPageViewControllerTransitionStyle.Scroll)
}
}
}
The problem is that injectParameterWith method only accepts parameters of type AnyObject and the parameter I want to inject is of Int type so this code gives a compiler error.
How would I achieve this without causing any compiler error or crash?
As outlined in the Typhoon User Guide here, to inject an enum it is necessary to box it as an NSNumber. I'm actually not sure how to box explicitly in Swift but you could do it with something like:
var num: NSNumber = mode.rawValue
I'm not able to try it now but according to this you should be able to just add import Foundation to the top of the file and it will do an implicit cast to NSNumber from Int

Swift dynamic variable can't be of type Printable

I have a Swift project that contains two UITableViewControllers. The second UITableViewController is linked to a MVC model called Model. According to the UITableViewCell I select in the first UITableViewController, I want to initialize some properties of Model with Ints or Strings. Therefore, I've decided to define those properties with Printable protocol type. In the same time, I want to perform Key Value Observing on one of these properties.
Right now, Model looks like this:
class Model: NSObject {
let title: String
let array: [Printable]
dynamic var selectedValue: Printable //error message
init(title: String, array: [Printable], selectedValue: Printable) {
self.title = title
self.array = array
self.selectedValue = selectedValue
}
}
The problem here is that the following error message appears on the selectedValue declaration line:
Property cannot be marked dynamic because its type cannot be
represented in Objective-C
If I go to the Xcode Issue Navigator, I can also read the following line:
Protocol 'Printable' is not '#objc'
Is there any workaround?
There is no way to do what you want. Non-#objc protocols cannot be represented in Objective-C. One reason is that Non-#objc protocols can represent non-class types (and indeed, you said that you wanted to use it for Int and String, both non-class types), and protocols in Objective-C are only for objects.
KVO is a feature designed for Objective-C, so you must think about what you expect it to see from the perspective of Objective-C. If you were doing this in Objective-C, you would not want to have a property that could either be an object like id or a non-object like int -- you can't even declare that. Instead, as you said in your comment, you probably want it to be just objects. And you want to be able to use Foundation's bridging to turn Int into NSNumber * and String into NSString *. These are regular Cocoa classes that inherit from NSObject, which implements Printable.
So it seems to me you should just use NSObject or NSObjectProtocol.
Unfortunately ObjC does not treat protocols as types, they are just a convenient way of grouping members. Under the covers they are of type Any, so regretfully you will have to make the property Any and cast to Printable.
The best I can thing of is:
dynamic var selectedValue: Any
var printableValue : Printable {
get {
return (Printable)selectedValue
}
set {
selectedValue = newValue
}
}

Resources