Generic class initialization from protocol - ios

I want to have classes objects JSONSerialization. So my input is [String: Any] and from a documentation I know it may be either NSNull, NSString or NSNumber. So I've made a protocol:
protocol PlainValue { }
and all of those above conform to this protocol:
extension NSString: PlainValue { }
extension NSNull: PlainValue { }
extension NSNumber: PlainValue { }
Then I want to create a class storage that hold key-value like:
class KeyValue<T: PlainValue> {
let key: NSString
let value: T
init(key: NSString, value: T) {
self.key = key
self.value = value
}
}
And want to use it like this:
func parse(json: [String: Any]) {
...
if let value = json[key] as? PlainValue { // this should be `Any` but I want to check here if thats an PlainValue or embedded object
let obj = KeyValue<PlainValue>(key: key, value: value) // this currently not working
...
}
...
}
But my issue is how to declare this object creation on protocol level. If I do this like:
protocol PlainValue {
func convert(key: NSString) -> KeyValue<PlainValue>
}
I'm getting error:
Value of protocol type 'PlainValue' cannot conform to 'PlainValue'; only struct/enum/class types can conform to protocols
Make sense, since I'm already in protocol declaration. So I have a feeling that maybe this should be declared on KeyValue<T> level? But I'm not sure if I'm on a right path for this, since I'm getting compilers error on every approach I'm trying to make. Can anyone point me into right direction how to make it working?
Approach that I feel like it's closest to working is:
extension NSNumber: PlainValue {
func convert(key: NSString) -> KeyValue<NSNumber> {
return KeyValue<NSNumber>(key: key, value: self)
}
}
And similar for above on NSString and NSNull, but not sure how to declare this on protocol level to make this callable from my parsing function. Since
protocol PlainValue: JSONValue {
func convert(key: NSString) -> KeyValue<Self> // this claims that implementation does not match this declaration
func convert<T: PlainValue>(key: NSString) -> KeyValue<T> // same as above
associatedtype Object: PlainValue
func convert(key: NSString) -> KeyValue<Object> // this is working! But... then I cannot check if value is PlainValue in my parsing function, because of `Protocol 'PlainValue' can only be used as a generic constraint because it has Self or associated type requirements`
}

A protocol cannot conform to another protocol, the generic type T in KeyValue<T> must be a concrete type.
An alternative is a protocol extension with an associated type
extension NSString: PlainValue { }
extension NSNull: PlainValue { }
extension NSNumber: PlainValue { }
protocol PlainValue {
associatedtype ValueType : PlainValue = Self
func convert(key: NSString) -> KeyValue<ValueType>
}
extension PlainValue where ValueType == Self {
func convert(key: NSString) -> KeyValue<ValueType> {
return KeyValue<ValueType>(key: key, value: self)
}
}
class KeyValue<T: PlainValue> {
let key: NSString
let value: T
init(key: NSString, value: T) {
self.key = key
self.value = value
}
}
let stringResult = "Foo".convert(key:"bar")
let numberResult = NSNumber(value:12).convert(key:"baz")
let nullResult = NSNull().convert(key:"buz")

Not sure if I understand correctly what you would like to achieve, but based on what you wrote, you don't necessarily need generics here. The code below would resolve your issue, if there are no other factors that make generics necessary in your situation. Also, this way you won't need to declare the object creation on the protocol level.
class KeyValue {
let key: NSString
let value: PlainValue
init(key: NSString, value: PlainValue) {
self.key = key
self.value = value
}
}
Your original example did not work because you needed a concrete type there that conforms to PlainValue. In swift, only concrete types such as struct/enum/class can conform to protocols. For example let obj = KeyValue<NSString>(key: key, value: value) would work there, while using PlainValue would not.

Related

Swift - Injecting and storing protocols

I have a mapper as follows
protocol MapperProtocol {
associatedtype T
associatedtype U
func map(item: T) -> U?
}
And I want to inject it to a class as follows
protocol ParserProtocol {
associatedtype T
associatedtype U
func parse(from: T) -> U?
}
class TrackParser: ParserProtocol {
typealias T = String
typealias U = Track
private let mapper: MapperProtocol
init(mapper: MapperProtocol) {
self.mapper = mapper
}
func parse(from path: String) -> Track? {
guard let data = try? Data(contentsOf: URL(filePath: path)) else { return nil }
return mapper.map(item: data)
}
}
TrackParser will be initialised from somewhere else so it doesn't need to know the concrete type of the mapper.
When I want to implement it that way I get the following error.
Any ideas how to fix it?
Notice that the parse implementation requires that the mapper has T == Data and U == Track, but you haven't specified those constraint anywhere in TrackParser.
We can make T and U the primary associated types of MapperProtocol, so that the same-type requirements can be specified very easily as MapperProtocol<Data, Track>.
protocol MapperProtocol<T, U> { // Notice the "<T, U>"
associatedtype T
associatedtype U
func map(item: T) -> U?
}
Also, starting from Swift 5.7, existential types are required to be prefixed with "any", so you would write:
private let mapper: any MapperProtocol<Data, Track>
init(mapper: any MapperProtocol<Data, Track>) {
self.mapper = mapper
}

Issue with `Protocol can only be used as a generic constraint because it has Self or associated type requirements`

I'm trying to generate a ViewModel that conforms to a Protocol Protocoling, the protocol is generic, and has an associated type.
There are a few ViewModel's that conform to the protocol, so I am trying to create a factory for the viewModel.
I have encotuntered the following error by Swift:
Protocol can only be used as a generic constraint because it has Self or associated type requirements
Example code:
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> Protocoling {
switch type {
case .name: return NameViewModel(name: "Gil")
case .address: return AddressViewModel(address: ["Israel", "Tel Aviv"])
}
}
}
The error is in func viewModel(forType type: MyTypes) -> Protocoling.
Is there a way to solve this issue?
You can use a protocol with an associated type (PAT) as a return type like that without more constraint because the compiler needs to know which type to use.
In your case you must use a technic called the type erasure to be able to work with any Protocoling:
class AnyProtocoling: Protocoling {
let data: Any
init<U: Protocoling>(_ viewModel: U) {
self.data = viewModel.data as Any
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> AnyProtocoling {
switch type {
case .myName:
return AnyProtocoling(NameViewModel(name: "Gil"))
case .myAddress:
return AnyProtocoling(AddressViewModel(address: ["Israel", "Tel Aviv"]))
}
}
}
This will allow you to "erase" the associated type of your protocol and return an Any version of your view model.
In order to understand why the PAT needs to work like that I like the following example: the Equatable protocol (which is a PAT):
static func ==(lhs: Self, rhs: Self) -> Bool
This function uses the Self type which is an associated type. You want to use it in the next generic function:
func areEquals(left: Equatable, right: Equatable) -> Bool {
return left == right
}
Here the compiler will trigger this error: Protocol can only be used as a generic constraint because it has Self or associated type requirements. Why? Lets take this example:
struct tomato: Equatable {}
struct salad: Equatable {}
areEquals(left: tomato(), right: salad())
There is no reason to compare tomatoes and salads. The associated type Self is not the same. To avoid this error in this case you need to constraint the Self type as following:
func areEquals<T: Equatable>(left: T, right: T) -> Bool
Now you know the T are equatables and with the same associated types.
This is very simple to fix, in your concrete factory implementation you just need to specify a generic for your factory that has to conform to protocol protocoling, see code below :
Swift 4
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory<T> where T: Protocoling {
func viewModel(forType type: MyTypes) -> T? {
switch type {
case .myName: return NameViewModel(name: "Gil") as? T
case .myAddress: return AddressViewModel(address: ["Israel", "Tel Aviv"]) as? T
default: return nil /* SUPPORT EXTENSION WITHOUT BREAKING */
}
}
}
It's a first step into the wonderful world of abstraction with protocols. You really create some amazing things with it. Though, I have to say, that personally it's not as intuitive as something like inheritance, it's a great little mind bending puzzle for creating decoupled and abstract systems, that are actually far more powerful.
Swift is a great introductory language, and I believe that it's protocol and extension mechanisms make it one of the more complex and interesting languages.
This design pattern is a great way setting up things like dependency injection.

Having two generic methods, one with a constraint. Swift 3.1 started preferring one without constraint.

I have a generic class with two generic methods one of which has a requirement to conform to a protocol. After updating to XCode 8.3.2 the method with no constraint is always executed even if the type conforms to specified protocol.
protocol AProtocol {
init(dict: dict) {
}
}
struct AStruct: AProtocol {
init(dict: dict) {
}
}
struct Container<T> {
var object: Any?
init<T: AProtocol>(dict: [String : Any], type: T) {
object = T(dict: dict)
}
init<T>(dict [String : Any], type: T) {
object = dict
}
}
class AClass<S> {
var value: S
init<S>(value: S) {
self.value = value
let container = Container(dict: [], type: S.self)
}
}
So in the initializer of AClass the container's initializer without constraint will always be called regardless whether S conforms to AProtocol or not.
My question is does anyone know workaround or at least know what has changes with newer swift versions, because this used to work properly on swift 3.0?

why need to use NSObject?

i am confused about that should i need to used NSObject to swift3 . if so please guide me also is it best practice to used NSNumber
class App: NSObject {
var id: NSNumber?
var name: String?
var category: String?
var imageName: String?
var price: NSNumber?
var screenshots: [String]?
var desc: String?
var appInformation: AnyObject?
// override func setValue(_ value: Any?, forKey key: String) {
// if key == "description" {
// self.desc = value as? String
// } else {
// super.setValue(value, forKey: key)
// }
// }
}
Note : could you tell me please why need to use NSObject ? what is the advantage ?
NSObject class:
This class is the root class of most Objective-C class hierarchies, from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects. (source).
AnyObject protocol: its a implicit confirmation of any object.
You use AnyObject when you need the flexibility of an untyped object or when you use bridged Objective-C methods and properties that return an untyped result. AnyObject can be used as the concrete type for an instance of any class, class type, or class-only protocol.
AnyObject can also be used as the concrete type for an instance of a type that bridges to an Objective-C class.
let s: AnyObject = "This is a bridged string." as NSString
print(s is NSString)
// Prints "true"
let v: AnyObject = 100 as NSNumber
print(type(of: v))
// Prints "__NSCFNumber"
The flexible behavior of the AnyObject protocol is similar to Objective-C’s id type. For this reason, imported Objective-C types frequently use AnyObject as the type for properties, method parameters, and return values. (source)
So you can use NSNumber variable as AnyObject which can be later type cast implicitly accordingly.
You're overriding a super class method:
override func setValue(_ value: Any?, forKey key: String) { }
So, if your class doesn't extends NSObject class, how could overrides its super class method?
You could not extend NSObject class, but you'll have to remove override keyword from code since setValue would become a method of your custom class. If you do it, you'll not be able to call super.setValue(value, forKey: key) obviously.

Swift generic class which could take Array of elements or single element

I would like to make generic class which will be able to take Parsable type, or Array of Parsable type. Logic for both are almost the same so i don't want to make two different class for this operation. Is it possible to solve it using Swift generics, or protocol associatedtype types?
protocol Parsable: class {
associatedtype Type
static func objectFromDictionary(dictionary: Dictionary<String, AnyObject>, inContext context: NSManagedObjectContext) -> Type?
func importFromDictionary(dictionary: Dictionary<String, AnyObject>)
}
class ParseOperation<T: Parsable>: NSOperation {
func execute() -> T {
}
}
class ParseOperation<T where T: SequenceType, T.Generator.Element == Parsable>: NSOperation {
func execute() -> T {
// Do parsing
}
}
This i how i would like to work:
class ParseOperation<T where T: SequenceType, T.Generator.Element == Parsable OR T: Parsable>: NSOperation {
func execute() -> T {
// Do parsing
}
}
In my current implementation i am using enum which looks little bit ugly:
class ParseOperation<T where T: NSManagedObject, T:Parsable>: NSOperation {
var responseToParse: AnyObject?
var parseType: ParseType
var parsedObjects: [T]?
init(parseType: ParseType) {}
func execute() {
var objects: [NSManagedObject] = []
if self.parseType == .Single {
if let responseToParse = self.responseToParse as? Dictionary<String, AnyObject>,
let parsedObject = T.objectFromDictionary(responseToParse, inContext: localContext) {
objects.append(parsedObject)
}
} else if self.parseType == .Array {
if let responseToParse = self.responseToParse as? Array<Dictionary<String, AnyObject>> {
for dictionary in responseToParse {
if let parsedObject = T.objectFromDictionary(dictionary, inContext: localContext) {
objects.append(parsedObject)
}
}
}
}
self.parsedObjects = objects
...
}
}
I modified #RonaldMartin 's answer to show how ParsableArray might help you. It don't need to take input of Parsable elements, just implement parse function this way:
protocol Parsable {
associatedtype T
static func parse(input: AnyObject) -> T?
}
struct ParsableArray<TElement where TElement: Parsable>: Parsable {
static func parse(input: AnyObject) -> [TElement.T]? {
guard let arrayInput = input as? [AnyObject] else {
return nil
}
return arrayInput.flatMap(TElement.parse)
}
}
I've renamed objectFromDictionary to parse because it's need to take AnyObject not the Dictionary to be able to parse array. You can add context or whatever you like to parse method, of course.
If Parsable done this way then ParseOperation becomes very simple:
class ParseOperation<T where T: Parsable>: NSOperation {
let input: AnyObject
var result: T.T?
init(input: AnyObject) {
self.input = input
}
override func main() {
result = T.parse(input)
}
}
Then, you can parse arrays this way (note: this is only to demonstrate how to create ParseOperation; S is just some Parsable struct):
let op = ParseOperation<ParsableArray<S>>(input: [["1": 5, "2": 6], ["3": 10]])
op.main()
var r: [S]? = op.result
I hope, this will help.
As far as I know, the type constraint system is not designed to handle OR constraints. However, it should still be possible to do what you're asking.
One approach is to represent both singleton Parsables and collections of Parsables under a single type that you can use to constrain ParseOperation. The neatest way to do this would be to extend Array (or SequenceType, CollectionType, etc.) to conform to the Parsable type as well, but this is not yet possible as of Xcode 7.3. You can use the same workaround from that linked question and add an intermediate class to represent Parsable arrays:
class ParsableArray: Parsable {
let array: [Parsable]
init(array: [Parsable]) {
self.array = array
}
// Parsable conformance...
}
Now, you can just use the original protocol for your type constraint:
class ParseOperation<T: Parsable>: NSOperation {
func execute() -> T {
// Do parsing
}
}
Ideally you should be able to do this:
// This DOES NOT compile as of XCode 7.3
extension Array: Parsable where Element: Parsable {
// Parsable implementation
}
However it currently doesn't work.
Currently you have to rely on a wrapper struct:
struct ParsableArray<Element: Parsable>: Parsable {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
// Parsable implementation
}
You can also implement a convenience method for [Parsable] arrays:
extension Array where Element: Parsable {
func toParsable() -> ParsableArray<Element> {
return ParsableArray(self)
}
}
So you could execute your method like this:
let array = [Parsable]()
parse(array.toParsable()) // Equivalent to: parse(ParsableArray(array))

Resources