Extending a constrained protocol for an array argument is not possible - swift

I'm going to explain it by an example. We have a protocol for force having firstName and lastName like:
protocol ProfileRepresentable {
var firstName: String { get }
var lastName: String { get }
}
the type we are going to use have these two, but in an optional form:
struct Profile {
var firstName: String?
var lastName: String?
}
so after conforming to the ProfileRepresentable, we will extend the ProfileRepresentable and try to return the value and a default one for nil state:
extension Profile: ProfileRepresentable { }
extension ProfileRepresentable where Self == Profile {
var firstName: String { self.firstName ?? "NoFirstName" }
var lastName: String { self.lastName ?? "NoLastName" }
}
So far so good
Now there is a similar flow for a list of Profiles.
protocol ProfilerRepresentable {
var profiles: [ProfileRepresentable] { get }
}
struct Profiler {
var profiles: [Profile]
}
First issue
conforming to ProfilerRepresentable does NOT automatically done the implementation as expected (since Profile already conforms to ProfileRepresentable)
extension Profiler: ProfilerRepresentable { }
Second Issue
Following the previous pattern, extending ProfilerRepresentable is not working as expected and it raises a warning:
⚠️ All paths through this function will call itself
extension ProfilerRepresentable where Self == Profiler {
var profiles: [ProfileRepresentable] { self.profiles }
}
How can I achieve the goal for arrays by the way ?

Here is possible solution. Tested with Xcode 12 / swift 5.3
protocol ProfilerRepresentable {
associatedtype T:ProfileRepresentable
var profiles: [T] { get }
}
extension Profiler: ProfilerRepresentable { }
struct Profiler {
var profiles: [Profile]
}

[Profile] is not a subtype of [ProfileRepresentable]. (See Swift Generics & Upcasting for a related but distinct version of this question.) It can be converted through a compiler-provided copying step when passed as a parameter or assigned to a variable, but this is provided as a special-case for those very common uses. It doesn't apply generally.
How you should address this depends on what precisely you want to do with this type.
If you have an algorithm that relies on ProfilerRepresentable, then Asperi's solution is ideal and what I recommend. But going that way won't allow you to create a variable of type ProfileRepresentable or put ProfileRepresentable in an Array.
If you need variables or arrays of ProfilerRepresentable, then you should ask yourself what these protocols are really doing. What algorithms rely on these protocols, and what other reasonable implementations of ProfileRepresentable really make sense? In many cases, ProfileRepresentable should just be replaced with a simple Profile struct, and then have different init methods for creating it in different contexts. (This is what I recommend if your real problem looks a lot like your example, and Asperi's answer doesn't work for you.)
Ultimately you can create type erasers (AnyProfile), but I suggest exploring all other options (particularly redesigning how you do composition) first. Type erasers are perfect if your goal is to erase a complicated or private type (AnyPublisher), but that generally isn't what people mean when they reach for them.
But designing this requires knowing a more concrete goal. There is no general answer that universally applies.
Looking at your comments, there no problem with having multiple types for the same entity if they represent different things. Structs are values. It's fine to have both Double and Float types, even though every Float can also be represented as a Double. So in your case it looks like you just want Profile and PartialProfile structs, and an init that lets you convert one to the other.
struct Profile {
var firstName: String
var lastName: String
}
struct PartialProfile {
var firstName: String?
var lastName: String?
}
extension Profile {
init(_ partial: PartialProfile) {
self.firstName = partial.firstName ?? "NoFirstName"
self.lastName = partial.lastName ?? "NoLastName"
}
}
extension PartialProfile {
init(_ profile: Profile) {
self.firstName = profile.firstName
self.lastName = profile.lastName
}
}
It's possible that you have a lot of these, so this could get a bit tedious. There are many ways to deal with that depending on exactly the problem you're solving. (I recommend starting by writing concrete code, even if it causes a lot of duplication, and then seeing how to remove that duplication.)
One tool that could be useful would be Partial<Wrapped> (inspired by TypeScript) that would create an "optional" version of any non-optional struct:
#dynamicMemberLookup
struct Partial<Wrapped> {
private var storage: [PartialKeyPath<Wrapped>: Any] = [:]
subscript<T>(dynamicMember member: KeyPath<Wrapped, T>) -> T? {
get { storage[member] as! T? }
set { storage[member] = newValue }
}
}
struct Profile {
var firstName: String
var lastName: String
var age: Int
}
var p = Partial<Profile>()
p.firstName = "Bob"
p.firstName // "Bob"
p.age // nil
And a similar converter:
extension Profile {
init(_ partial: Partial<Profile>) {
self.firstName = partial.firstName ?? "NoFirstName"
self.lastName = partial.lastName ?? "NoLastName"
self.age = partial.age ?? 0
}
}
Now moving on to your Array problem, switching between these is just a map.
var partials: [Partial<Profile>] = ...
let profiles = partials.map(Profile.init)
(Of course you could create an Array extension to make this a method like .asWrapped() if it were convenient.)
The other direction is slightly tedious in the simplest approach:
extension Partial where Wrapped == Profile {
init(_ profile: Profile) {
self.init()
self.firstName = profile.firstName
self.lastName = profile.lastName
self.age = profile.age
}
}
If there were a lot of types, it might be worth it to make Partial a little more complicated so you could avoid this. Here's one approach that allows Partial to still be mutable (which I expect would be valuable) while also allowing it to be trivially mapped from the wrapped instances.
#dynamicMemberLookup
struct Partial<Wrapped> {
private var storage: [PartialKeyPath<Wrapped>: Any] = [:]
private var wrapped: Wrapped?
subscript<T>(dynamicMember member: KeyPath<Wrapped, T>) -> T? {
get { storage[member] as! T? ?? wrapped?[keyPath: member] }
set { storage[member] = newValue }
}
}
extension Partial {
init(_ wrapped: Wrapped) {
self.init()
self.wrapped = wrapped
}
}
I don't love this solution; it has a weird quirk where partial.key = nil doesn't work to clear a value. But I don't have a nice fix until we get KeyPathIterable. But there are some other routes you could take depending on your precise problem. And of course things can be simpler if Partial isn't mutable.
The point is that there's no need for protocols here. Just values and structs, and convert between them when you need to. Dig into #dynamicMemberLookup. If your problems are very dynamic, then you may just want more dynamic types.

Related

Is it possible to change Swift String Interpolation for the String typealias?

I have a lot of DTOs in my app which log some field. That field should not be logged because the data is kind of sensitive.
The model looks like this:
typealias HiddenFieldType = String
struct DTO1 {
var field1_1: String
var fieldToHide: HiddenFieldType
var field1_2: String
var field1_3: String
}
struct DTO2 {
var field2_1: String
var field2_2: String
var fieldToHide: HiddenFieldType
var field2_3: String
}
the code which outputs the data is like this (actually it's os_log in a real app):
func test() {
let dto1 = DTO1(field1_1: "1_1", fieldToHide: "super-secret-1", field1_2: "1_2", field1_3: "1_3")
let dto2 = DTO2(field2_1: "2_1", field2_2: "2_2", fieldToHide: "super-secret-2", field2_3: "2_3")
print("Test1: dto1=\(dto1) dto2=\(dto2)")
}
Approach #1
It seems the field can be hidden in DTO1 with such code:
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: DTO1) {
appendInterpolation("field1_1: \(value.field1_1), fieldToHide: 🤷‍♀️, field1_2: \(value.field1_2), field1_3: \(value.field1_3)")
}
}
However, the solution is neither scalable nor maintainable:
the same extension should be added for each DTO
each field should be included into appendInterpolation - a lot of boilerplate
if a new field is added to some DTO, we may forget to update appendInterpolation etc
Approach #2
I tried to add interpolation for HiddenFieldType (assuming it's a type, just like DTO1...):
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: HiddenFieldType) {
appendInterpolation("🤷‍♀️")
}
}
But this solution doesn't work at all:
the compiler says "Function call causes an infinite recursion"
and it actually causes an infinite recursion
when changing appendInterpolation to appendLiteral, there's no recursion, but "super-secret-1" is not hidden
Approach #3
I tried overriding DefaultStringInterpolation, conforming to ExpressibleByStringLiteral/ExpressibleByStringInterpolation, but it doesn't work: the compiler says that HiddenFieldType is String, and Conformance of 'String' to protocol 'ExpressibleByStringLiteral' was already stated in the type's module 'Swift'
The only approach I can imagine is changing typealias HiddenFieldType = String to struct HiddenFieldType { let value: String }, so the HiddenFieldType becomes a "real" type.
Approach #4
Then such code doesn't cause an infinite recursion anymore, but doesn't works either (the value is unhidden)
struct HiddenFieldType {
let value: String
}
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: HiddenFieldType) {
appendInterpolation("🤷‍♀️")
}
}
Approach #5
This code finally works:
struct HiddenFieldType {
let value: String
}
extension HiddenFieldType: CustomStringConvertible {
var description: String {
"🤷‍♀️"
}
}
As I can't imagine any better, for now I'd use this approach, but it also has some slight scalability issues, as I must update each DTO's initializing point:
from
let dto1 = DTO1(field1_1: "1_1", fieldToHide: "super-secret-1", field1_2: "1_2", field1_3: "1_3")
to
let dto1 = DTO1(field1_1: "1_1", fieldToHide: .init(value: "super-secret-1"), field1_2: "1_2", field1_3: "1_3")
and I hoped to only add some extension in the file which contains typealias HiddenFieldType = String, and not to update the entire code.
The questions
Is it possible to hide the value of HiddenFieldType without changing it from typealias to struct, and without updating each DTO?
Is there any better approach than 5?
Thanks in advance
Is it possible to hide the value of HiddenFieldType without changing it from typealias to struct
I think you're attempting to use the wrong tool for the job here. A typealias is just a name change, and it sounds like you want something that acts fundamentally different than a String (i.e. one gets printed when passed into an os_log call and one doesn't). You won't be able to write logic that treats a String different from its typealias; the compiler doesn't differentiate between them.
Is it possible to make your DTOs classes instead of structs? (EDIT: see below, you can keep them as structs and just use a protocol extension) If so, you could use reflection on a superclass to accomplish this without having to manually specify the description for every different DTO.
struct HiddenFieldType {
let value: String
}
open class DTO: CustomStringConvertible {
public var description: String {
Mirror(reflecting: self).children.compactMap { $0.value as? String }.joined(separator: "\n")
}
}
final class DTO1: DTO {
let field1_1: String
let field1_2: String
let fieldToHide: HiddenFieldType
init(field1_1: String, field1_2: String, fieldToHide: HiddenFieldType) {
self. field1_1 = field1_1
self. field1_2 = field1_2
self. fieldToHide = fieldToHide
}
}
Note that I'm including all Strings in the description but, if you have types other than String and HiddenFieldType that you want to log, you could always just filter out the HiddenFieldTypes specifically.
Personally, I'd be hesitant to rely on reflection for any critical code but other people have more tolerance for it so its a judgement call.
EDIT:
You don't need to use inheritance to accomplish this. Instead of a superclass, DTO should be a protocol that conforms to CustomStringConvertible:
protocol DTO: CustomStringConvertible {}
extension DTO {
public var description: String {
Mirror(reflecting: self).children.compactMap { $0.value as? String }.joined(separator: "\n")
}
}

How can I implement a class structure that allows to an instance of a subclass to change to an instance of another subclass of the same class

I'm trying an item object that can be a note (just a string of text as property) or a task, so having a status, priority,a due date, etc. etc. as additional properties.
It would be possible transforming at runtime a note item in a task item and vice-versa.
I'm thinking how to implement this, I started defining a class structure having "item" as principal class and two subclasses (note and task) but I can't understand how managing the transition between the two subclasses
Can you help me?
This is not possible in Swift, at least not in any way that you'd use as a general tool. (It is possible in ObjC via isa-swizzling, and that can be bridged into Swift, but even in ObjC you'd never want to use it for this kind of problem. It's a very tricky tool, not a general purpose solution to types.)
Instead of complex class substitution, you want to redesign this. There are several approaches. You can just convert types (see the init(copying:) methods):
protocol Item {
var id: UUID { get }
var name: String { get set }
var contents: String { get set }
}
struct Note: Item {
let id: UUID
var name: String
var contents: String
init(copying item: Item) {
self.id = item.id
self.name = item.name
self.contents = item.contents
}
}
struct Task: Item {
let id: UUID
var name: String
var contents: String
var complete: Bool = false
var dueDate: DateComponents?
init(copying item: Item) {
self.id = item.id
self.name = item.name
self.contents = item.contents
}
}
Or, you can separate out additional properties and swap them (this approach is ideal if Item is a class, but I'd highly recommend trying to make Item a struct).
protocol ItemProperties{}
struct Item {
let id: UUID
var name: String
var contents: String
var properties: ItemProperties
}
// No additional properties for notes
struct Note: ItemProperties {}
struct Task: ItemProperties {
var complete: Bool = false
var dueDate: DateComponents?
}
var item = Item(id: UUID(), name: "", contents: "", properties: Note())
// convert to task
item.properties = Task(complete: false, dueDate: nil)
// Convert back to note
item.properties = Note()
That said, if you only have Tasks and Notes, I'd avoid overthinking this until you have a clear sense of what you'd add in the future. I'd consider just a simple struct with a type enum:
enum ItemType {
case note
case task(complete: Bool, due: DateComponents)
}
struct Item {
let id: UUID
var name: String
var contents: String
var type: ItemType
}
You can access these values using switch or if case let:
switch testItem2.type {
case let .task(complete: complete, due: due):
// Handle complete and due
break
case .note:
break
}
if case let .task(complete: complete, due: due) = testItem2.type {
// Handle complete or due
}
Sometimes it's convenient to create optional methods:
extension Item {
var complete: Bool? {
if case let .task(complete: complete, due: _) = type {
return complete
}
return nil
}
}
if let complete = testItem2.complete, complete {
// If this is a task and complete
}
The syntax around enums is sometimes a little clunky. If you have more than a couple of properties for the associated value, I highly recommend wrapping them up in a struct:
enum ItemType {
case note
case task(TaskProperties)
}
struct TaskProperties {
var complete: Bool
...
}
Barring a few tricks used in Objective C, a Swift instance can’t change it’s type at runtime. For one, different instances have different sizes, so there’s no good way to really make that work.
What you’re reaching for is something similar to SmallTalk’s become: message, which you can send to an object in order to get all references to it, program-wide, to be replaced with a reference to some other object instead. It’s cool, but leads to some confusing designs, and has some pretty brutal performance implications. You don’t really want this.
The exact best solution would depend on your specific use case. You haven’t provided much detail. The obvious/simple option is for each subclass to have an initializer that lets you create e.g. a new note from a task.
However, this won’t automatically update all references to the old note to point to the new task. If that’s something you need, you would need to add a layer of indirection yourself, where all item references point to items (which Item no longer has any subclasses), which themselves contain ItemDetails (or something like that) which are subclassed by NoteDetails and TaskDetails. Within the Item, the details can be switched back and forth between being a note or being a task, while all outstanding references to the wrapping item remain valid.
An alternative approach is by using a mixed design, of both structs and enums:
enum Item {
case note(Note)
case task(Task)
}
struct Note {
var text: String
}
struct Task {
var title: String
var status: Status
var priority: Priority
var dueDate: Date?
// ... other props
}
Switching between Note and Task would then be a simple matter of replacing self within the enum:
extension Task {
init(note: Note) {
self.init(title: note.text, status: .pending, priority: .low, dueDate: nil, ...)
}
}
extension Item {
mutating func convertToTask() {
guard case let .note(note) = self else {
// already a task, do nothing
// alternatively, you can throw an error
return
}
self = .task(Task(note: note))
}
}
// look, I have a `Note`
var item: Item = .note(Note(text: "something to do"))
// but now I have a `Task` :)
item.convertToTask()

How to get a class with generic type accept an array of different by same generic types?

I'm in the process trying to learn and understand protocols with associated types in swift.
At the same time, I'm learning SwiftUI and taking a course on Udemy.
The app that we will building is a coffee order application.
With that being said, I do not follow the tutorial to the "T" as I tried to structure the app differently, so I can learn to think on my own. The app is nothing too fancy.
The tutorial doesn't use generics and protocols to represent or structure data. It is just a tutorial to showcase SwiftUI.
I created a protocol called Coffee that has a CupSize associated type.
Each coffee Cappuccino, Espresso, and Brewed Coffee confirms to the Coffee protocol.
protocol Priceable {
var cost: Double { get }
}
protocol Coffee {
associatedtype CupSize
var cupSize: CupSize { get }
init(cupSize: CupSize)
}
enum EspressoCupSize {
case small
}
struct Espresso: Coffee, Priceable {
var cupSize = EspressoCupSize.small
var cost: Double { return 3.00 }
}
enum BrewedCoffeeCupSize {
case small
case medium
case large
}
struct BrewedCoffee: Coffee, Priceable {
var cupSize: BrewedCoffeeCupSize
var cost: Double {
switch self.cupSize {
case .small: return 1.00
case .medium: return 2.00
case .large: return 3.00
}
}
}
enum CappuccinoCupSize {
case small
case medium
case large
}
struct Cappuccino: Coffee, Priceable {
var cupSize: CappuccinoCupSize
var cost: Double {
switch self.cupSize {
case .small: return 2.00
case .medium: return 3.00
case .large: return 4.00
}
}
}
Then, I created an Order struct and an OrderManager class.
Order struct has a generic and needs to be a Priceable item.
The idea of a generic priceable item is to support other items in the future in case I want to expand the app...not just coffee.
The idea behind OrderManager is to keep track all the orders and manage the CRUD operations of the orders (still need to implement delete, read, and update).
struct Order<Item: Priceable> {
var name: String
var item: Item
}
class OrderManager<Item> {
private var orders: [Item]
init(orders: [Item]) {
self.orders = orders
}
func add(_ order: Item) {
self.orders.append(order)
}
}
My issue is using OrderManager.
let maryOrder = Order(name: "Mary", item: Espresso())
let sueOrder = Order(name: "Sue", item: BrewedCoffee(cupSize: .medium))
// Dummy Structure
struct Person {}
let orderManager = OrderManager(orders: [
maryOrder,
sueOrder,
Person() // This works!!! Which is not what I want.
])
I want the generic type for OrderManager to be an Order, but since Order has its own generic type of Priceable, I cannot seem to find the correct answer or find the correct syntax.
Things I have tried to get OrderManager to work
class OrderManager<Order> {} // Does not work because Order needs a generic type
class OrderManager<Order<Priceable>> // Still does not work
class OrderManager<Item: Priceable, Order<Item> {} // Still does not work.
// and I tried other solutions, but I cannot get this to work
// Also, when I think I got the right syntax, I cannot add Mary and Sue's orders to
// OrderManager because Mary's item is Espresso and Sue's item is BrewedCoffee
How can I get OrderManager to accept only an array of orders?
It's good that you want to experiment with generics, but this isn't the occasion for it. You say:
The idea ... is to support other items in the future in case I want to expand the app...not just coffee.
But you don't need a generic for that. The only requirement for managing an order is that the order's item be Priceable. Priceable is already a type; you don't need to add a generic type to the mix.
struct Order {
var name: String
var item: Priceable
}
class OrderManager {
private var orders: [Order]
init(orders: [Order]) {
self.orders = orders
}
func add(_ order: Order) {
self.orders.append(order)
}
}
I'm in the process trying to learn and understand generics with associated types in swift.
There is no such thing as "generics with associated types in Swift." There are generics, and there are protocols with associated types (PAT). They have some things in common, but are deeply different concepts used for very different things.
The purpose of a generic is to allow the type to vary over types chosen by the caller.
The purpose of a PAT is to allow a type to be used by existing algorithms, using types chosen by the implementer. Given this, Coffee does not make sense as a protocol. You're trying to treat it like a heterogeneous type. That's not what a PAT is. A PAT is a hook to allow types to be used by algorithms.
class OrderManager<Item> { ... }
This says that OrderManager can hold anything; literally anything at all. It doesn't have to be Priceable. In your case, Item is being coerced into Any, which is definitely not what you wanted (and why Person works when it shouldn't). But it doesn't make a lot of sense that OrderManager is tied to some item type. Do you really want one OrderManager for Coffee and a completely different OrderManager for Espresso? That doesn't match what you're doing at all. OrderManager should work over an Order of anything, right?
It's not really possible to determine what protocols and generics you do need here because you never do anything with OrderManager.orders. Start with the calling code. Start with no generics or protocols. Just let the code duplicate, and then extract that duplication into generics and protocols. If you don't have a clear algorithm (use case) in mind, you should not be creating a protocol yet.
See matt's answer for a starting point, but I'm sure it's not enough for your problem. You likely will need more things (most likely the name of the item for instance). Start with some simple structs (Espresso, BrewedCoffee, etc), and then start working out your calling code, and then you'll probably have more questions we can discuss.
To your question of how to attack this kind of problem, I would begin like this.
First, we have some items for sale. I model them in their most obvious ways:
// An Espresso has no distinguishing characteristics.
struct Espresso {}
// But other coffees have a size.
enum CoffeeSize: String {
case small, medium, large
}
// You must know the size in order to create a coffee. You don't need to know
// its price, or its name, or anything else. But you do have to know its size
// or you can't pour one. So "size" is a property of the type.
struct BrewedCoffee {
let size: CoffeeSize
}
struct Cappuccino {
let size: CoffeeSize
}
Done!
OK, not really done, but seriously, kind of done. We can now make coffee drinks. Until you have some other problem to solve, you really are done. But we do have another problem:
We want to construct an Order, so we can give the customer a bill. An Order is made up of Items. And Items have names and prices. New things can be added to an Order, and I can get textual representation of the whole thing. So we model first what we need:
struct Order {
private (set) var items: [Item]
mutating func add(_ item: Item) {
items.append(item)
}
var totalPrice: Decimal { items.map { $0.price }.reduce(0, +) }
var text: String { items.map { "\($0.name)\t\($0.price)" }.joined(separator: "\n") }
}
And to implement that, we need a protocol that provides name and price:
protocol Item {
var name: String { get }
var price: Decimal { get }
}
Now we'd like an Espresso to be an Item. So we apply retroactive modeling to make it one:
extension Espresso: Item {
var name: String { "Espresso" }
var price: Decimal { 3.00 }
}
And the same thing with BrewedCoffee:
extension BrewedCoffee {
var name: String { "\(size.rawValue.capitalized) Coffee" }
var price: Decimal {
switch size {
case .small: return 1.00
case .medium: return 2.00
case .large: return 3.00
}
}
}
And of course Cappuccino...but you know, as I start to write that I really want to cut-and-paste BrewedCoffee. That suggests maybe there's a protocol hiding in there.
// Just a helper to make syntax prettier.
struct PriceMap {
var small: Decimal
var medium: Decimal
var large: Decimal
}
protocol SizedCoffeeItem: Item {
var size: CoffeeSize { get }
var baseName: String { get }
var priceMap: PriceMap { get }
}
With that, we can implement the requirements of Item:
extension SizedCoffeeItem {
var name: String { "\(size.rawValue.capitalized) \(baseName)" }
var price: Decimal {
switch size {
case .small: return priceMap.small
case .medium: return priceMap.medium
case .large: return priceMap.large
}
}
}
And now the conformances require no code duplication.
extension BrewedCoffee: SizedCoffeeItem {
var baseName: String { "Coffee" }
var priceMap: PriceMap { PriceMap(small: 1.00, medium: 2.00, large: 3.00) }
}
extension Cappuccino: SizedCoffeeItem {
var baseName: String { "Cappuccino" }
var priceMap: PriceMap { PriceMap(small: 2.00, medium: 3.00, large: 4.00) }
}
These two examples are of two different uses of protocols. The first is implementing a heterogeneous collection ([Item]). These kinds of protocols cannot have associated types. The second is to facilitate code sharing between types. These kinds can. But in both cases I didn't add any protocols until I had a clear use case: I needed to be able to add them to Order and get back certain kinds of data. That led us to each step along the way.
As a starting point, model your data simply and design your algorithms. Then adapt your data to the algorithms with protocols. Protocols come late, not early.
You do not need to define Generic type on the class , you should put it on a method like following:
class OrderManager {
func doOrder<T: Priceable, L: Protocol2 >(obj: T) -> L {
// Use obj as a Priceable model
// return a Protocol2 model
}
}
and for send to the class you just send your model for example
varProtocol2 = OrderManager.doOrder(obj: maryOrder.item)
Here is an example with two generic object
protocol prot1 {
var a: Int {get set}
}
protocol protRet {
var b: String {get set}
init()
}
struct prot1Struct: prot1 {
var a: Int
}
struct prot2Struct: protRet {
init() {
b = ""
}
var b: String
}
class Manage {
func Do<T: prot1, L: protRet>(obj: T) -> L {
var ret: L = L()
ret.b = "\(obj.a)"
return ret
}
}
var obj1: prot2Struct?
var paramItem = prot1Struct(a: 10)
obj1 = Manage().Do(obj: paramItem)
Also if you want to use it on a Class you can do it like following:
class manageb<T: prot1, L: protRet> {
func Do(obj: T) -> L {
var ret: L = L()
ret.b = "\(obj.a)"
return ret
}
}
var obj1: prot2Struct?
var paramItem = prot1Struct(a: 10)
let classB = manageb<prot1Struct, prot2Struct>()
obj1 = classB.Do(obj: paramItem)

Working with generic constraints

I know this question has been asked before but I have no idea how to solve this current problem. I have defined a protocol MultipleChoiceQuestionable with an associatedtype property:
protocol Questionable {
var text: String {get set}
var givenAnswer: String? {get set}
}
protocol MultipleChoiceQuestionable: Questionable {
associatedtype Value
var answers: Value { get }
}
struct OpenQuestion: Questionable {
var text: String
var givenAnswer: String?
}
struct MultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [String]
var text: String
var givenAnswer: String?
var answers: Value
}
struct NestedMultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [MultipleChoiceQuestion]
var text: String
var answers: Value
var givenAnswer: String?
}
Types which conform to this protocol are saved in an array as Questionable like so:
// This array contains OpenQuestion, MultipleChoiceQuestion and NestedMultipleChoiceQuestion
private var questions: [Questionable] = QuestionBuilder.createQuestions()
Somewhere in my code I want to do something like:
let question = questions[index]
if let question = question as? MultipleChoiceQuestionable {
// Do something with the answers
question.answers = .....
}
This is not possible because Xcode warns me: Protocol MultipleChoiceQuestionable can only be used as a generic constraint. I've been searching around on how to solve this issue since generics are quite new for me. Apparently Swift doesn't know the type of the associatedtype during compile time which is the reason this error is thrown. I've read about using type erasure but I don't know if that solves my problem. Maybe I should use generic properties instead or perhaps my protocols are defined wrong?
If the action you want to apply to your sub-protocol objects does not rely on the associated type (i.e. neither has the a generic parameter nor returns the generic type) you can introduce an auxiliary protocol which just exposes the properties/methods you need, let your type conform to that protocol, and declare the question in terms of that protocol.
For example, if you just want to know some info about the question:
protocol MultipleChoiceInfo {
var numberOfAnswers: Int { get }
}
extension MultipleChoiceQuestion: MultipleChoiceInfo {
var numberOfAnswers: Int { return answers.count }
}
// do the same for the other multiple-choice types
Then you can access the questions through the new protocol like this:
let question = questions[index]
if let info = question as? MultipleChoiceInfo {
print(info.numberOfAnswers)
}
As I said, if you cannot provide an abstract (non-generic) interface then this won't work.
EDIT
If you need to process the generic data inside your questions you can extract the logic depending on the concrete generic type into another "processing" type which provides an interface to your questions. Each question type then dispatches its data to the processor interface:
protocol MultipleChoiceProcessor {
func process(stringAnswers: [String])
func process(nestedAnswers: [MultipleChoiceQuestion])
}
protocol MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor)
}
extension MultipleChoiceQuestion: MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor) {
processor.process(stringAnswers: answers)
}
}
Just create a type conforming to MultipleChoiceProcessor and do the type-check dance again:
if let proxy = question as? MultipleChoiceProxy {
proxy.apply(processor:myProcessor)
}
As an aside, if you don't have more protocols and structs in your real application, you might also just ditch the protocol stuff altogether... for this kind of problem it seems a bit over-engineered.

deep copy for array of objects in swift

I have this class named Meal
class Meal {
var name : String = ""
var cnt : Int = 0
var price : String = ""
var img : String = ""
var id : String = ""
init(name:String , cnt : Int, price : String, img : String, id : String) {
self.name = name
self.cnt = cnt
self.price = price
self.img = img
self.id = id
}
}
and I have an array of Meal :
var ordered = [Meal]()
I want to duplicate that array and then do some changes to the Meal instances in one of them without changing the Meal instances in the second one, how would I make a deep copy of it?
This search result didn't help me
How do I make a exact duplicate copy of an array?
Since ordered is a swift array, the statement
var orderedCopy = ordered
will effectively make a copy of the original array.
However, since Meal is a class, the new array will contain references
to the same meals referred in the original one.
If you want to copy the meals content too, so that changing a meal in one array will not change a meal in the other array, then you must define Meal as a struct, not as a class:
struct Meal {
...
From the Apple book:
Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.
To improve on #Kametrixom answer check this:
For normal objects what can be done is to implement a protocol that supports copying, and make the object class implements this protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
And then the Array extension for cloning:
extension Array where Element: Copying {
func clone() -> Array {
var copiedArray = Array<Element>()
for element in self {
copiedArray.append(element.copy())
}
return copiedArray
}
}
and that is pretty much it, to view code and a sample check this gist
You either have to, as #MarioZannone mentioned, make it a struct, because structs get copied automatically, or you may not want a struct and need a class. For this you have to define how to copy your class. There is the NSCopying protocol which unifies that on the ObjC world, but that makes your Swift code "unpure" in that you have to inherit from NSObject. I suggest however to define your own copying protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
which you can implement like this:
class Test : Copying {
var x : Int
init() {
x = 0
}
// required initializer for the Copying protocol
required init(original: Test) {
x = original.x
}
}
Within the initializer you have to copy all the state from the passed original Test on to self. Now that you implemented the protocol correctly, you can do something like this:
let original = Test()
let stillOriginal = original
let copyOriginal = original.copy()
original.x = 10
original.x // 10
stillOriginal.x // 10
copyOriginal.x // 0
This is basically the same as NSCopying just without ObjC
EDIT: Sadly this yet so beautiful protocol works very poorly with subclassing...
A simple and quick way is to map the original array into the new copy:
let copyOfPersons: [Person] = allPersons.map({(originalPerson) -> Person in
let newPerson = Person(name: originalPerson.name, age: originalPerson.age)
return newPerson
})
The new Persons will have different pointers but same values.
Based on previous answer here
If you have nested objects, i.e. subclasses to a class then what you want is True Deep Copy.
//Example
var dogsForAdoption: Array<Dog>
class Dog{
var breed: String
var owner: Person
}
So this means implementing NSCopying in every class(Dog, Person etc).
Would you do that for say 20 of your classes? what about 30..50..100? You get it right? We need native "it just works!" way. But nope we don't have one. Yet.
As of now, Feb 2021, there is no proper solution of this issue. We have many workarounds though.
Here is the one I have been using, and one with less limitations in my opinion.
Make your class conforms to codable
class Dog: Codable{
var breed : String = "JustAnyDog"
var owner: Person
}
Create this helper class
class DeepCopier {
//Used to expose generic
static func Copy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
Call this method whenever you need true deep copy of your object, like this:
//Now suppose
let dog = Dog()
guard let clonedDog = DeepCopier.Copy(of: dog) else{
print("Could not detach Dog")
return
}
//Change/mutate object properties as you want
clonedDog.breed = "rottweiler"
//Also clonedDog.owner != dog.owner, as both the owner : Person have dfferent memory allocations
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our object. Just make sure all your Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.