I need to pass models of type decodable and access title and subtitle from it so because Decodable doesn't have title and subtitle properties I implemented an extension for decodable and added a title and subtitle properties to Decodable So any object of type Decodable could write decodableObject.title so when I pass object which conform to decodable and contains title and subtitle properties I need then to access the data of it not Docodable object data but what happens that it just access Decodable extension properties so to access my target object I should make downcasting to this class which then I should write implementation for each model type
//decalring struct which conforms to Decodable
struct TestModel:Decodable {
var title:String?
}
//added property
extension Decodable {
var title:String?{
return "Decodable base"
}
}
func setup(){
var testModel = TestModel()
testModel.title = "Subclass"
checkProperties(model: testModel, mod: TestModel.self)
}
func checkProperties<T:Decodable>(model:Any,mod:T.Type){
typealias MMM = T
let title = (model as! MMM).title
print("type is \(title)")
}
// the resutl will be "Type is Decodable Base"
I need the expected result to be
// the result will be "Type is subclass"
Well, maybe this helps you:
protocol HasTitle {
var title: String? { get }
}
extension Decodable {
var title:String? {
return "Decodable base"
}
}
struct TestModel: Decodable, HasTitle {
var title: String?
}
func checkProperties<T:HasTitle>(model:Any,mod:T.Type){
typealias MMM = T
let title = (model as! MMM).title
print("type is \(title)")
}
EDIT
This makes usage of a concept called Retroactive Modeling. The rationale is separating that property in a protocol, which I called HasTitle. The trick is making an extension to Decodable which has a property title declared the same way you expect in HasTitle protocol. Then, whichever type who conforms to Decodable got that title property by default and you need just to declare HasTitle conformance. However, you're still free to override it title property.
Related
I'm going around in circles trying to get Hashable to work with multiple struct that conform to the same protocol.
I have a protocol SomeLocation declared like this:
protocol SomeLocation {
var name:String { get }
var coordinates:Coordinate { get }
}
Then I create multiple objects that contain similar data like this:
struct ShopLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
init(from decoder: Decoder) throws {
...
}
}
struct CarLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
init(from decoder: Decoder) throws {
...
}
}
I can later use these in the same array by declaring:
let locations: [SomeLocation]
The problem is, I create an MKAnnotation subclass and need to use a custom Hashable on the SomeLocation objects.
final class LocationAnnotation:NSObject, MKAnnotation {
let location:SomeLocation
init(location:SomeLocation) {
self.location = location
super.init()
}
}
override var hash: Int {
return location.hashValue
}
override func isEqual(_ object: Any?) -> Bool {
if let annot = object as? LocationAnnotation
{
let isEqual = (annot.location == location)
return isEqual
}
return false
}
This gives me 2 errors:
Value of type 'SomeLocation' has no member 'hashValue' Binary operator
'==' cannot be applied to two 'SomeLocation' operands
So I add the Hashable protocol to my SomeLocation protocol:
protocol SomeLocation: Hashable {
...
}
This removes the first error of hashValue not being available, but now I get an error where I declared let location:SomeLocation saying
Protocol 'SomeLocation' can only be used as a generic constraint because it has Self or associated type requirements
So it doesn't look like I can add Hashable to the protocol.
I can add Hashable directly to each struct that implements the SomeLocation protocol, however that means I need to use code like this and keep updating it every time I might make another object that conforms to the SomeLocation protocol.
override var hash: Int {
if let location = location as? ShopLocation
{
return location.hashValue
}
return self.hashValue
}
I have tried another way, by making a SomeLocationRepresentable struct:
struct SomeLocationRepresentable {
private let wrapped: SomeLocation
init<T:SomeLocation>(with:T) {
wrapped = with
}
}
extension SomeLocationRepresentable: SomeLocation, Hashable {
var name: String {
wrapped.name
}
var coordinates: Coordinate {
wrapped.coordinates
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(coordinates)
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.name == rhs.name && lhs.coordinates == rhs.coordinates
}
}
however when I try to use this in the LocationAnnotation class like
let location: SomeLocationRepresentable
init(location:SomeLocation) {
self.location = SomeLocationRepresentable(with: location)
super.init()
}
I get an error
Value of protocol type 'SomeLocation' cannot conform to 'SomeLocation'; only struct/enum/class types can conform to protocols
Is it possible to achieve what I am trying to do? Use objects that all conform to a protocol and use a custom Hashable to compare one to the other?
Deriving the protocol from Hashable and using a type eraser might help here:
protocol SomeLocation: Hashable {
var name: String { get }
var coordinates: Coordinate { get }
}
struct AnyLocation: SomeLocation {
let name: String
let coordinates: Coordinate
init<L: SomeLocation>(_ location: L) {
name = location.name
coordinates = location.coordinates
}
}
You then can simply declare the protocol conformance on the structs, and if Coordinate is already Hashable, then you don't need to write any extra hashing code code, since the compiler can automatically synthesize for you (and so will do for new types as long as all their properties are Hashable:
struct ShopLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
}
struct CarLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
}
If Coordinate is also Codable, then you also can omit writing any code for the encoding/decoding operations, the compile will synthesize the required methods (provided all other properties are already Codable).
You can then use the eraser within the annotation class by forwardingn the initializer constraints:
final class LocationAnnotation: NSObject, MKAnnotation {
let location: AnyLocation
init<L: SomeLocation>(location: L) {
self.location = AnyLocation(location)
super.init()
}
override var hash: Int {
location.hashValue
}
override func isEqual(_ object: Any?) -> Bool {
(object as? LocationAnnotation)?.location == location
}
}
I have parent class called "Item", and child class called "YTListItem". I want to have a functions called instantiate() which will be implemented in each child class, and return a view controller. The type of the view controller will be different depending on which child class is calling the instantiate() method. My issue is that swift does not seem to recognize me overriding the function if it has different parameters or return types.
The error occurs when I override the function, I get the error "Method does not override any method from its superclass".
class Item {
var name: String
var description: String
init(name: String = "Test text", description: String = "Test description of the item") {
self.name = name
self.description = description
}
func instantiate() {}
}
class YTListItem: Item {
var address: String
init(name: String = "Test text", description: String = "Test text", address: String = "SivTGfXxYz0") {
self.address = address
super.init(name: name, description: description)
}
override func instantiate(storyboard: UIStoryboard) -> YTDescViewController? {
let vc = storyboard.instantiateViewController(identifier: "desc") as? YTDescViewController
return vc
}
}
Is there a way to do this? I've seen mentions of protocols when searching how to make it work but I'm new to swift so I'm hoping to be able to get through it using methods I've already learned.
First of all I don't think you should be instantiating a ViewController from you model class. Instead you should be injecting your model to the ViewController. But for your particular scenario you can just return a UIViewController from instantiate function rather than it's subclass (YTDescViewController). And I think you should use protocols for your scenario something like this:
protocol Item {
var name: String {get set}
var description: String {get set}
var storyboard : UIStoryboard {get set}
func instatntiate() -> UIViewController
}
extension Item {
var viewControllerID : String {
return "desc"
}
}
struct YTListItem: Item {
var name: String
var description: String
var storyboard: UIStoryboard
func instatntiate() -> UIViewController {
return storyboard.instantiateViewController(identifier: viewControllerID)
}
}
You can also use associatedType to customize the return type of instantiate (or the parameters) in Item protocol. But in that case you would need to implement type erasures to hold generic reference to class/struct objects that implement Item protocol.
I need to save a class in realm, this class contains a generic type as the following:-
#objcMembers
class ClassA<T: Object & Codable>: Object, Codable {
dynamic var key: String?
dynamic var type: T?
override class func primaryKey() -> String {
return "key"
}
}
#objcMembers
class ClassB: Object, Codable {
}
let object: ClassA<ClassB>
realm.add(object, update: true)
But this code unfortunately save only ClassA.key in realm and ignors ClassA.type.
I have googled about this issue but without any result unfortunately. It seems that nobody uses a generic type inside a realm class.
Finally I’ve reached the proper solution. I have used the advantage of the generic realm list type, then I have modified the “type” variable to be a list of generic instead of single generic object. It is now working fine with me. Now I can easily use a generic type inside a class and save this class to realm without any problem.
#objcMembers
class ClassA<T: Object & Codable>: Object, Codable {
dynamic var key: String?
var type: List<T>()
override class func primaryKey() -> String {
return "key"
}
}
#objcMembers
class ClassB: Object, Codable {
}
let object: ClassA<ClassB>
realm.add(object, update: true)
#objcMembers only exposes compatible members to Objective-C. Swift generics do not get included with this as they are not compatible with Objective-C. Realm works off of exposing the members to Objective-C.
Possible solution:
#objcMembers
class ClassA<T: Object & Codable>: Object, Codable {
dynamic var key: String?
var type: T?
override class func primaryKey() -> String {
return "key"
}
}
#objcMembers
class ClassB: Object, Codable {
}
class ClassABComposite: ClassA<ClassB> {
// T can be realized as ClassB here
override var type: ClassB? {
set {
backingType = newValue
}
get {
return backingType
}
}
// This will work because it's not generic
#objc dynamic var backingType: ClassB?
}
I have a Codable class with a variable that holds a dictionary with String keys and values that may be Strings, Ints or custom structs conforming to Codable. My question is:
How do I define a dictionary with values that are Codable?
I hoped it would be enough to say
var data: [String: Codable] = [:]
but apparently that makes my class no longer conform to Codable. I think the problem here is the same as I had here, where I am passing a protocol rather than an object constrained by the protocol
Using JSON Encoder to encode a variable with Codable as type
So presumably I would need a constrained generic type, something like AnyObject<Codable> but that's not possible.
EDIT: Answer
So since this can't be done as per the accepted answer, I am resorting to a struct with dictionaries for each data type
struct CodableData {
var ints: [String: Int]
var strings: [String: String]
//...
init() {
ints = [:]
strings = [:]
}
}
var data = CodableData()
A Swift collection contains just one type, and for it to be Codable, that type would need to be Codable — not Codable the type, but an adopter of the Codable protocol. You need the type here to be some one specific Codable adopter.
Therefore the only way to do what you're describing would be basically to invent some new type, StringOrIntOrStruct, that wraps a String or an Int or a struct and itself adopts Codable.
But I don't think you're doing to do that, as the effort seems hardly worth it. Basically, you should stop trying to use a heterogeneous Swift collection in the first place; it's completely against the spirit of the language. It's a Bad Smell from the get-go. Rethink your goals.
I think the compiler is simply not smart enough to infer the Codable implementation automatically. So one possible solution is to implement Codable by hand:
class Foo: Codable {
var data: [String:Codable] = [:]
func encode(to encoder: Encoder) throws {
// ...
}
required init(from decoder: Decoder) throws {
// ...
}
}
I don’t know whether you can change the data type to help the compiler do the work for you. At least you can pull the hand-coding to the problematic type:
enum Option: Codable {
case number(Int)
case string(String)
func encode(to encoder: Encoder) throws {
// ...
}
init(from decoder: Decoder) throws {
if let num = try? Int(from: decoder) {
self = .number(num)
} else if let str = try? String(from: decoder) {
self = .string(str)
} else {
throw something
}
}
}
class Foo: Codable {
var data: [String:Option] = [:]
}
Have any of you tried Generics in Swift?
Look:
public struct Response<T> where T : Codable {
let ContentEncoding : String?
let ContentType : String?
let JsonRequestBehavior : Int?
let MaxJsonLength : Int?
let RecursionLimit : Int?
let data : Data?
public struct Data : Codable {
let response : String?
let status : String?
let details : [T]?
}
}
I have a doubt about protocols in Swift about the use of var and the keywords { get set }.
From Apple documentation:
If a protocol requires a property to be gettable and settable, that
property requirement cannot be fulfilled by a constant stored property
or a read-only computed property. If the protocol only requires a
property to be gettable, the requirement can be satisfied by any kind
of property, and it is valid for the property to be also settable if
this is useful for your own code.
Property requirements are always declared as variable properties,
prefixed with the var keyword. Gettable and settable properties are
indicated by writing { get set } after their type declaration, and
gettable properties are indicated by writing { get }.
I can't understand why I can't use let. A var in a protocol with only get isn't just a let?
Something like this:
protocol someProtocol
{
var someProperty: String { get }
}
it would not be just:
protocol someProtocol
{
let someProperty: String
}
I'm missing something?
"A var in a protocol with only get isn't just a let?" No. A let indicates a constant. But that is not the case here. Consider the following:
protocol SomeProtocol {
var someProperty: String { get }
}
class SomeClass : SomeProtocol {
var someProperty: String = ""
func cla () {
someProperty = "asd"
}
}
let someInstance = SomeClass()
print(someInstance.someProperty) // outputs ""
someInstance.cla()
print(someInstance.someProperty) // outputs "asd"
The protocol specifies what the conforming class shows to the outside - some property of type String named someProperty which you can at least get.
If the protocol specifies { get } your class can choose to conform via let someProperty: String = "" but it can similarly choose to conform via the above code. If on the other hand the protocol specifies { get set } you cannot use let in the implementation but have to make it set-able as well.
A protocol simply cannot define that a value has to be constant - neither should it, that is an implementation detail that has to be taken care (or decided about) by the class / struct that implements it.
The difference is between
protocol MyProtocol {
let someProperty: String
}
which makes no sense — a protocol isn't supposed to dictate how someProperty is defined/stored, only that it's available as a property. It could be either a computed or stored property, but that's for the implementer to decide, not the protocol itself.
and
protocol MyProtocol {
var someProperty: String { get } // abstract interface
}
struct MyStruct: MyProtocol {
let someProperty: String // concrete implementation: stored property
}
struct OtherStruct: MyProtocol {
let i: Int
var someProperty: String { return "\(i)" } // concrete implementation: computed property
}
which is perfectly allowed!
I think a protocol can require that a structure has something, but it can't restrict functionality of struct or object. That shouldn't prevent you from doing what you'd probably like to do in code, for example using a var in the protocol and a let for the implementation is acceptable.
protocol MyProtocol {
var trythis: Int { get }
}
struct MyStructure: MyProtocol {
let trythis: Int
}
A property declared with let is considered read-only under the hood. For this reason, a protocol can require that a property be a constant by setting it read-only. This deduction can be verified using some of the Objc runtime functions property_getAttributes.
protocol SomeProtocol {
var someTypeProperty: Int { get }
}
struct Foo: SomeProtocol {
let someTypeProperty: Int
}