2

Let's say I have one protocol

protocol TestProtocol {
}

Also, have one struct and inherited it from protocol.

struct StructOne: TestProtocol {
}

Now, I have one view controller class and created one generic function to accept an array of TestProtocol type objects(This is a generic arugment). This is for the passing parameter for SDK API calls.

But in some API calls, I do not need to pass this parameter array. So, I just wanted to set nil value or default empty array within the function definition.

Here is the class

class TestViewController: UIViewController {
//  func genericCall<T: TestProtocol>(param: [T] = []) { // Not work
//  func genericCall<T: TestProtocol>(param: [T]? = nil) { // Not work
  func genericCall<T: TestProtocol>(param: [T]?) {
    if param?.isEmpty == true {
      print("Empty Param Calling")
    } else {
      print("With Param Calling")
    }
  }
   
  override func viewDidLoad() {
    super.viewDidLoad()
    let param = [StructOne(), StructOne()]
    self.genericCall(param: param) // This one work
    self.genericCall(param: [] as [StructOne]) // This one also work. But want default value in function
    self.genericCall(param: nil) // Not work : Error - Generic parameter 'T' could not be inferred
//    self.genericCall() // Not work with default empty value : Error - Generic parameter 'T' could not be inferred
  }
}

I am getting this compile-time error: Generic parameter 'T' could not be inferred

I can set an empty array during the function call. which is mention here

I also check this link, which allows setting nil value if have only T type, but in here is the array of T ([T]).

Is there any way to set the default nil value or any other way to set default an empty array? so we can avoid passing an empty array to each function call.

Update:

I can't use it this way. As SDK function call not allowed me to pass param value.

func genericCall(param: [TestProtocol] = []) {
    // param: Not allowed me to pass to the sdk call function.
    if param.isEmpty == true {
        print("Empty Param Calling")
    } else {
        print("With Param Calling")
    }
}

Note: This is a demo code. In reality, I'm using one of the SDK so, I cant change more in the protocol.

oguz ismail
  • 1
  • 16
  • 47
  • 69
Raja Kishan
  • 16,767
  • 2
  • 26
  • 52
  • Why have you used generics here? Why not just declare `func genericCall(param:[TestProtocol]?)` – Paulw11 Jun 29 '21 at 21:46
  • And if you do need generics (ie this is just a trivial example for the purposes of the question), but the generic function in a generic class and then you can create a specialised instance of the class and `T` doesn't need to be inferred – Paulw11 Jun 29 '21 at 21:54
  • @Paulw11 I used your approach and it was my first attempt but when I pass this param value to SDK function call it to throw me the error ```Protocol as a type cannot conform to the protocol itself``` – Raja Kishan Jun 30 '21 at 06:18

2 Answers2

3

nil is too broad in this case, as the compiler can't infer to which [T]? it should apply the nil.

You will need to explicitly specify the Optional generic argument here:

self.genericCall(param: [StructOne]?.none)
Cristik
  • 30,989
  • 25
  • 91
  • 127
  • Thank You for answering. I can fix the issue by your first approach passing StructOne. But getting error while using the second approach. ```Protocol 'TestProtocol' as a type cannot conform to the protocol itself``` – Raja Kishan Jun 29 '21 at 18:05
  • @RajaKishan I'll remove it from the answer, I didn't had a chance to compile it, but since you get errors, it means that protocol existentials with generics don't yet work in Swift. – Cristik Jun 29 '21 at 18:09
  • No problem. But is there any way where I can set value in function definition instead of passing this value ```[StructOne]?.none``` at the time of function call. – Raja Kishan Jun 29 '21 at 18:12
  • @RajaKishan your're kinda out of luck here, as [Swift protocols don't conform to themselves](https://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself), so you'll likely need to change your design if you want to accomodate this – Cristik Jun 29 '21 at 18:29
1

You can create a generic method constraining your generic type to RangeReplaceableCollection and its Element to your TestProtocol. The reason it will work is that RangeReplaceableCollection requires the protocols that conform to it to provide an empty initializer:


protocol TestProtocol { }

struct StructOne: TestProtocol { }

class TestViewController: UIViewController {

    func genericCall<T: RangeReplaceableCollection>(param: T = .init()) -> T where T.Element:  TestProtocol {
        if param.isEmpty {
          print("Empty Param Calling")
        } else {
          print("With Param Calling")
        }
        return param
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        let result: [StructOne] = genericCall() // This one work
        print(result)
    }
}

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • Thank you for your answere. But when I call genericCall(). It gives me the error Instance method 'Instance method 'genericCall(param:)' requires that '_.Element' conform to 'TestProtocol'' Am I missing anything? – Raja Kishan Jun 30 '21 at 06:14
  • @RajaKishan you probably forgot to add the returning type `T`. Note that you need to explicitly set the resulting type as well – Leo Dabus Jun 30 '21 at 14:56
  • Thank You. since my function is not return anything. so doing this may be the next developer is confused why this returning. so currently I'm now going with other answer. Thanks for this way – Raja Kishan Jun 30 '21 at 16:29