How can I use a protocol with a typealias as a func parameter?
You cannot specialize generic protocol with <>
.
Instead, you can:
func method<B: ProtocolB where B.T: ProtocolA>(value: B) {
}
That says, method
accepts B
where B
conforms ProtocolB
and its T
conforms ProtocolA
.
And, in this case, you don't need where B.T: ProtocolA
because it's obvious.
func method<B: ProtocolB>(value: B) {
...
}
How to pass generic protocol as function parameter
You must use Datatype
as a generic constraint:
func set<T: Datatype>(key: String, type: T ,value: T.dataType)
Note how you also need to change the type of the value
parameter to T.dataType
, for this to be type safe. That is, the value you pass in must be the same type as the dataType
of the type
parameter.
AssociatedType in a protocol with typealias
You need a class/struct that conforms to Router
, which has an Answer
of String
:
class StringRouter : Router {
typealias Answer = String
}
let callback: StringRouter.AnswerCallback? = nil
If all you want is such a type alias, you don't need a protocol:
typealias AnswerCallback<T> = (T) -> Void
Using protocol with typealias as a property
Depending on what you are actually trying to do, this can work using type erasure. If you follow the instructions in the link R Menke posted in the comments, you can achieve what you are trying to do. Since your property in TestClass
seems to be a let, I'm going to assume you already know the type of DataType
at compile time. First you need to setup a type erased Archivable
class like so:
class AnyArchiver<T>: Archivable {
private let _save: ((T, String) throws -> Void)
private let _load: (String throws -> T)
init<U: Archivable where U.DataType == T>(_ archiver: U) {
_save = archiver.save
_load = archiver.load
}
func save(data: T, withNewName newName: String) throws {
try _save(data, newName)
}
func load(fromFileName fileName: String) throws -> T {
return try _load(fileName)
}
}
Much like Swift's AnySequence
, you'll be able to wrap your Archiver
in this class in your TestClass
like so:
class TestClass {
let archiver = AnyArchiver(Archiver())
}
Through type inference, Swift will type TestClass
' archiver let constant as an AnyArchiver<Int>
. Doing it this way will make sure you don't have to create a dozen protocols to define what DataType
is like StringArchiver
, ArrayArchiver
, IntArchiver
, etc. Instead, you can opt in to defining your variables with generics like this:
let intArchiver: AnyArchiver<Int>
let stringArchiver: AnyArchiver<String>
let modelArchiver: AnyArchiver<Model>
rather than duplicating code like this:
protocol IntArchivable: Archivable {
func save(data: Int, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> Int
}
protocol StringArchivable: Archivable {
func save(data: String, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> String
}
protocol ModelArchivable: Archivable {
func save(data: Model, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> Model
}
let intArchiver: IntArchivable
let stringArchiver: StringArchivable
let modelArchiver: ModelArchivable
I wrote a post on this that goes into even more detail in case you run into any problems with this approach. I hope this helps!
How can I pass a typealias as a function parameter in Swift?
A type alias is just a synonym for an existing type - it doesn't create a new type, it just create a new name.
That said, if you want to pass a type to a function, you can make it generic, and define it as follows:
func doSomething<T>(type: T.Type) {
println(type)
}
You can invoke it by passing a type - for instance:
doSomething(String.self)
which will print
"Swift.String"
If you define a typealias for String
, the output won't change though:
typealias MyString = String
doSomething(MyString.self)
Swift - Make func type explicit (in protocol)
As Action
is a typealias
and not a type, swift can't differentiate them.
As explained in the documentation:
After a type alias is declared, the aliased name can be used instead of the existing type everywhere in your program. The existing type can be a named type or a compound type. Type aliases do not create new types; they simply allow a name to refer to an existing type.
Edit: My bad, I thought you wanted to differentiate () -> Void
and Action
.
As far as I know, using a closure is the only way of doing this in swift.
typealias Foo = (Int) -> Void
protocol FooBar {
var foo: Foo { get }
}
class FooBarBaz: FooBar {
var foo: Foo = { intValue in
// ...
}
}
Edit 2: Additional information:
According to the Grammar of a function declaration, the only way of declaring a function is to explicitly provide a parameter-clause
, which require listing all parameters between parentheses:
function-declaration → function-head function-name generic-parameter-clauseopt function-signature generic-where-clauseopt function-bodyopt
function-signature → parameter-clause throwsopt function-resultopt
parameter-clause → ( ) | ( parameter-list )
So apparently, it's not possible to declare a function with a type as signature without using a closure.
What is the purpose of declaring typealias for generic type parameter
Declaring a public typealias makes it possible to access the generic type parameter outside of the closed generic type.
For example if you declare a typealias WidgetFoo = Foo<Widget>
and keep using WidgetFoo
in other places it will be possible to access its T
via WidgetFoo.Element
(which refers to Widget
) whereas you cannot access the generic type parameter E
itself. This enables robust and refactoring friendly code - imagine you want to replace Widget
with BetterWidget
you only have to change one place (the type alias declaration) and no other because WidgetFoo.Element
will then refer to BetterWidget
.
Example code (provided by @Airspeed Velocity)
struct S<T> { typealias U = T }
typealias TA = S<Int>
let x: TA.T // 'T' is not a member type of 'TA' (aka 'S<Int>')
let y: TA.U
Protocol with generic typealias used by an extension
The extension method parsed()
cannot infer that the generic type T
is the same type as the immutable that you assign the result of the call to.
You can solve this by supplying the parsed(..)
method with the actual parser type as a parameter. You needn't actually make explicit use of this parameter (hence internal name omitted using _
), and its sole purpose in this context is to infer the type of the generic T
.
// ...
extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
func parsed<T: ParserType>(_: T.Type) -> ([T.ParsedObjectType], PaginationContext?) {
let paginatedParser = PaginatedParser<T>()
return try! paginatedParser.parse(self as! AnyObject)
}
}
With this, you needn't explicitly supply the type of result
as it can be inferred from the return of parsed(..)
. Note that I've also added a limitation in the extension for keys that conform to StringLiteralConvertible
(String
, among others, see language ref.) and for AnyObject
values. You shouldn't make extensions more general than they need to be.
Example usage:
let thing2 = [String: AnyObject]()
let result = thing2.parsed(DogParser)
print(result.dynamicType)
/* (Array<Dog>, Optional<PaginationContext>), OK */
Is it possible to have a property containing generic protocol in Swift?
AnotherProtocol
has an associated value which must be some type that conforms to MyProtocol
. A protocol cannot conform to protocols, including itself, so that type cannot be MyProtocol
; it has to be a type conforming to MyProtocol
.
var items
, then, has to be an array of whatever that type is.
So, let's say you have a type conforming to MyProtocol
which defines the associated type to be String
:
struct Foo: MyProtocol {
func doSmth(_ value: String) -> String {
value
}
}
Then you can define MyClass
as:
class MyClass: AnotherProtocol {
var items: [Foo]
init(_ items: [Foo]) { self.items = items }
}
Of course, if you don't want it to be specific to Foo
, you can make it generic with respect to any type T
that conforms to MyProtocol
and has an associated type of String
:
class MyClass<T: MyProtocol>: AnotherProtocol where T.DoSmthValue == String {
var items: [T]
init(_ items: [T]) { self.items = items }
}
let myClass = MyClass([Foo(), Foo(), Foo()])
Related Topics
Spritekit Skscene Not Resizing Correctly to Fit iPhone 12
Convert Gregorian Date to Hijri Date
Nsundomanager: Capturing Reference Types Possible
Add Value to Variable Inside Closure in Swift
How to Pull The Artist Value from Mpmediaitemcollection
Hit Fatal Error: Unexpectedly Found Nil While Unwrapping an Optional Value (Lldb)
Closure (With Default Value) as Function Parameter
Why Is a Firestore Listener Returning .Added Twice When a Single Document Is Added
Change The 2Nd and 3Rd Pickerview Acording to What Row from The 1St Picker Is Selected
How to Update User Interface on Core Data
Find Index of Annotation Mapkit
Metal: Limit Mtlrendercommandencoder Texture Loading to Only Part of Texture
Can't Unwrap Optional.None When Setting Window Background Color
How to Bend a Rectangle in Sprite Kit
Heightanchor.Constraint Not Change Height of View
Combining Scenekit and Spritekit in a Single Screen
How to Get Section of UItableview from Inside a Child UIcollectionview