Swift: conforming to properties in protocol? - swift

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?
}

Related

Swift associated types vs generic interface

I started learning swift and just learned about associated types and opaque types.
Take a look at the following example -
protocol Appendable {
associatedtype Item
var collection: [Item] { get set }
func Append(_ item: Item)
}
For which an implementation would be -
struct IntArray: Appendable {
typealias Item = Int
var collection: [Int] = []
func append(_ item: Int) {
collection.append(item)
}
}
I understand that any class that conforms to Appendable could declare it’s own type for Item and use that type, thus making the Appendable protocol more generic.
My question is, if and how is this any different from a generic interface in, let’s say, C#? (I come from a C# background). Also, if it is different, is there any reason that they chose to implement generic protocols that specific way?
Like so -
public interface IAppendable<Item> {
List<Item> Collection { get; }
void Append(Item item);
}
For which an implementation would look like this -
public class IntList: IAppendable<int> {
private List<int> _list = new List<int>();
public List<int> Collection => _list;
public void Append(int item) {
_list.Add(item);
}
}

how to create variable property in protocol where as {get set} for one class and {get} for another class

protocol SomeProtocol {
var mustBeSettable: String { get set }
}
class Stage1: SomeProtocol {
//Here "mustBeSettable" should be {get set}
}
class Stage2: SomeProtocol {
//Here "mustBeSettable" should be {get} only
}
in Stage1 class I need access for "mustBeSettable" as {get set}, where as in Stage2 class "mustBeSettable" should be {get} only. but same property I need to use in both classes.
The possible solution is to do it in reverse order, make originally read-only at protocol level (otherwise it would not be possible to fulfil protocol requirements):
protocol SomeProtocol {
var mustBeSettable: String { get }
}
class Stage1: SomeProtocol {
var mustBeSettable: String // read-write
init(_ value: String) {
mustBeSettable = value
}
}
class Stage2: SomeProtocol {
let mustBeSettable: String // read-only
init(_ value: String) {
mustBeSettable = value
}
}

Pass enum case as parameter in swift 4

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)

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.

public OptionSet with private constructor

I have a sample code:
public struct MyOptions: OptionSet {
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
public static let one = MyOptions(rawValue: 1 << 0)
public static let two = MyOptions(rawValue: 1 << 1)
}
In other module I can do:
print(MyOptions.one)
print(MyOptions(rawValue: 10))
How can I do public struct with private constructor and public static properties(like a one, two) to limit manual creation?
You can't. When you conform a type to a protocol, all the required stubs' protection levels must be at least equal to the type's protection level. I'll try to explain why.
Let's say I have a type Foo that I conform to Hashable. I then assign an instance as a Hashable type:
let foo: Hashable = Foo()
Because the instance is of type Hashable, I am guaranteed to have access to the hash(into:) method. But what if I make the method private? At that point you end up with unexpected behavior. Either for some reason I can't access a method that I was guaranteed access to, or I have access to a method that I shouldn't be able to reach. It's a conflict of access levels.
So yeah, what you want to do isn't possible.