Swift default argument and ignore argument in protocol method/function - ios

How can I set a protocol's function so that it can receive an optional argument or even ignore it ?
I have this protocol :
protocol Game {
func modeName(forRound: Int) -> ModeName
}
With these 2 special classes :
//Goal: Default forRound should be 0 if none provided
class OnlineGame : Game {
func modeName(forRound: Int = 0) -> ModeName {
//Some code
}
}
//Goal: I don't care about the forRound value here
class OfflineGame : Game {
func modeName(_ forRound: Int) -> ModeName {
//Some code
}
}

First of all, in the protocol, you are declaring "method", and the first parameter of "method" has no external name by default. So here is the very normal case code:
class SomeGame: Game {
func modeName(forRound: Int) -> ModeName {
// ...
}
}
let game: Game = SomeGame()
let modeName = game.modeName(1) // not `game.modeName(forRound: 1)`
In your OnlineGame case, if the parameter has default value, it has external name automatically even if it's the first parameter of the method. You can override that behavior with _ as explicit external name:
class OnlineGame : Game {
func modeName(_ forRound: Int = 0) -> ModeName {
//Some code
}
}
In your OfflineGame case, you can ignore the parameter with _ as internal name:
class OfflineGame : Game {
func modeName(_: Int) -> ModeName {
//Some code
}
}

Related

How do you write a mock in Swift?

Where do I put the mocked code? Do I need to write everything again? Do I change the original code?
Short answer: using protocols.
If your injectable object is final, a struct, or an enum, it isn't even possible to override it to mock. Instead of using a concrete type as your dependency, use a protocol and conform your implementation to it. In addition to allowing "mocking" regardless of the real type (class, struct, enum), this lists the public interface all in one place, uninterrupted by the implementations. It also forces you to think about what needs to be part of the non-private interface.
With retroactive protocol conformance (i.e. in extensions), you can even use this to mock system classes like, e.g., CBCentralManager or CLLocationManager.
Example:
Not easily mockable:
struct Foo {
let id: Int
let data: Int
}
final class FooManager {
var x: Int
func getFoo(id: Int) -> Foo {
return Foo(id: id, data: x)
}
}
class FooUser {
let fooManager: FooManager
init(fooManager: FooManager) {
self.fooManager = fooManager
}
func getData() -> Int {
return fooManager.getFoo(id: 3).data
}
}
Trivially mockable:
struct Foo {
let id: Int
let data: Int
}
// Easy to see what's visible.
protocol FooManager {
func getFoo(id: Int) -> Foo
}
final class RealFooManager: FooManager {
private var x: Int
func getFoo(id: Int) -> Foo {
return Foo(id: id, data: x)
}
}
class FooUser {
let fooManager: FooManager
init(fooManager: FooManager) {
self.fooManager = fooManager
}
func getData() -> Int {
return fooManager.getFoo(id: 3).data
}
}
// In test target.
class MockFooManager: FooManager {
var requestedId: Int?
var data: Int = 17
func getFoo(id: Int) -> Foo {
requestedId = id
return Foo(id, data: data)
}
}
class FooUserTests {
func testFooUserGetData() {
let mock = MockFooManager()
let user = FooUser(fooManager: mock)
let data = user.getData()
XCTAssertEqual(data, mock.data)
XCTAssertEqual(mock.requestedId, 3)
}
}

Swift switch between generics type and protocol conformance

I want to reach this goal:
func parse<T>(element: Any?) -> [T] {
// if T is kind of MyProtocol, return get result
// else
let array = [T]()
//do some stuff
return array
}
func get<T: MyProtocol>(obj: Any?) -> [T] {
return //some other stuffs
}
Is it possible in Swift language?
EDIT:
I have a class, let's say Parser, with some properties. I want a unique function signature, but the executed code must vary in base of property type.
class Parser: ParserProtocol {
let property1 : [MyClass1] = parse(element: elem1)
let property2 : [MyClass2] = parse(element: elem2)
}
protocol ParserProtocol {
func parse<T>(element: Any?) -> [T]
}
is this something you could use?
protocol GenericsTypeProtocol {
func callParseLogic() -> Void
}
protocol MyProtocol : GenericsTypeProtocol {}
extension GenericsTypeProtocol {
func callParseLogic() -> Void {
print("Generic logic called")
}
}
extension GenericsTypeProtocol where Self : MyProtocol {
func callParseLogic() -> Void {
print("MyProtocol logic called")
}
}
class GenericClass : GenericsTypeProtocol { }
class MyClass : MyProtocol { }
class MixedClass : GenericsTypeProtocol, MyProtocol {}
let a = GenericClass()
a.callParseLogic() //prints: Generic logic called
let b = MyClass()
b.callParseLogic() //prints: MyProtocol logic called
let c = MixedClass()
c.callParseLogic() //prints: MyProtocol logic called

How to determine the generic type from protocol implementation

I have a protocol that has a function that can return a String or a [String: String]. This is my declaration:
protocol Test {
associatedtype T: Hashable
func returnSomething() -> T
}
Then I want a default implementation for returnSomething, so I made a protocol extension:
extension Test {
func returnSomething() -> T {
let valueToReturn = readValueFromPLISTthatCanReturnAStringOrDictionary() as T
return valueToReturn
}
}
So finally I have 2 clases, TestString and TestDictionary that both implements Test protocol and I want to indicate the T parameter and I want to use the default implementation. How I do this?
class TestString: Test {}
class TestDictionary: Test { }
class TestString: Test where Test.T = String or similar?
I have a protocol that has a function that can return a String or a [String: String]. This is my declaration:
No problem. Let's write that down.
enum StringOrDictionary {
case string(String)
case dictionary([String: String])
}
protocol Test {
func returnSomething() -> StringOrDictionary
}
Then I want a default implementation for returnSomething, so I made a protocol extension:
Sounds good. I'll assume that readValueFromPLISTthatCanReturnAStringOrDictionary() actually returns Any, since that's what is returned by propertyList(from:).
extension Test {
func returnSomething() -> StringOrDictionary {
let value = readValueFromPLISTthatCanReturnAStringOrDictionary()
switch value {
case let string as String: return .string(string)
case let dictionary as [String: String]: return .dictionary(dictionary)
default: fatalError() // Or perhaps you'd like to do something else
}
}
}
It'd probably be nice to name your type something more meaningful than StringOrDictionary, but other than that, it should be pretty straightforward. Just make a type that means what you say. You want a type that means "OR" and that is an enum. (If you want a type that means "AND" that's a struct BTW.)
Regarding your answer, this isn't legal:
class RandomClass: Test where Test.T == String {
func getValue() {
let bah = doSomething() // I don't need here to specify bah's type.
}
}
The way to define your T is to implement the required method.
class RandomClass: Test {
func returnSomething() -> String {
return ""
}
}
If you wanted to share some common code, then you can attach that as an extension rather than a default implementation. You could write a returnString() method and call it from the RandomClass.returnSomething(). This is all very useful in some cases, but I definitely wouldn't use it in this case. You don't mean "returns any possible type (T)." You mean "returns one of two possible types" and that's an enum, not a generic.
Update: Apparently they've added a new feature that they've talked about but I thought wasn't in yet. You could now implement RandomClass this way:
class RandomClass: Test {
typealias T = String
}
(Which is a very nice new feature, even if it's not a good answer for this problem.)
Here's a solution to your immediate problem:
Create 2 subtypes of your protocol, each with a different definition of the associated type, and a different default implementation. You select which default implementation you'd like your classes to use by picking between the 2 sub types.
The next issue here is that [String: String] isn't Hashable. This is due to a lack of support for conditional conformances (e.g. the ability to express that a Dictionary is Hashable iff the keys and values are both Hashable), one of Swift's largest downfalls, IMO. You'll probably want to use the type erasing wrapper AnyHashable.
protocol ResultProvider {
associatedtype Result: Hashable
func getResult() -> Result
}
protocol StringResultProvider: ResultProvider {
typealias Result = String
}
extension StringResultProvider {
func getResult() -> String {
return "A string result"
}
}
protocol IntResultProvider: ResultProvider {
typealias Result = Int
}
extension IntResultProvider {
func getResult() -> Int {
return 123
}
}
class TestIntResult: IntResultProvider {}
class TestString: StringResultProvider {}
print(TestString().getResult())
print(TestIntResult().getResult())
// protocol DictionaryResultProvider: ResultProvider {
// typealias Result = [String: String]
// }
// extension DictionaryResultProvider {
// func getResult() -> [String: String] {
// return ["A dictionary": "result"]
// }
// }
// class TestDictionaryProvider: DictionaryResultProvider {}
You need to specify the typealias when you extend the class, like so:
protocol Test {
associatedtype T: Hashable
func returnSomething() -> T
}
extension String: Test {
typealias T = Int
}
func def() -> Int {
return 6
}
extension Test {
func returnSomething() -> T {
return def() as! Self.T
}
}
"".returnSomething()
6
However, I couldn't find a way to do it without force casting.
The only working solution is made the generic in the function and specify the variable type when calling the function. I was wondering if i could specify the T type when i implement the protocol in the class, similar like this:
class RandomClass: Test where Test.T == String {
func getValue() {
let bah = doSomething() // I don't need here to specify bah's type.
}
}
But previous example just don't work, so an alternative could be this:
protocol Test {
func doSomething<T>() -> T
}
extension Test {
func doSomething<T>(key: String) -> T {
return returnDictOrStringFromPLIST(key: key) as! T
}
}
class TestString: Test {
func getValue() {
let bah: String = doSomething()
}
}
class TestDict: Test {
func getValue() {
let bah: [String: String] = doSomething()
}
}

Heterogeneous mixture of protocol types, including a generic protocol

protocol ParentProtocol { }
protocol ChildProtocol: ParentProtocol { }
protocol Child_With_Value_Protocol: ParentProtocol {
associatedType Value
func retrieveValue() -> Value
}
Attempting to create a single array of type ParentProtocol that contains both ChildProtocol and Child_With_Value_Protocol. Is there any possible way to create a function that loops through the heterogeneous array and returns the values of just type Child_With_Value_Protocol?
This may require an architecture change. Open to all solutions.
Attempted Failed Solution #1
var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]
func retrieveValues() -> [Any] {
var values = [Any]()
for parent in parents {
if let childWithValue = parent as? Child_With_Value_Protocol { // Fails to compile
values.append(childWithValue.retrieveValue())
}
}
return values
}
This fails with an error of protocol 'Child_With_Value_Protocol' can only be used as a generic constraint because it has Self or associated type requirements which makes sense since the compiler would not know the type when converted to just Child_With_Value_Protocol, this leads to the next failed solution.
Attempted Failed Solution #2
If the array was a homogeneous array of just Child_With_Value_Protocol, type erasing could be used to retrieve the values.
var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]
struct AnyValue {
init<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: () -> Any
}
func retrieveValues() -> [Any] {
var values = [Any]()
for parent in parents {
values.append(AnyValue(parent).retrieveValue()) // Fails to compile
}
return values
}
This fails to compile due to the fact that the struct AnyValue has no initializer for the ParentProtocol.
Attempted Failed Solution #3
struct AnyValue {
init<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: () -> Any
}
var erased: [AnyValue] = [AnyValue(...), AnyValue(...), AnyValue(...)]
func retrieveValues() -> [Any] {
var values = [Any]()
for value in erased {
values.append(value.retrieveValue())
}
return values
}
Unlike the other solutions, this solution actually compiles. Problem with this solution resides in the fact that the array erased can only hold values of the type-erased versions of Child_With_Value_Protocol. The goal is for the array to hold types of both Child_With_Value_Protocol and ChildProtocol.
Attempted Failed Solution #4
Modifying the type-erase struct to include an initializer for ParentProtocol still creates a solution that compiles, but then the struct will only use the less specific init, instead of the more specific init.
struct AnyValue {
init?<T: ParentProtocol>(_ protocol: T) {
return nil
}
init?<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: (() -> Any)?
}
The prior comments are likely right. Nevertheless, you could box the variants in an enum and create an array of those. The reference would then switch on the enum value, each having associated data of the right type
EDIT: I didn't bother with the associatedValue, because it seems irrelevant to the question being asked. The following works in a playground:
protocol ParentProtocol: CustomStringConvertible {
static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol]
}
protocol ChildProtocol: ParentProtocol { }
protocol Other_Child_Protocol: ParentProtocol { }
enum FamilyBox {
case Parent(parent: ParentProtocol)
case Child(child: ChildProtocol)
case OtherChildProtocol(withValue: Other_Child_Protocol)
}
var parents: [FamilyBox] = []
struct P: ParentProtocol {
var description: String { return "Parent" }
static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
var values = [ParentProtocol]()
for parent in parents {
switch parent {
case .Parent(let elementValue):
values.append(elementValue)
default:
break;
}
}
return values
}
}
struct C: ChildProtocol {
var description: String { return "Child" }
static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
var values = [ParentProtocol]()
for parent in parents {
switch parent {
case .Child(let elementValue):
values.append(elementValue)
default:
break;
}
}
return values
}
}
struct CV: Other_Child_Protocol {
var description: String { return "Other Child" }
static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
var values = [ParentProtocol]()
for parent in parents {
switch parent {
case .OtherChildProtocol(let elementValue):
values.append(elementValue)
default:
break;
}
}
return values
}
}
let p = FamilyBox.Parent(parent: P())
let c = FamilyBox.Child(child: C())
let cv = FamilyBox.OtherChildProtocol(withValue: CV())
let array:[FamilyBox] = [p, c, cv]
print(P.retrieveValues(array))
print(C.retrieveValues(array))
print(CV.retrieveValues(array))
The prints from the last three lines are:
[Parent]
[Child]
[Other Child]
While I'm sure it can be improved, I think that meets the original intent. No?

How to make a closure optional with a default value

I have a bunch of functions that I would like to be able to specify a default closure if one is not provided. I can't seem to figure out how to do it without some ugly code.
So for example, I would like the perform function to accept an optional parameter called closure that is executed when provided. Otherwise it will default to executing myClosure. How can I make this better so I don't have to repeat the function calls?
class MyClas {
typealias closureType = ((number: Int) -> Int)?
func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = nil) -> Int {
if closure == nil {
return myClosure(number)
} else {
return closure!(number: number)
}
}
}
Ideally, I could do this!
class MyClass {
typealias closureType = ((number: Int) -> Int)?
func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = myClosure) -> Int {
return closure(number: number)
}
}
Your problem is that you've made made myClosure a method (or member function), which means it doesn't have the signature you want (it's instead a curried function, of type MyClass->Int->Int).
Either pull it out of the class, or make it a static (or rather "class" in the case of a class) method:
class MyClass {
typealias closureType = (number: Int) -> Int
class func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = MyClass.myClosure) -> Int {
return closure(number: number)
}
}
P.S. once you do this, it doesn't need to be optional any more
Just to show it compiling as a non-static method:
class MyClass {
typealias closureType = MyClass -> (number: Int) -> Int
func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = myClosure) -> Int {
return closure(self)(number: number)
}
}
let c = MyClass()
println(c.perform(5)) // prints 10
Closure is first-class citizen in Swift. So you can provide default value for it.
class MyClass {
func perform(number: Int, closure: Int -> Int = { $0 * 2 }) -> Int {
return closure(number)
}
}

Resources