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 - swift

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()

Related

Extending a constrained protocol for an array argument is not possible

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.

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)

Cast between types using only protocols?

I have 2 structs that are only related by protocols. I'm trying to cast between the two types since they share a protocol, but I get a runtime error.
This is what I'm trying to do:
protocol A {
var id: String { get }
var name: String { get }
var isActive: Bool { get set }
}
struct Entity: A {
let id: String
var name: String
var isActive: Bool
func someFun() {
print("\(name), active: \(isActive)")
}
}
struct ViewModel: A {
let id: String
var name: String
var isActive: Bool
}
let vm = ViewModel(id: "abc", name: "John Doe", isActive: true)
let pt = vm as A
let et = pt as! Entity
print(et.someFun()) //crash
The error I get is:
Could not cast value of type '__lldb_expr_87.ViewModel' (0x111835638) to '__lldb_expr_87.Entity' (0x111835550).
This is a real bummer because if I have millions of records, I don't want to have to loop through each one to manually convert one by one. I was hoping to implicitly cast magically like this:
let entity = vm as A as Entity
let entities = vms as [A] as [Entity]
Any performance-conscious way to pass objects between boundaries without looping through each one?
As vadian stated in his comment, you can't typecast like this. To perform what you are trying to do by typecasting, you would need to do something like this:
let entity = Entity(id: vm.id, name: vm.name, isActive: vm.isActive)
let entities = vms.map {
return Entity(id: $0.id, name: $0.name, isActive: $0.isActive
}
The reason why you are crashing is because typecasting doesn't change the properties and methods that an object actually has, only the methods and properties they have access to. If you performed the following, you would lose access to your someFun() method:
let entityAsA = Entity(id: "id", name: "name", isActive: true) as A
However, the following will compile and run without crashing:
(entityAsA as! Entity).someFun()
So, when you attempt to cast from ViewModel to A to Entity, it doesn't change the way the value was created in memory (without access to a function called someFun() ) and you'll crash every time.

ReSwiftRecorder Add Action with property

Recently I have used ReSwift API, And I want to add ReSwiftRecorder too!
The sample of ReSwiftRecorder in Github is very simple app
I need to to something more complicated. I have an object which get data from server and I need to It reloads its data when app is not connected to net. Here is my code:
AppState:
struct AppState: StateType {
var menus: Result<[Menu]>?
}
MenuReducer:
func menusReducer(state: Result<[Menu]>?, action: Action) -> Result<[Menu]>? {
switch action {
case let action as SetMenusAction:
return action.menus
default:
return state
}
}
AppReducer:
struct AppReducer: Reducer {
func handleAction(action: Action, state: AppState?) -> AppState {
return AppState(
menus: menusReducer(state: state?.menus, action: action),
)
}
}
MenuActions:
struct SetMenus: Action {
let menus: Result<[Menu]>
}
I know I need to change MenuAction to Something like this:
let SetMenusActionTypeMap: TypeMap = [SetMenusAction.type: SetMenusAction.self]
struct SetMenusAction: StandardActionConvertible {
static let type = "SET_MENU_ACTION"
let menus: Result<[Menu]>
init() {}
init(_ standardAction: StandardAction) {}
func toStandardAction() -> StandardAction {
return StandardAction(type: SetMenusAction.type, payload: [:], isTypedAction: true)
}
}
but I got error on init functions
Return from initializer without initializing all stored properties
when I set a initializer code the error disappear but app does not restore saved data! How can I fix it?
You will want to add serialization/deserialization code. The menus property needs to be set. Also, you will want to serialize that property as payload:
let SetMenusActionTypeMap: TypeMap = [SetMenusAction.type: SetMenusAction.self]
struct SetMenusAction: StandardActionConvertible {
static let type = "SET_MENU_ACTION"
let menus: Result<[Menu]>
init() {
self.menus = // however you initialize that
}
init(_ standardAction: StandardAction) {
let maybeMenus = standardAction.payload["menus"] as? [Menu]?
self.menus = // create Result from Optional<[Menu]>
}
func toStandardAction() -> StandardAction {
let maybeMenus = self.menus.asOptional // Cannot serialize Result itself
return StandardAction(type: SetMenusAction.type, payload: ["menus" : maybeMenus], isTypedAction: true)
}
}
So problems I see here: JSON serialization depends on Dictionary representation of your payload data, i.e. the properties of your object. Can Result be serialized directly? I guess not, so you need to convert it, probably easiest to nil.
All in all, the payload is the key you missed and now you have to figure out how to use it with the data you have at hand. Also, it makes me a bit suspicious that the Result type itself is part of the AppState. I expected it to be reduced away or handled before dispatching an action, like SettingMenusFailedAction instead of ChangeMenusAction(result:) or similar. Just as a sidenote: actions should be more than typed property setters.

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.