I am having an issue creating a function that returns a generic type that conforms to the Codable protocol.
To give more context:
Below is a protocol with an associated type:
public protocol CodableProtocol {
associatedtype T: Codable
var httpMethod: HTTPMethods { get }
func toDictionary() -> T
}
Notice the toDictionary() method that returns a generic type that must conform to codable. For the most part it is easy to implement toDictionary() by defining the return as Dictionary<String, String>
However, the problem arises when the Dictionary has more than one data type.
For example: [String: [String: String/ Int], String: String]
The value for the key in the dictionary is codable, so basically when implementing toDictionary() one needs to return a generic type that is constrained to Codable.
My problem is implementing the toDictionary() in the class returning a generic type and having that class conform to the protocol. I almost feel that defining a type alias in the class could fix it, but when I do it breaks conformance to the protocol.
Related
Assuming this code:
protocol Test: AnyObject {}
class RealTest: Test {}
class Wrapper<A: AnyObject> {
weak var value: A?
init(_ value: A) {
self.value = value
}
}
let realTest = RealTest()
let wrapper = Wrapper(realTest)
the code above works and wrapper is created. However, when I change the realTest to:
let realTest: Test = RealTest()
I get the error message
Generic class 'Wrapper' requires that 'Test' be a class type
Is there a solution to this, as I need the AnyObject requirement and I only know the protocol the objects are conforming to?
Protocols do not conform to themselves (or to any protocol). Only concrete types can conform to a protocol.
If the only thing you need to know about this type is that it is a class (AnyObject), then you don't want a generic here, you just want to pass the protocol type itself (technically the "existential").
class Wrapper {
let value: AnyObject
init(_ value: AnyObject) {
self.value = value
}
}
If you need a generic here for some other reason, then the generic must be over a concrete type, not a protocol.
I'm struggling with error message of "Type 'Self' does not conform to protocol 'Hashable' " when using an extension to implement a type method for all conforming enums as below:
protocol A {
static func doSth(with dict[Self: Any])
}
extension A where Self: RawRepresentable, Self.RawValue == Int {
static func doSth(with dict[Self: Any]) {
for (key, value) in dict { //...do something
}
}
enum First: Int, A {
case f1, f2, f3
}
enum Second: Int, A {
case s1, s2, s3
}
....
On the other hand, when I implement the method without using protocol and extension in each of the Enums by coding my method parameter's array key type as Enum like this:
static func doSth(with dict[First: Any])
static func doSth(with dict[Second: Any])
and so on...I have no error and can use the method of doSth correctly. However, I have dozens of such Enums and of course prefer to implement this method in extension instead.
P.S. The purpose of the implementation is to call the method doSth with dictionary argument having a key from the enum's case, such as:
First.doSth(with: [.f1: "good", .f2:"better"]
Any suggestion is welcome.
Make your protocol conform to Hashable (this is precisely what the error message is saying):
protocol A: Hashable { ...}
Your problem is that [Self: Any] isn't valid unless Self: Hashable, so your doSth method can't be defined.
Protocols in Swift can declare the init() method in their definition. However, I can't think of any use case where this solves any problem other than forcing the conforming classes to define the init() as in the protocol. We can call the declared methods on the protocol type but init on protocol cannot be used to instantiate its object, which is its only purpose.
What problem does declaring init() method in a protocol solve?
I think the real utility comes when it's used as a constraint in a generic class o function. This is real code from one of my projects.
I declare a protocol with a init:
protocol JSONCreatable {
init(fromJson json: JSON)
}
Then, in a generic function where I return a class that conforms to that protocol:
import SwiftyJSON
extension JSON {
func asObject<T>() -> T? where T: JSONCreatable {
if isEmpty {
return nil
}
return T(fromJson: self)
}
func asArray<T>() -> [T] where T: JSONCreatable {
return array?.map{ json in T(fromJson: json) } ?? []
}
}
This allows me to do things like this:
let user: User = json["user"].asObject()
let results: [Element] = json["elements"].asArray()
It forces class to have init(data: data) from some data, example:
protocol JSONable {
init(data: JSON)
}
forces all classes, that are JSONable to have an initialiser from JSON, so you are always sure, that you can create an instance from JSON.
It's commonly used in order to allow for protocol extensions and generic placeholders constrained to protocols to call the initialiser on the given concrete type that conforms to the protocol. For example, consider RangeReplaceableCollection's default implementation of init<S : Sequence>(_ elements: S):
extension RangeReplaceableCollection {
// ...
/// Creates a new instance of a collection containing the elements of a
/// sequence.
///
/// - Parameter elements: The sequence of elements for the new collection.
public init<S : Sequence>(_ elements: S) where S.Iterator.Element == Iterator.Element {
self.init()
append(contentsOf: elements)
}
// ...
}
Without init() being defined as a protocol requirement of RangeReplaceableCollection, there's no way for the extension to know that we can call init() in order to create a new instance of the conforming type.
But it can also be used directly outside of generics and extensions – for example, it can be used to construct a new instance represented by a given existential metatype (the metatype of 'some concrete type that conforms to a protocol'):
protocol P {
init()
}
struct S : P {
init() {}
}
let s: P = S()
let s1 = type(of: s).init() // creates a new instance of S, statically typed as P.
In this example:
type(of: s) returns the dynamic type of s as P.Type (an existential metatype), as s is statically typed as P. Remember that type(of:) is a (T) -> T.Type operation.
init() constructs a new instance of the underlying concrete type, in this case S.
The new instance is statically typed as P (i.e boxed in an existential container).
I have the following struct, variable and a function:
struct MyModel {
var keyString: String
var keyNum: Int
}
let data = "{\"keyString\": \"valueString\", \"keyNum\": 1 }"
func myFunction<T: AnyObject>(str: String) throws -> T? {
return nil
}
How can I call the function with MyModel? Below code makes the compiler complain: "Generic parameter 'T' could not be inferred"
let myModel = try? myFunction(str: data) as? MyModel
Link to Swift REPL: http://swiftlang.ng.bluemix.net/#/repl/57f1fa479ce3c95fc38e63b3
Since your generic type parameter is declared with a constraint, like this:
func myFunction<T: AnyObject>
So whatever type you use, it must be a reference type or implement NSObjectProtocol or something like that.
A swift struct is a value type, so MyModel can't possibly be used as a generic type parameter.
Solutions:
Change the generic type constraint to <T: Any> to make it accept value types.
Remove the generic type constraint.
Change the struct to a class and add an initializer.
I have the following playground:
// : Playground - noun: a place where people can play
import Foundation
// Define a protocol
protocol MyProtocol
{
func getMeAString() -> String!
}
// Create a class that conforms to the protocol
class ClassThatConformsToProtocol: MyProtocol
{
func getMeAString() -> String! {
return "hey!"
}
}
// Define a function that takes a generic, but ensure the generic conforms to the protocol
func logTheThing<T: MyProtocol>(theThing: T!)
{
theThing.getMeAString()
}
// Let's create an instance
let instanceOfClassThatConforms = ClassThatConformsToProtocol()
// We're now going to see if we can pass the object to our generic method
logTheThing(instanceOfClassThatConforms)
// It works!
// Let's create another method, but this one only knows the protocol its parameter conforms to, the in implementing class
func takeInAnObjectThatWeOnlyKnowItsProtocol(object:MyProtocol)
{
logTheThing(object) // error: cannot convert value of type 'MyProtocol' to expected argument type '_!'
}
As mentioned, I receive an error stating:
error: cannot convert value of type 'MyProtocol' to expected argument
type '_!'
For what reason would I not be able to pass this conformed object into the generic method?
If the compiler knows an object conforms to the protocol, for what reason would I NOT be able to pass it into a generic method?
A protocol does not conform to itself. You must make this generic:
func takeInAnObjectThatWeOnlyKnowItsProtocol<T: MyProtocol>(object: T)
{
logTheThing(object)
}
To the next question: why doesn't a protocol conform to itself? Because it doesn't. Eventually it probably will, but it doesn't today.
That said, given this specific code, there's no reason to make this generic at all. Just pass the protocol and it'll do exactly what you want:
func logTheThing(theThing: MyProtocol) {
theThing.getMeAString()
}
func takeInAnObjectThatWeOnlyKnowItsProtocol(object: MyProtocol) {
logTheThing(object)
}
Passing object: MyProtocol means "any type that conforms to MyProtocol" and that matches in both places. Passing <T: MyProtocol>(object: T) means "a specific, concrete type that conforms to MyProtocol" and "any type that conforms to MyProtocol" is not a "specific, concrete type" and so fails (today; again, they'll probably fix that some day).
(Unrelated note: There is never a good reason to return String! in Swift. Just return String. And you'd get a better error message and fewer other little problems if you don't use T! and just use T in your logging call. Unless you're bridging to ObjC, there is almost never a reason to pass or return ! types. They only make sense in pure Swift for properties.)