Advertiser Model Protocol Oriented Programming
Tinder Firestore Swipe and Match
In the previous lesson, we were able to introduce the concept of View Models to display information inside of our cards. Now that we have that bit of plumbing implemented, let's now introduce another model object in our application that we would like to show inside of a card: Advertiser. This advertiser model will have its own unique properties, sharing very little similarities with a User model object. Utilizing the middle layer of CardViewModel we can quickly implement a different card by applying a toCardViewModel function on advertiser.

Comments (8)
st4n
4 years ago
for the constructor I'm using [StructName].init( the autocompletion pops and then I'm removing .init :) it seems faster than all this copy-paste.. stupid XCode anyways haha :)
Brian Voong
4 years ago
ayon
4 years ago
I also used the same technic, sometimes xcode is as dumb as a donkey! :)
Thanh Minh
4 years ago
This technique I learned from Brian that show the pop up when create an object. struct Advertiser: ProducesCardViewModel { let advID: String let title: String init(advID: String, dict: [String:Any]) { self.advID = advID self.title = dict["title"] as? String ?? "title" } }
Christophe Bugnon
4 years ago
Ok I get my answer here ! :D I made something like this because I made it differently for model protocol ProducesCardViewModel { associatedtype ModelData associatedtype Advertiser static func modelToViewModel(model: ModelData) -> CardViewModel static func advertiserToViewModel(adv: Advertiser) -> CardViewModel } struct CardViewModel: ProducesCardViewModel {} And in my HomeController: let cardViewModels = [ User(name: "Kelly", age: 23, profession: "Music DJ", imageName: "lady5c"), User(name: "Jane", age: 18, profession: "Teacher", imageName: "lady4c"), nil, Advertiser(title: "Slide Out Menu", brandName: "Lets Build That App", posterPhotoName: "lady4c") ].compactMap { (data) -> CardViewModel? in var cardViewModel: CardViewModel? switch data { case let user as User: cardViewModel = CardViewModel.modelToViewModel(model: user) case let adv as Advertiser: cardViewModel = CardViewModel.advertiserToViewModel(adv: adv) default: break } return cardViewModel } It's maybe better to use Internally dependance as you made if we want a reactive result later no?
Brian Voong
4 years ago
Christophe Bugnon
4 years ago
Thanks a lot, I really appreciate your comment. :) I'm just trying to get a clean architecture to get a generic code and use it wherever I need. I think that all structures should be like that.
Tokyojogo
4 years ago
Hi Christophe, can you post your CardViewModel file? What is associated type ModelData and associated type Advertiser for? I was following your code and made protocol ProducesCardViewModel { associatedtype ModelData associatedtype Advertiser static func modelToViewModel(model: ModelData) -> CardViewModel static func advertiserToViewModel(adv: Advertiser) -> CardViewModel } struct CardViewModel: ProducesCardViewModel { //properties that our card will display/render out let imageName: String let attributedString: NSAttributedString let textAlignment: NSTextAlignment static func userToCardViewModel(user: User) -> CardViewModel { let attributedText = NSMutableAttributedString(string: user.name, attributes: [.font: UIFont.systemFont(ofSize: 32, weight: .heavy)]) attributedText.append(NSAttributedString(string: " \(user.age)", attributes: [.font: UIFont.systemFont(ofSize: 24, weight: .regular)])) attributedText.append(NSAttributedString(string: "\n\(user.profession)", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .regular)])) return CardViewModel(imageName: user.imageName, attributedString: attributedText, textAlignment: .left) } static func advertiserToCardViewModel(advertiser: Advertiser) -> CardViewModel { let attributedText = NSMutableAttributedString(string: advertiser.title, attributes: [.font: UIFont.systemFont(ofSize: 34, weight: .heavy)]) attributedText.append(NSAttributedString(string: "\n\(advertiser.brandName)", attributes: [.font: UIFont.systemFont(ofSize: 24, weight: .bold)])) return CardViewModel(imageName: advertiser.posterPhotoName, attributedString: attributedText, textAlignment: .center) } } but I'm not conforming to the protocol because of the associated type.
Christophe Bugnon
4 years ago
Typealias is like a generic but for a protocol. Warning, when you create the method of your protocol because you can't change the name of your parameter as you made. If your name as a parameter is model you have to keep this name and don't call it user, otherwise your typealias is useless and the method is not conforming at your protocol. I have a little different result because I'm more advanced in the course. protocol ProducesCardViewModel { associatedtype ModelData associatedtype Advertiser static func modelToViewModel(model: ModelData) -> CardViewModel static func advertiserToViewModel(adv: Advertiser) -> CardViewModel } class CardViewModel: ProducesCardViewModel { let imageUrls: [String] let attributedString: NSAttributedString let textAlignment: NSTextAlignment static func modelToViewModel(model: User) -> CardViewModel { let ageString = model.age != nil ? "\(model.age!)" : "N\\A" let professionString = model.profession == "" ? "Not available" : "\(model.profession!)" let attributedText = NSMutableAttributedString(string: model.name ?? "", attributes: [.font: UIFont.systemFont(ofSize: 32, weight: .heavy)]) attributedText.append(NSAttributedString(string: " \(ageString)", attributes: [.font: UIFont.systemFont(ofSize: 24, weight: .regular)])) attributedText.append(NSAttributedString(string: "\n\(professionString)", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .regular)])) let imageUrls = [model.imageUrl1, model.imageUrl2, model.imageUrl3].compactMap { $0 }.filter { $0.isEmpty == false } return CardViewModel(imageNames: imageUrls, attributedString: attributedText, textAlignment: .left) } static func advertiserToViewModel(adv: Advertiser) -> CardViewModel { let attributedText = NSMutableAttributedString(string: adv.title, attributes: [.font: UIFont.systemFont(ofSize: 34, weight: .heavy)]) attributedText.append(NSAttributedString(string: "\n\(adv.brandName)", attributes: [.font: UIFont.systemFont(ofSize: 24, weight: .bold)])) return CardViewModel(imageNames: [adv.posterPhotoName], attributedString: attributedText, textAlignment: .center) } }
Tokyojogo
4 years ago
Do you have this on Github?
Alvathor
4 years ago
We should have a like button here ;)
Christophe Bugnon
4 years ago
Sorry for the late answer, no I haven't but I could do it if you want. :)
Tokyojogo
4 years ago
sure!
Cinquain
4 years ago
This video was classy!
Cinquain
4 years ago
Fire!
stonypig1
4 years ago
Thanks for this wonderful tutorial, I have a question that inside the Struct, you use Let for all the properties, is ok to use Var , isn't those property will change all the time when data go through them? thanks
Brian Voong
4 years ago
stonypig1
4 years ago
at 12:54, why the array can be force case to that protocol, isn't it the type of CardViewModel? so you mean once it conform to a specific protocol, it's object can become the type of protocol too? sorry for this noob question. first time encounter this kinda stuff. and thanks for mention it. really learn a lot in this section. thanks.
Brian Voong
4 years ago
TWei
4 years ago
I think it's because of Polymorphism which does what exactly what you're assuming. Please correct me if I'm wrong.
Cinquain
4 years ago
This episode was beautiful!
Alex Grinberg
3 years ago
Brian, Is the code here still relevant today? Would it work?
Brian Voong
3 years ago
HELP & SUPPORT