Inter Object Parent/Child Communication in Swift - ios

I have a view controller that contains instances (objects) of a few related classes that need to intercommunicate.
I can easily set these up by creating instances of the objects within the VC:
var house = House ()
var road = Road ()
var town = Town ()
But how do I get a method in the House object to 'speak'/send data to the Road object? In some languages I could invoke the parent object and target the other class and its methods that way:
func sendHouseData (num: Int) {
_parent.road.getHouseData (num)
}
Obviously the _parent keyword is not swift code so won't work.
The only way I can think of doing this is to create an instance of road inside the House object as a child object, thus exposing it directly in scope to the method code. Having a lot of external code in the VC to do all the heavy lifting is another idea that seems like bad practice and inelegant.
Any ideas?
Many thanks.
Kw

One way would be to use the Delegation pattern. Each class should declare a protocol with all the messages it can send, and the class that needs to listen to those messages should implement the protocol, register as the (or one of the) delegate(s) and respond accordingly.
Here's a good example how to implement Delegation Pattern.
And a sample implementation for your objects:
protocol HouseDelegate {
func readData(data:Int)
}
class House {
var delegate:HouseDelegate
var data:Int = 0
init(delegate:HouseDelegate) {
self.delegate = delegate
}
func sendData() {
self.delegate.readData(data)
}
}
class Road: HouseDelegate {
var data:Int = 0
func readData(data: Int) {
print("Read data \(data)")
self.data = data
}
}
var road = Road ()
var house = House (delegate: road)
house.sendData() //prints "Read data 0"

Best way to handle this is by using mediator pattern, if you need to establish communication between House, Road, Town you can set it in a way that they communicate to 'mediator' (parent object). In this case parent object can mediate and coordinate between the objects
Or, you can use NSNotificationCenter, post a notification from one object and listen to the notification in another object, but this is hard to track as soon as your app gets more complex.
Let's say the Word class is parent class of the House, Road, Town.
Here you can establish communication between Word, House and Town, and inter-communication between House and Town.
Be advised this is more pseudo code I'm writting it in browser but you'll get idea
class Word {
let house = House()
let town = Town()
init() {
// Here World will be notified when the house hasReceivedVisitor and the town hasBuiltRoad.
self.house.hasReceivedVisitor = { visitorName in
print("\(visitorName) has visited the house!")
// Communication between house and town ...
self.town.doSometingWithVisitor(visitorName)
}
self.town.hasBuiltRoad = { roadNumber in
print("A road \(roadNumber) has been built!")
// Communication between town and house ...
self.house.doSometingWithRoad(roadNumber)
}
}
}
class House {
var hasReceivedVisitor: ((vistorName: String) -> ())?
func createVisitor() {
let vistor = Visitor("Mike")
self.hasReceivedVisitor?(vistor.vistorName)
}
func doSometingWithRoad(roadNumber: Int) {
// .....
}
}
class Town {
var hasBuiltRoad: ((roadNumber: Int) -> ())?
func createRoad() {
let road = Road(66)
self.hasBuiltRoad?(road.roadNumber)
}
func doSometingWithVisitor(visitorName: String) {
// .....
}
}
The same thing above can be accomplished via delegation but I like blocks more.
Basically you have 3 options: Notifications (the worst), delegation or callbacks.

Related

Good Practices for Swift SpriteKit Class Structure

I am developing an iOS card game with Swift and have a question about good practice when it comes to designing the framework for games with SpriteKit and iOS. My basic structure is as follows:
class Card: SpriteKitNode {
cardValue: Int
func action() {}
}
struct Player {
playerName = "Joe"
playerPile = [Card]()
playerStack = [Card]()
}
struct Game {
// Create players and deals out the cards to each player pile.
}
Each player has a few piles of cards and they all come together in the game struct. Most of the cards in my game are independent cards. So, if player 1 plays a card, it has no impact on other players. However, some of the cards in my games have actions which are designed to impact other players decks depending on which card is played.
My question is, a card "action" sounds like it should be defined within the card class. It is a feature of the card itself that it has this ability. However, when I think about how I would implement this, I am not sure how it could impact at the game level to access other player piles. So what is the best practice when it comes to trying to define actions that have upstream impacts when the "Card" does not know how many players there are, which player it belongs to, and how to access another player's pile.
Example of an action I would like to implement: Player 1 can move the top card in any player stack to the top card in any other player's stack. So, player 1 could move the top card from player 2 to 1, 2 to 3, or any other combination based on the number of players on the board. I thought I could be able to do this by passing a lot of parameters to the action function action(moveFrom: Player1, moveTo: Player3), but thought I would come here to find out best practices.
There are also a few other actions which may have different inputs based on the card played. Should these be separate functions, or all built into one "Card Action" function?
// Possibly how this function might look.
func action(moveFrom: Player, moveTo: Player) {
let cardMoved = moveFrom.playerPile[0]
moveTo.playerPile.append(cardMoved)
}
Edit - Follow Up Question
After changing to a POP, I have a few further questions that are stumping me on how to implement.
My action function does not allow me to mutate the player's piles that have been selected. Error = "Cannot use mutating member on immutable value: 'fromPlayer' is a 'let' constant". Does this mean I would have to destroy and create and return new piles for each player each time it was called rather than just modifying the existing ones? This seems pretty inefficient is there a better way to do this?
I get another error when trying to call my action function. I have already checked that my card is indeed a "Slice Card", but I get the error "Value of type 'Card' has no member 'action'".
protocol ActionCard {
func action(fromPlayer: Player, toPlayer: Player)
}
class Card {
}
class SliceCard: Card, ActionCard {
func action(fromPlayer: Player, toPlayer: Player) {
let cardTaken = fromPlayer.stack.removeLast()
toPlayer.stack.append(cardTaken)
}
}
struct Player {
var stack = [Card]()
func playCard(card: Card, fromPlayer: Player, toPlayer: Player) {
if card is SliceCard {
card.action(fromPlayer: fromPlayer, toPlayer: toPlayer)
}
}
}
let player1 = Player()
let player2 = Player()
let cardSelected = SliceCard()
player1.playCard(card: cardSelected, fromPlayer: player1, toPlayer: player2)
Interesting question. I would suggest you a POP (Protocol Oriented Programming) approach.
The Player type
First of all I would suggest using a class for the Player type because you want to pass the same instance to other methods/actions and want these method to be able to mutate the original instance.
You could still use a struct + inout parameters but using a class feels more correct.
class Player {
let name: String
var pile: [Card] = []
var stack: [Card] = []
init(name: String) {
self.name = name
}
}
The ActionError enum
Just create an enum and add a case for every possible error an action can throw
enum ActionError: Error {
case playerHasNoCards
// add more errors here
}
The BaseCard class
You put here all the stuff common to any Card.
class BaseCard: SKSpriteNode {
let cardValue: Int
init(cardValue: Int) {
self.cardValue = cardValue
let texture = SKTexture(imageNamed: "card_image")
super.init(texture: texture, color: .clear, size: texture.size())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The Card protocol
Here you required that in order to conform to Card a type must
be a class that inherits from BaseCard
and must have an action(...) method
Here's the code
protocol Card: BaseCard {
func action(currentPlayer: Player, destinatonPlayer: Player, allPlayers: [Player]) throws
}
Please note that the action method should receive all the parameters needed for any action you want to implement.
Your first card
Finally you can implement your first card
class CardToStoleACardFromAnotherPlayer: BaseCard, Card {
func action(currentPlayer: Player, destinatonPlayer: Player, allPlayers: [Player]) throws {
guard destinatonPlayer.pile.isEmpty == false else { throw ActionError.playerHasNoCards }
let card = destinatonPlayer.pile.removeFirst()
currentPlayer.pile.append(card)
}
}
Create as many classes as you want, you will write a different logic into each class.
Examples
class CardToStoleAllCardsFromAllPlayers: BaseCard, Card {
func action(currentPlayer: Player, destinatonPlayer: Player, allPlayers: [Player]) throws {
// ...
}
}
class CardToGiftACardToAnotherPlayer: BaseCard, Card {
func action(currentPlayer: Player, destinatonPlayer: Player, allPlayers: [Player]) throws {
// ...
}
}
Considerations
Now you when you pick a Card and you want to execute its action just call the action method passing all the parameters.
Depending on the type of the instance (CardToStoleACardFromAnotherPlayer, CardToStoleAllCardsFromAllPlayers, CardToGiftACardToAnotherPlayer, ...) contained into that variable a different logic will be executed.

For one-to-few relationships: NotificationCenter or multicasting delegate?

If delegates were designed for one-to-one relationships between objects and NSNotifications were designed for one-to-potentially-many relationships, is there a best practice for one-to-few?
I've seen a lot of custom multicasting delegates in iOS where an object can cast to multiple subscribers (i.e. Swift Language Multicast Delegate), but the implementations are often very involved and seem overkill. One such problem is safely storing an array of weak references (the delegates) (How do I declare an array of weak references in Swift?).
I've seen a lot of recommendations (like this one multiple listeners for delegate iOS) that suggest this is what NotificationCenter was made for. But the idea of broadcasting out into the ether for a one-to-few relationship itself seems overkill.
Is there a best practice for Apple's frameworks and the Swift language? I never see them write about this. Is NotificationCenter a suitable use for a one-to-few relationship where a multicasting delegate would otherwise be needed?
I would not use NotificationCenter because the type of the message and the data between the sender and receiver (observer) becomes lost. Using Notification Center will make your code to rely on Notification object where you need to use the userInfo dictionary of the notification to add the data which makes it harder to understand what exactly keeps the notification (will need to see how exactly the data is populated when the notification is send).
The delegate is a better solution and having more than 1 delegate in a weak list of delegates is ok. I have used such composition in many places where I need to register more then 1 listener to a particular event and works just fine.
You can create the delegates collection once and reuse it very easily across the code. Here is my solution:
class WeakContainer {
private weak var value: AnyObject?
public init(value: AnyObject) {
self.value = value
}
func get() -> AnyObject? {
return self.value
}
}
class DelegatesCollection<T>: Sequence {
private lazy var weakDelegates = [WeakContainer]()
var delegates: [T] {
return self.weakDelegates.map() { $0.get() as! T }
}
var hasDelegates: Bool {
return !self.weakDelegates.isEmpty
}
init() { }
func add(delegate: T) {
var exists = false
for currentDelegate in self.weakDelegates {
if(currentDelegate.get() === (delegate as AnyObject)) {
exists = true
break
}
}
if(!exists) {
self.weakDelegates.append(WeakContainer(value: delegate as AnyObject))
}
}
func remove(delegate: T) {
var i = 0
for currentDelegate in self.weakDelegates {
if(currentDelegate.get() == nil || currentDelegate.get() === (delegate as AnyObject)) {
self.weakDelegates.remove(at: i)
break
}
i += 1
}
}
func makeIterator() -> IndexingIterator<[T]> {
return self.delegates.makeIterator()
}
}
I can speculate that Apple frameworks use only single delegate because it is a business logic what actions to perform when delegate is called. From Apple's point of view it is enough to delegate that some event has happened and to leave the application to decide what to do next so there is no point to support multiple delegates on framework level.

How to modify Object in Dependency Injection Pattern

So for the time I worked on the project and avoided Singletons and used Dependency Injection. By this I mean instead of creating a shared instance I created a class instance and passed to all controllers whichever needs.
Now my question, my model object which has references in all controllers, I need to point them either to a new object as for the requirements the data is fully updated like calling the init() again.
But if I do that in a certain controller that reference will only point to this new object.
So if you get what I mean I want the pointee of the references or where at memory address that object is there should be replaced to a new one and all references should still point to that old address / new object.
I think you need to inject not model in controllers, but service for obtaining and saving this model, something like this.
Protocol for service:
protocol ModelServiceProtocol {
func obtainModel(completion: (Model) -> Void)
func save(model: Model, compleiton: ()->Void)
}
Example ViewController with dependency:
class ViewController: UIViewController {
let modelService: ModelServiceProtocol
init(modelService: ModelServiceProtocol) {
self.modelService = modelService
}
func obtainModel() {
modelService.obtainModel { model in
// do something
}
}
func saveEditedModel() {
modelService.save(model: model) {
// model saved
}
}
}
Implementation of ModelService that will obtain and save your model:
class ModelService: ModelServiceProtocol {
func obtainModel(completion: (Model) -> Void) {
// implementation of obtainig model from some storage
}
func save(model: Model, compleiton: ()->Void) {
// implementation of saving model in some storage
}
}
Injection of dependency:
func buildController() -> ViewController {
let modelService = ModelService()
let viewController = ViewController(modelService: modelService)
return viewController
}
In this approach you will get actual model in ViewController, edit and save to some storage. Model will be actual on every step

RxSwift, use .scan to keep track of the state of an object

I know state is the enemy of Reactive programming but I'm dealing with it in my process of learning RxSwift.
My app is very simple, the first screen is a list and a search of books and the second a detail of the book in which you can add/remove a book to your shelf and mark it as read/unread.
To show the detail of the book I create a BookViewModel passing a BooksService to perform network operations and the current Book to show.
The problem is that I have to keep track of the changes in the book in order to change the UI: for example, after removing the book the button that previously says "Remove" now it has to say "Add".
I achieve this behavior using a Variable<Book> exposed to the observers as a Driver<Book>, but I'm messing a lot with it when the network operation returns and I have to update the value of the Variable<Book> in order to trigger the update of the UI.
This is the initializer of the view model:
init(book: Book, booksService: BooksService) {
self._book = Variable(book)
self.booksService = booksService
}
This is the observable I expose
var book: Driver<Book> {
return _book.asDriver()
}
And here it is my function to add/remove the book:
func set(toggleInShelfTrigger: Observable<Void>) {
toggleInShelfTrigger // An observable from a UIBarButtonItem tap
.map({ self._book.value }) // I have to map the variable's value to the actual book
.flatMap({ [booksService] book -> Observable<Book> in
return (book.isInShelf ?
booksService.delete(book: book) :
booksService.add(book: book))
}) // Here I have to know if the books is in the shelf or not in order to perform one operation or another.
.subscribe(onNext: { self._book.value = $0 }) // I have to update the variable's value in order to trigger the UI update
.disposed(by: disposeBag)
}
I am very unhappy with this code and the whole view model. It works but it is clunky, and essentially wrong because if the network operation fails the subscription will be disposed and my button will became unresponsive.
If I get rid of the Variable<Book> and return a Driver<Book> from the method set(toggleInShelfTrigger: Observable<Void>) I won't have this mess but I will not be able to know if I have to add or to remove the book.
So, what is the real world way to keep track of the state of an object in this kind of app? How can I achieve this by only using Rx operators?
EDIT
I've managed to clean that crappy code but I'm still trying to achieve state without Variable and using scan operator.
This is my new BookViewModel initializer:
init(book: Book, booksService: BooksService) {
self.bookVariable = Variable(book)
let addResult = addBook
.mapBookFrom(bookVariable)
.flatMapLatest({ booksService.add(book: $0) })
.updateBookVariable(bookVariable)
let removeResult = ... // Similar to addResult, changing service call
let markReadResult = ... // Similar to addResult, changing service call
let markUnreadResult = ... // Similar to addResult, changing service call
self.book = Observable.of(addResult, removeResult, markReadResult, markUnreadResult).merge()
.startWith(.success(book))
}
I made a couple of custom operators to help me manage the Variable<Book>, one to get the real Book:
private extension ObservableType where E == Void {
func mapBookFrom(_ variable: Variable<Book>) -> Observable<Book> {
return map({ _ in return variable.value })
}
}
And another to update the Variable after the service returns:
private extension ObservableType where E == BookResult<Book> {
func updateBookVariable(_ variable: Variable<Book>) -> Observable<BookResult<Book>> {
return self.do(onNext: { result in
if case let .success(book) = result {
variable.value = book
}
})
}
}
Now I have a very clean view model, but not "perfect".
I would place the responsibility of the observing changes to the model object (Book) with the View.
Also, Variable is deprecated, best to use PublishRelay instead.
Of course, it depends how far you want to engineer this architecture, but something not too far from your example would be:
class BookDetailViewController: UIViewController {
let viewModel = BookViewModel(book: Book, booksService: BooksService)
func loadView() {
view = BookDetailView(viewModel: viewModel)
}
// ...
}
class BookDetailViewModel {
let book: PublishRelay<Book>
func addBook() {
book
.flatMap(booksService.add)
.bind(to: book)
.subscribe()
}
// ...
}
class BookDetailView: UIView {
let button: UIButton
init(viewModel: BookDetailViewModel) {
viewModel.book
.asObservable()
.subscribe(onNext: { book [button] in
button.setText(book.isSaved ? "Remove" : "Add")
})
button.rx.tap
.map { _ in viewModel.book.isSaved }
.subscribe(onNext: {
$0 ? viewModel.removeBook() : viewModel.addBook()
})
}
}
You could also implement a func toggle() in the view model instead, and just forward the button tap to call that method. It might be more accurate, semantically, depending on your interpretation of business logic and the extent to which you want to gather all of it in the view model.
Also note the example code is missing dispose bags, but that's another topic.

Chain of delegates in Swift

TDLR; I have three classes, when Class A object is updated, it's calling on its delegate (Class B) which is calling on its delegate (Class C) without doing anything else. Class C will use Class B in different ways depending on the values in Class A. Class B need to know of its Class A at touch events. Is this acceptable?
classA { var information: String }
classB { var thing: ClassA thing.delegate = self }
classC { var things: [ClassB] for thing in things { thing.delegate = self } }
My real example
I have three classes: A mapViewController, a mapMarker and a place (model). The map holds multiple mapMarkers, and every mapMarker has a property place, which contains information of what the marker should look like (like place type, "bar", "restaurant" etc). The place might receive new information via a silent push notification, and hence being updated. When the place is updated, I need to notify the mapViewController that the marker needs to be redrawn (I'm using MapBox and their annotations doesn't support redrawing in any way but removing and adding the marker again, since the imageForAnnotation method is a delegate one.)
My first thought was to make two protocols placeDelegate and mapMarkerDelegate.
Place:
protocol PlaceDelegate: class
{
func placeUpdated()
}
class Place {
weak var delegate: PlaceDelegate?
var propertyThatCanBeUpdate: String {
didSet {
//Checking if the newValue == oldValue
delegate.placeUpdated()
}
}
MapMarker
protocol MapMarkerDelegate: class
{
markerShouldReDraw(mapMarker: MapMarker)
}
class MapMarker: PlaceDelegate {
var place: Place!
weak var delegate: MapMarkerDelegate?
init(place: Place) {
self.place = place
place.delegate = place
}
func placeUpdate()
{
delegate.markerShouldReDraw(self)
}
}
MapViewController
class MapViewController {
//I could easily set the marker.delegate = self when adding the markers
func markerShouldReDraw(mapMarker: MapMarker)
functionForRedrawingMarker()
}
This feels a bit ugly, and a bit weird that the MapMarker is just passing the "my place has been updated" information forward. Is this acceptable as far as performance goes? Should I use some kind of NSNotification instead? Should I make the MapViewController the delegate of place and search my array of mapMarker for the one holding the correct place?

Resources