Bronze/Silver Challenge: Ch. 24

Bronze Challenge

This one I couldn’t get to work quite right without adding something to Asset - a property designed to hold onto the asset’s index in the array. I couldn’t figure out a more elegant way to do it, so I went with the quick-and-dirty method

/// Asset.swift
...
    var assetIndex: Int
...
    init(name: String, value: Double) {
        ...
        self.assetIndex = 0
    }

/// Person.swift
func takeOwnership(of asset: Asset) {
    accountant.gained(asset, completion: {
        ...
        asset.assetIndex = assets.endIndex // have to assign the index here. Subtracting 1 because endIndex returns the number AFTER the last index, as if you started at '1'
    })
}

/* new function */
func removeOwnership(of asset: Asset) {
    accountant.lost(asset, completion: {
        asset.owner = nil
        assets.remove(at: asset.assetIndex - 1)
    })
}
/// Accountant.swift
/* new function */
    func lost(_ asset: Asset, completion: () -> Void) {
        netWorth -= asset.value
        completion()
    }
/// main.swift
/* and finally...*/
bob?.takeOwnership(of: laptop!)
bob?.takeOwnership(of: hat!)
bob?.removeOwnership(of: laptop!) // removes asset and its reference

Silver Challenge

This one isn’t too difficult (so maybe I did something wrong?) - all you have to do is check if the owner is nil or not before setting ownership of the asset. Update takeOwnership(of:) accordingly:

func takeOwnership(of asset: Asset) {
    if asset.owner == nil {
        accountant.gained(asset, completion: {
            asset.owner = self
            assets.append(asset)
            asset.assetIndex = assets.endIndex - 1
        })
    } else {
        print("Item is already owned by \(asset.owner?.name). Remove ownership before you reassign.")
    }
}

For the bronze challenge, I figured out you can conform Asset to Equatable, then implement the required func ==:

static func ==(lhs: Asset, rhs: Asset) -> Bool {
    return lhs === rhs
}

and then you can get the Asset’s index like so:

assets.remove(at: assets.index(of: asset)!)

Seems to work!

(Oh, now I see Equatable is the subject of the next chapter!)

@macintacos, (love the handle, btw, made me lol) Thanks for posting, I wasn’t able to figure that part out. When I typed your code in, I noticed that the only problem with tracking this index in the asset is that you are then forced to disown the items in the opposite order as you took ownership of them

For example, if you setup the array of assets with 2 items, and first try to disown the 0th item, this will shrink the array to have only one asset, at index==0, and then when you try to remove the other item, since its assetIndex is still 1, you end up trying to remove assets[1], which gives you an index out of array bounds crash. To avoid this, you need to empty with LIFO.

I am not sure how to fix it so that it works, though :confused:

Haha, I was reading to compare my answer and found this. You made my life so much easier when I was doing the Gold challenge in the next chapter. :joy:

What do you think: Swift or Objective C?

The choice of a programming language for your application is one of the most complicated and important decisions, which are taken by the programmer. Because of it, in that article you will be able to see all of the benefits and drawbacks of one of the most popular languages called Swift as well as the comparison of it with much older https://artjoker.net/blog/objective-c-or-swift/

Hi, @macintacos , using assetIndex property seems work, but can be better, my solution like this:

///Asset.swift
class Asset: CustomStringConvertible, Equatable {
    
    // Declare this class adapt to Equatable protocol
    static func ==(lhs: Asset, rhs: Asset) -> Bool{
            return (lhs.name == rhs.name) && (lhs.value == rhs.value)
    }
}

///Account.swift
func lost(_ asset: Asset, completion: () -> Void) {
    netWorth -= asset.value
    completion()
}

///person.swift
func removeOwnership(of asset: Asset) {
    accountant.lost(asset) {
        let assetIndex = assets.index(of: asset)
        if let index = assetIndex {
            assets.remove(at: index)
            asset.owner = nil
        }
    }        
}

When you clients ask to build an iOS on Objective-C, just show them this article for custom marketplace development true to life?

My solutions:

class Person: CustomStringConvertible {
    let name: String
    let accountant = Accountant()
    var assets = [Asset]()
 
     var description: String {
        return "Person(\(name))"
    }

    init(name: String) {
        self.name = name
        
        accountant.netWorthChangedHandler = {
            [weak self] netWorth in
        
            self?.netWorthDidChange(to: netWorth)
        
            return
        }
    }
    deinit {
        print("\(self) is being deallocated")
    }

    func takeOwnership(of asset: Asset) {
        //asset.owner = self
        //assets.append(asset)
        /*
         We now use the closure on Accountant's method gained to add
         self as owner of the asset and put the asset in the assets list
         */
        accountant.gained(asset) {
            asset.owner = self
            assets.append(asset)
        }
    }

    func relenquishOwnership(of asset: Asset) -> Bool {
        if let found = assets.index(where: {($0.name == asset.name && $0.value == asset.value)}) {
            accountant.lost(assets[found]) {
                let removed = assets.remove(at: found)
                removed.owner = nil
            }
        
            return true
        } else {
            
            return false
        }
    }

    func netWorthDidChange(to netWorth: Double) {
        print("The net worth of \(self) is now \(netWorth)")
    }

    func useNetWorthChangedHandler(handler: @escaping (Double) -> Void) {
        accountant.netWorthChangedHandler = handler
    }
}

    class Asset: CustomStringConvertible {
        let name: String
        let value: Double
        weak var owner: Person? {
            willSet {
                if let oldOwner = owner {
                    // we've gotta relenquish this asset from the previous owner!
                    oldOwner.relenquishOwnership(of: self)
                }
            }
        }
        var description: String {
            if let actualOwner = owner {
                return "Asset(\(name), worth \(value), owned by \(actualOwner))"
            } else {
                return "Asset(\(name), worth \(value), not owned by anyone)"
            }
        }
        init(name: String, value: Double) {
            self.name = name
            self.value = value
        }
        deinit {
            print("\(self) is being deallocated")
        }
    }

class Accountant {
    typealias NetWorthChanged = (Double) -> Void
    var netWorthChangedHandler: NetWorthChanged? = nil
    var netWorth: Double = 0.0 {
        didSet {
            netWorthChangedHandler?(netWorth)
        }
    }

    func gained(_ asset: Asset, completion: () -> Void) {
        netWorth += asset.value
        completion()
    }

    func lost(_ asset:Asset, completion: () -> Void) {
        netWorth -= asset.value
        completion ()
    }

}

If you don’t know what the difference between Swift and Objective-C is and what language is better for your project, find out what language is better for iOS development: https://mlsdev.com/blog/51-7-advantages-of-using-swift-over-objective-c