This example Swift code does not compile if SomeClass is defined as struct. The compiler says: #value $T6 is not identical to (String, Proto)
However, the compiler does not complain if SomeClass is a class.
Why?
public protocol Proto {
func hello(value:Int)
}
public struct SomeClass {
var map = [String:Proto]()
public func store (key:String, value:Proto) {
map[key] = value // That does not work if SomeClass is a struct
}
}
Because you modify the element of the struct in the store function, and if you do so, you have to add the "mutating" prefix to it.
So:
public mutating func store (key:String, value:Proto) {
map[key] = value // That does not work if SomeClass is a struct
}
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method.
Related
I'm a developer on Java and I'm trying to write in Swift the same solution that I have in Java code.
Is it possible to do this on Swift?
Example Java:
public interface Converter<S,T> {
T convert(S in)
}
public class CarConverterToDTO implements Converter<Car, CarDTO> {
#Override
public CarDTO convert(Car in) {
.....
}
}
Example Swift:
protocol Converter {
func convert<IN, OUT>(in: IN) -> OUT
}
How it would be the implementation?
Thanks!!!
What appears to be a simple question is actually the tip of a rather large and unpleasant iceberg…
I'm going to start by giving you what is probably the real solution to your problem:
class Converter<Input, Output> {
func convert(_ input: Input) -> Output {
fatalError("subclass responsibility")
}
}
struct Car { }
struct CarDTO { }
class DTOCarConverter: Converter<Car, CarDTO> {
override func convert(_ input: Car) -> CarDTO {
return CarDTO()
}
}
Above, I've translated your Java Converter interface into a Swift class instead of a Swift protocol. That's probably what you want.
Now I'll explain why.
A programmer coming from Java to Swift might think that a Swift protocol is the equivalent of a Java interface. So you might write this:
protocol Converter {
associatedtype Input
associatedtype Output
func convert(_ input: Input) -> Output
}
struct Car { }
struct CarDTO { }
class /* or struct */ DTOCarConverter: Converter {
func convert(_ input: Car) -> CarDTO {
return CarDTO()
}
}
Okay, now you can create a converter and convert something:
let converter = DTOCarConverter()
let car = Car()
let dto = converter.convert(car)
But you're going to run into a problem as soon as you want to write a function that takes a Converter as an argument:
func useConverter(_ converter: Converter) { }
// ^
// error: protocol 'Converter' can only be used as a generic constraint because it has Self or associated type requirements
“Well, duh,” you say, “you forgot the type arguments!” But no, I didn't. Swift doesn't allow explicit type arguments after a protocol name:
func useConverter(_ converter: Converter<Car, CarDTO>) { }
// ^ ~~~~~~~~~~~~~
// error: cannot specialize non-generic type 'Converter'
I don't want to get into why you can't do this. Just accept that a Swift protocol is not generally equivalent to a Java interface.
A Swift protocol with no associated types and no mention of Self is, generally, equivalent to a non-generic Java interface. But a Swift protocol with associated types (or that mentions Self) is not really equivalent to any Java construct.
When discussing this problem, we often use the acronym “PAT”, which stands for “Protocol with Associated Types” (and includes protocols that mention Self). A PAT doesn't define a type that you can use as a function argument, return value, or property value. There's not much you can do with a PAT:
You can define a subprotocol. For example, Equatable is a PAT because it defines the == operator to take two arguments of type Self. Hashable is a subprotocol of Equatable.
You can use a PAT as a type constraint. For example, Set is a generic type. Set's type parameter is named Element. Set constrains its Element to be Hashable.
So you can't write a function that takes a plain Converter as an argument. But you can write a function that takes any implementation of Converter as an argument, by making the function generic:
func useConverter<MyConverter: Converter>(_ converter: MyConverter)
where MyConverter.Input == Car, MyConverter.Output == CarDTO
{ }
That compiles just fine. But sometimes it's inconvenient to make your function generic.
And there's another problem that this doesn't solve. You might want a container that holds various Converters from Car to CarDTO. That is, you might want something like this:
var converters: [Converter<Car, CarDTO>] = []
// ^ ~~~~~~~~~~~~~
// error: cannot specialize non-generic type 'Converter'
We can't fix this by making converters generic, like we did with the useConverter function.
What you end up needing is a “type-erased wrapper”. Note that “type-erased” here has a different meaning that Java's “type erasure”. In fact it's almost the opposite of Java's type erasure. Let me explain.
If you look in the Swift standard library, you'll find types whose names start with Any, like AnyCollection. These are mostly “type-erased wrappers” for PATs. An AnyCollection conforms to Collection (which is a PAT), and wraps any type that conforms to Collection. For example:
var carArray = Array<Car>()
let carDictionary = Dictionary<String, Car>()
let carValues = carDictionary.values
// carValues has type Dictionary<String, Car>.Values, which is not an array but conforms to Collection
// This doesn't compile:
carArray = carValues
// ^~~~~~~~~
// error: cannot assign value of type 'Dictionary<String, Car>.Values' to type '[Car]'
// But we can wrap both carArray and carValues in AnyCollection:
var anyCars: AnyCollection<Car> = AnyCollection(carArray)
anyCars = AnyCollection(carValues)
Note that we have to explicitly wrap our other collections in AnyCollection. The wrapping is not automatic.
Here's why I say this is almost the opposite of Java's type erasure:
Java preserves the generic type but erases the type parameter. A java.util.ArrayList<Car> in your source code turns into a java.util.ArrayList<_> at runtime, and a java.util.ArrayList<Truck> also becomes a java.util.ArrayList<_> at runtime. In both cases, we preserve the container type (ArrayList) but erase the element type (Car or Truck).
The Swift type-erasing wrapper erases the generic type but preserves the type parameter. We turn an Array<Car> into an AnyCollection<Car>. We also turn a Dictionary<String, Car>.Values into an AnyCollection<Car>. In both cases, we lose the original container type (Array or Dictionary.Values) but preserve the element type (Car).
So anyway, for your Converter type, one solution to storing Converters in a container is to write an AnyConverter type-erased wrapper. For example:
struct AnyConverter<Input, Output>: Converter {
init<Wrapped: Converter>(_ wrapped: Wrapped) where Wrapped.Input == Input, Wrapped.Output == Output {
self.convertFunction = { wrapped.convert($0) }
}
func convert(_ input: Input) -> Output { return convertFunction(input) }
private let convertFunction: (Input) -> Output
}
(There are multiple ways to implement type-erased wrappers. That is just one way.)
You can then use AnyConverter in property types and function arguments, like this:
var converters: [AnyConverter<Car, CarDTO>] = [AnyConverter(converter)]
func useConverters(_ converters: [AnyConverter<Car, CarDTO>]) {
let car = Car()
for c in converters {
print("dto = \(c.convert(car))")
}
}
But now you should ask: what's the point? Why bother making Converter a protocol at all, if I'm going to have to use a type-erased wrapper? Why not just use a base class to define the interface, with subclasses implementing it? Or a struct with some closures provided at initialization (like the AnyConverter example above)?
Sometimes, there's not a good reason to use a protocol, and it's more sensible to just use a class hierarchy or a struct. So you should take a good look at how you're implementing and using your Converter type and see if a non-protocol approach is simpler. If it is, try a design like I showed at the top of this answer: a base class defining the interface, and subclasses implementing it.
The Swift equivalent to your Java code looks like this.
protocol Converter {
associatedtype Input
associatedtype Output
func convert(input: Input) -> Output
}
class CarConverterToDTO: Converter {
typealias Input = Car
typealias Output = CarDTO
func convert(input: Car) -> CarDTO {
return CarDTO()
}
}
Explanation
The equivalent to a generic Java interface in Swift, would be a protocol with associatedtypes.
protocol Converter {
associatedtype Input
associatedtype Output
}
To create an implementation of that protocol, the implementation must specify which types the associated types maps to, using typealias.
class CarConverterToDTO: Converter {
typealias Input = Car
typealias Output = CarDTO
}
Type Erasure
If you try to use to this approach, you may run into the issue of trying to store an instance of your generic protocol in a variable or property, in which case you will get the compiler error:
protocol 'Converter' can only be used as a generic constraint because it has Self or associated type requirements
The way to solve this issue in Swift, is by using type erasure, where you create a new implementation of your generic protocol, that itself is a generic type (struct or class), and uses a constructor accepting a generic argument, matching your protocol, like so:
struct AnyConverter<Input, Output>: Converter {
// We don't need to specify type aliases for associated types, when the type
// itself has generic parameters, whose name matches the associated types.
/// A reference to the `convert(input:)` method of a converter.
private let _convert: (Input) -> Output
init<C>(_ converter: C) where C: Converter, C.Input == Input, C.Output == Output {
self._convert = converter.convert(input:)
}
func convert(input: Input) -> Output {
return self._convert(input)
}
}
This is usually accompanied by an extension function on the generic protocol, that performs the type erasure by creating an instance of AnyConverter<Input, Output> using self, like so:
extension Converter {
func asConverter() -> AnyConverter<Input, Output> {
return AnyConverter(self)
}
}
Using type erasure, you can now create code that accepts a generic Converter (by using AnyConverter<Input, Output>), that maps Car to CarDTO:
let car: Car = ...
let converter: AnyConverter<Car, CarDTO> = ...
let dto: CarDTO = converter.convert(input: car)
I have a generic class of the form:
class BaseClass<T> {
var prop: T
...
}
I then have multiple subclasses of the form:
class SubClassOne: BaseClass<SomeSubClass> {
...
}
class SubClassTwo: BaseClass<SomeOtherSubClass> {
...
}
Where the type parameters SomeSubClass and SomeOtherSubClass both inherit from a common base class SomeBaseClass.
I now want to define a variable to store instances of both SubClassOne and SubClassTwo. I have tried many possibilities:
var obj: BaseClass
var obj: BaseClass<SomeBaseClass>
var obj: BaseClass<Any>
But the first attempt results in the error Reference to generic type 'BaseClass' requires arguments in <...>, and the other two result in the error Cannot assign value of type 'SubClassOne' to type ... when trying to assign a value. I even tried to trick the Swift compiler into inferring the type for me by initializing an array:
var testArray = [SubClassOne(), SubClassTwo()]
But even this failed, resulting in the error Heterogeneous collection literal could only be inferred to [Any]; add explicit type annotation if this is intentional. Indeed, the only type annotation that successfully allows storage of both SubClasses is Any or AnyObject. Is it possible to store these instances with a more specific type? If not, why?
The reason it's important to do so is that I ultimately want to get the property prop from the stored variable obj. I am unable to do so if obj is stored as Any. I am also unable to simply cast it to SubClassOne or SubClassTwo because the method itself where I am trying to access the properties is a generic method, and which of SubClassOne or SubClassTwo to cast to depends on the generic type parameter of the method:
func castObj<T>(asType: T.Type) {
(self.obj as? T).prop
}
Which would be called as: castObj(asType: SubClassOne.self) or castObj(asType: SubClassTwo.self). However, we run into the same problem: the only generic type parameter constraint I can define that accepts both SubClassOne and SubClassTwo is Any, and then the Swift compiler complains: Value of type 'T' has no member 'prop'.
As a workaround I tried to define a protocol that encapsulates the desired property:
protocol HasProp {
var prop: SomeBaseClass { get }
}
Then I added this to the declaration of SubClassOne and SubClassTwo. However this resulted in still another error: Type 'SubClassOne' does not conform to protocol 'HasProp'. This confuses me as well, since SubClassOne and SubClassTwo both inherit prop from BaseClass<SomeSubClass> and so actually do conform to the protocol.
In summary:
Is it possible to store instances of SubClassOne and SubClassTwo with a more specific type that gives access to properties of BaseClass? If not, why?
Why do the SubClasses not conform to the protocol as expected?
How can I change the design to attain my desired behavior?
The problem is that at the moment the function castObj has no type constraints for its generic parameter, T. By giving a type constraint of BaseClass you should be fine, since BaseClass has both properties.
func castObj<T: BaseClass>(asType: T.Type) {
(self.obj as? T).propOne
(self.obj as? T).propTwo
}
In your example, the type of propTwo was common to both subclasses and the type of propOne was specialized. Make your design reflect that.
[was]
class BaseClass<T,U> {
var propOne: T
var propTwo: U
...
}
class SubClassOne: BaseClass<SomeSubClass, SomeClass> {}
class SubClassTwo: BaseClass<SomeOtherSubClass, SomeClass> {}
[could be]
class BaseClass<U> {
var propTwo: U
...
}
class SubClassOne<T>: BaseClass<SomeClass> {
var propOne: T
...
}
class SubClassTwo<T>: BaseClass<SomeClass> {
var propOne: T
...
}
The point is to keep common things in the base class and compose your specializations.
There's a fundamental misconception that SubclassOne and SubclassTwo are in the same inheritance hierarchy. Because of the generic type, they inherit from different base classes. You cannot mix and match them.
Think about it. With inheritance you should be able to use any subclass anywhere where you have the base class, so in your test example:
var testArray = [SubClassOne(), SubClassTwo()]
What type would the right hand side of the following expressions have to be?
testArray[0].prop = something
And this one
testArray[1].prop = something;
In SubClassOne, the type of prop is SomeSubClass and in SubClassTwo the type of prop must be SomeOtherSubClass.
The only way for you to get this to work is for prop to be declared as SomeBaseClass and that removes the necessity for BaseClass to be generic.
Edit
Why doesn't the protocol work?
The problem with the protocol is that you define the property as having the type of the base class but it is read/write. A property in an implementation of the protocol cannot fulfill the contract with a property that is specialised to one of the subclasses because other bits of code need to be able to assign any instance of the base class to the property.
protocol MyProtocol
{
var prop: BaseClass
}
struct MyImplementation: MyProtocol
{
var prop: SubClass
}
class BaseClass {}
class SubClass: BaseClass {}
class DifferentSubClass: BaseClass {}
var instance: MyProtocol = MyImplementation()
instance.prop = DifferentSubClass()
// Should be legal because the protocol says so but the type of prop in instance is SubClass.
I want to create a class, so that the it's subclasses after calling a function a, will receive a new object of the type Self. I'm insuring, that the subclasses will have the init() method.
In a way I want to clone the object, but actually it's more than that, since I want to create a clone with modified values of the original, so I don't really want to use swifty copy constructor syntax
Why it doesn't work? Definitely this:
func myCustomCopy(modificationCommand: Command) -> Test {
let newInt = modificationCommand.execute(self.myInt)
return Test(newInt: newInt)
}
is not what I want.
Example:
protocol Testable {
var myInt: Int { get set }
init(newInt: Int)
}
class Test: Testable {
var myInt = 10
required init(newInt: Int) { myInt = newInt }
func myCustomCopy(modificationCommand: Command) -> Self {
let newInt = modificationCommand.execute(self.myInt)
return self.init(newInt: newInt)
}
}
You may use the (dynamically typed) metatype returned by type(of:) to access an initializer of the concrete type of the metatype. Quoting the Language Reference - Metatypes
Use an initializer expression to construct an instance of a type from
that type’s metatype value. For class instances, the initializer
that’s called must be marked with the required keyword or the entire
class marked with the final keyword.
So in your case, you could use the metatype of self to call a required initializer of the concrete type of self, e.g.
func myCustomCopy() -> Self {
return type(of: self).init()
}
Note that, as specified in the quote above, since you are working with a non-final class, the initializer must be a required one.
I have a nested class defined like this:
#objc class A {
#objc class B{
}
}
and I need to instantiate A.B using NSClassFromString. I was able to do it for the simple class A but when I attach to the NSClassFromString parameter the .B string it just returns nil.
NSClassFromString("\(appName).A") // works...
NSClassFromString("\(appName).A.B") //doesn't work.
I suppose that since nested class are not available in Objective-c the NSClassFromString just doesn't work for nested classes... in that case is there another way to initialize a nested class from a string?
// EDIT
is curious how the inverse function NSStringFromClassreturns different format when executed for a standard class and a nested class:
"myApp.A" <---- STANDARD CLASS (A)
"_TtCC15myApp13A6B" <----- NESTED CLASS (A.B)
As you can see the format is completely different. What is that _TtCC15? Why the "." has been removed? I suppose that passing the class in that format to NSClassFromString should work.
I find that the following works in a playground (Xcode 8.2 / Swift 3):
// inheriting NSObject is required for `#objc`, at which point `#objc` is optional
class A: NSObject {
class B: NSObject {
override var description: String { return "foo" }
}
}
let str = NSStringFromClass(A.B.self)
guard let anyClass = NSClassFromString(str)
else { fatalError("no class") }
// cast to a type that defines `init()` so we can instantiate
guard let nsClass = anyClass as? NSObject.Type
else { fatalError("class isn't NSObject") }
// call `.init()`, not `nsClass()`; constructor syntax is for static types only
let instance = nsClass.init()
print(instance) // -> "foo"
The oddball class "name" string is because the ObjC runtime doesn't understand nested classes (or other kinds of types that Swift can define but ObjC can't) -- within Swift itself, such mangled names are how types, functions and such get uniquely defined. (For example, name mangling is also how function overloading works: func foo() and func foo(num: Int) -> Bool have different mangled names internally.)
The bridging machinery can still be made to dynamically resolve a class given its properly mangled Swift name, but Swift name mangling is an implementation detail and subject to change. (At least until Swift 4 locks down the ABI.) So it's safe to pass the result of NSStringFromClass to NSClassFromString within the same program, but not safe to (e.g.) record a mangled name once in testing, ship that mangled name as a string literal or resource in your app, and expect it to work with NSClassFromString later.
Instead, if you want your nested class (or any other Swift-defined class) to have a reliably known name for use in the ObjC runtime, give it an #objc(name) (as described in the docs):
#objc class A: NSObject {
#objc(A_B) class B: NSObject { /*...*/ }
}
guard let anyClass = NSClassFromString("A_B")
else { fatalError("no class") }
(Note this snippet won't run by itself -- the class A.B has to be referenced at least once somewhere in your process in order to be registered with the ObjC runtime. This could be as simple as putting let t = A.B.self somewhere in your app's startup code.)
I'm trying to store a type in a variable so that I can use it like a 1st class type later on.
class SomeModel {}
let someType = SomeModel.self
let array = Array<someType>()
In this case sure I could have done Array<SomeModel>() instead but I want to generalize it and let subclasses provide the value of someType.
However I get errors like someType isn't a type or use of undeclared type 'someType' on the last line.
If you need to store several type values in array, this works pretty well:
let array: [Any.Type] = [String.self, Int.self]
func someFunc<T>(model: T) -> Array<T> {
let array = Array<T>()
return array
}
let someType = SomeModel.self
let array = someFunc(someType())
Looks like this does what I want. The only drawback is that I have to create an instance of the desired type to pass. In this case its minimal overhead, but it just seems like a waste.
Another thing with using generics like this is it appears that the generic's possible types are computed at compile time, so at run time model.dynamicType doesn't necessarily match T. In most cases it will, but if you are doing any reflection driven stuff make sure to really check your use case well.
These days, this can be more easily and adaptably achieved by using .Type on a class or protocol. Note that only functions available to that root class or protocol will be accessible however, so you should ensure that a required initialiser of some kind is defined. For example:
protocol MyClass {
init(someValue: Int)
}
class MyWrapper {
let myClassType: MyClass.Type
init(classType: MyClass.Type) {
self.myClassType = classType
}
func new(with value: Int) -> MyClassType {
return MyClassType.init(someValue: value)
}
}
You can now initialise this rather silly factory class with a specific class implementing the MyClass protocol and when you call the new() function with an integer value it will generate a new instance of that class and return it.