Pass enum case as parameter in swift 4 - swift

So I´m trying to build a framework with a function like this
public func logScreen(screen: SomeEnum){
print(screen.rawValue)
}
and i want to pass as parameter a case of a string enum that is defined in the project that will make use of this framework

I don't think you can do this directly. But using swift protocol, you can achieve your goal I believe. You may review my idea.
First, you need to define a protocol in your framework. Say in you Framework class.
public protocol SomeEnum {
var rawValue: String { get }
}
Inside your framework class, you define your code:
public func logScreen(screen: SomeEnum){
print(screen.rawValue)
}
Now in your project, you can declare the enum and inherite the framework
protocol. Say for example in your project:
public enum MyEnum: SomeEnum {
case test
case debug
public var rawValue: String {
switch self {
case .test:
return "test"
case .debug:
return "Debug"
}
}
}
Now use the method from your project like below:
yourframeworkclass.logScreen(screen: MyEnum.test)

Related

How do I access a Swift String enum in Objective-C?

I've got the following swift autogen model, that i can't change
#objcMembers public class Device: NSObject, Codable {
public enum Auth: String, Codable {
case enabled = "ENABLED"
case supported = "SUPPORTED"
case notSupported = "NOT_SUPPORTED"
case bypassed = "BYPASSED"
}
public var auth: Auth
public var brand: String
}
I'm trying to access the enum property in my Objective-C class like this
self.isAuthEnabled = ([Device.auth.rawValue isEqualToString:#"ENABLED"]);
but I keep getting this error, Property 'auth' not found on object of type 'Device' even though I have access to the brand property, I can't access the enum

Why does accessing a String parameter of a subclassed NSManagedObject with a parent relationship crash?

I have generated classes for two core data entities. The first is called Address and is an abstract entity. The second is called Person, and it inherits from Address. I've added a few example managed attributes for the purpose of this test. And i've added a non-managed String property to the Person class. Accessing the string property of the Person class will crash. Why does this crash?
The Address and Person classes are automatically generated by Xcode, with the exception of the extra parameter: let foo = "Foo"
If i modify the code to make Person inherit from NSManagedObject directly instead of Address, then the code works and doesn't crash.
Automatically generated Address class:
#objc(Address)
public class Address: NSManagedObject {
}
extension Address {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Address> {
return NSFetchRequest<Address>(entityName: "Address")
}
#NSManaged public var street: String?
#NSManaged public var city: String?
}
Automatically generated person class with the exception of the "foo" parameter:
#objc(Person)
public class Person: Address {
public let foo = "Foo" //added this parameter
}
extension Person {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Person> {
return NSFetchRequest<Person>(entityName: "Person")
}
#NSManaged public var name: String?
}
problem code
let person = Person(context: context)
print(person.foo) //doesn't crash, but prints empty line instead of value
print("VALUE:\(person.foo):") //crashes with Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)
UPDATE:
if foo is defined as
public let foo: String? = "Foo"
then the print statements don't crash, instead they interpret the value as 'nil' and print that.
So my question becomes: Why is this value which is assigned as a constant being reset to nil under the covers?
I have two hand-waving explanations why you are getting nil:
Managed objects don't function very well until they are inserted.
Your foo is a what I would call a constant stored property. I made up the name because, red flag, I cannot find any examples of it in the Swift book chapter on Properties
Put these two together and you get an edge case that doesn't work.
That being said, I'm kind of surprised that your foo setting does not work, because foo is not a managed property (that is, it is not in the data model). If I make such a constant stored property in a regular, non-managed object…
public class Animal {
public let foo: String! = "Foo"
}
it reads back later as expected.
So, if you can accept that this edge case just doesn't work in Core Data, you can move on to several more normal ways that do work.
One way is to declare foo as a var and assign a value to in awakeFromInsert() which is, as I alluded to earlier, after insertion. In Core Data, awakeFromInsert() is one of your friends…
#objc(Person)
public class Person: Address {
public var foo: String!
override public func awakeFromInsert() {
foo = "Foo"
}
}
Another way that works is as a computed property…
#objc(Person)
public class Person: Address {
public var foo : String { return "Foo" }
}
And, finally, the most logical way, since foo is constant for all instances, is to make it a type property…
#objc(Person)
public class Person: Address {
static var foo: String = "Foo"
}
but of course if you do this you must reference it as Person.foo instead of person.foo.

Require associatedtype to be representable in a #convention(c) block

I want to have a generic way of doing something like in Swift 3:
public protocol Callable {
associatedtype In : CVarArg
associatedtype Out : CVarArg
}
public struct IntCallable : Callable {
public typealias In = Int
public typealias Out = Double
public typealias FunctionalBlock = #convention(c) (In) -> Out
public func call(_ block: FunctionalBlock) { /* do stuff */ }
}
So I'd like it to look more like this:
public protocol Callable {
associatedtype In : CVarArg
associatedtype Out : CVarArg
typealias FunctionalBlock = #convention(c) (In) -> Out
}
public struct IntCallable : Callable {
public typealias In = Int
public typealias Out = Double
}
public extension Callable {
public func call(_ block: FunctionalBlock) { /* do stuff */ }
}
However, I get the error:
'(Self.In) -> Self.Out' is not representable in Objective-C, so it cannot be used with '#convention(c)'
Is there any constraint I can place on the In/Out associatedtypes that will allow my to declare the generic form of the FunctionalBlock? It works fine without #convention(c), but I need it in order to form a C function call.
This is not currently possible in Swift, due to the how Swift manages values passed as protocols, and CVarArg is a protocol.
What happens behind the scenes is that when passing a value under the umbrella of a protocol, the Swift compiler creates an existential container that wraps the value, a value which is transparently unwrapped at the callee site.
So basically your block actually looks something like this:
typealias FunctionalBlock = #convention(c) (Container<In>) -> Container<Out>
Due to this behind-the-scenes transform, you're not passing values that can be represented in C, thus the error you get.
This is very similar to other protocol related issues, like the famous Protocol doesn't conform to itself?.
Your best bet would be to add overloads for all types that conform to CVarArg, since this is a finite and unchangeable list.

Swift: conforming to properties in protocol?

How to confirm to protocols that declares properties of other protocols in Swift?
There is a protocol GKGameModel in which its implementers need to have a properties conforming to a protocol
public protocol GKGameModel {
// ...
public var players: [GKGameModelPlayer]? { get }
public var activePlayer: GKGameModelPlayer? { get }
// ...
}
public protocol GKGameModelPlayer {
// ...
}
Now, suppose I have a class Player and GameModel that conforms to the above protocols
class Player : NSObject, GKGameModelPlayer {
//...
}
class GameModel : NSObject, GKGameModel {
//...
public var players: [Player]?
public var activePlayer: Player?
}
Now the above code doesn't compile and the error messages (among others) were:
protocol requires property 'activePlayer' with type 'GKGameModelPlayer?'; do you want to add a stub?
candidate has non-matching type 'Player?'
However the Player class conforms to protocol GKGameModelPlayer, hence it should confirm just fine. How can I get this to compile?
Strangely Objective-C deals with this just fine – take a look at the FourInARow example code which does something like this.
The protocol requires that the properties be typed exactly as shown. In other words, an array of GKGameModelPlayers and a single optional GKGameModelPlayer?. If your Player type conforms to the protocol, then an array of Players can be passed to the protocol property if casted / typed as [GKGameModelPlayer].
But the requirement here is not, for example, an activePlayer property that has a type that conforms to GKGameModelPlayer, but rather an activePlayer property that references an instance that it typed as / cast as a GKGameModelPlayer.
I.e. this would fix the error:
class GameModel : NSObject, GKGameModel {
//...
public var players: [GKGameModelPlayer]?
public var activePlayer: GKGameModelPlayer?
}
players and activePlayer property has a type that conforms to GKGameModelPlayer. So just change it to GKGameModelPlayer type instead of Player
class GameModel : NSObject, GKGameModel {
//...
public var players: [GKGameModelPlayer]?
public var activePlayer: GKGameModelPlayer?
}

Swift Enum with multiple types

I am trying to figure out how to duplicate my Java Enum into Swift and i don't know if
this is the right way.
My Enum in Java which i am trying to write in Swift:
public enum EnumDB {
DATABASE_NAME("DataBase"),
DATABASE_VERSION(1);
private String name;
private int value;
private EnumDB(String name) {
this.name = name;
}
private EnumDB(int value) {
this.value = value;
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
}
My Swift Code:
enum EnumDB {
case Name,Version
func getName() -> String{
switch self{
case .Name: return "DataBase"
}
}
func getNumber() -> Int{
switch self{
case .Version: return 1
default: return 0
}
}
}
My questions are:
Is this the right way to create an Enum with multiple value types , each enum contains
a different type?
unfortunately this way i can call the methods getName() and getNumber() on each Enum which is bad because i would want those methods to be presented according to the enum type.
Enum Associative values and Raw Values didn't help
to conclusion what i am looking for is writing an enum that his values can contains different types.
thanks
You can definitely have an enum with associated values of different types, which I think can give you what you're looking for. This is how I might implement your example:
enum EnumDB {
case Name(String)
case Version(Int)
}
let namedDB = EnumDB.Name("databaseName")
switch namedDB {
case .Name(let name):
println("Database name is \(name)")
case .Version(let versionNumber):
println("Database version is \(versionNumber)")
}