Remove a specific list of SKAction from a SKNode - ios

During the development of some interactions between different nodes, I realized I need to remove a specific list of actions from a node.
The current version of Sprite-Kit framework provides some instance methods as:
removeAllActions()
action(forKey key: String)
hasActions()
Obviously every action that running in my node has a String key for its identifications.
So I thought of something that was very similar to removeAllAction, then I made an SKNode extension:
public extension SKNode {
func removeAllAction(in list:[String]) {
list.forEach { if self.action(forKey: $0) != nil { self.action(forKey: $0)?.speed = 0.0; self.removeAction(forKey: $0)}}
}
}
And in my project I can use it as:
let actionList = ["idle_walk_sx","idle_walk_dx","walk_dx","walk_sx","walk_idle_sx","walk_idle_dx","rotate_sx_dx","rotate_dx_sx"]
self.removeAllAction(in: actionList)
The code works well.
But I'm not really sure about two factors:
the effectiveness of the speed corrections (to zero), I thought it
appropriate to introduce it in the face of repeated actions. Should I remove it due to avoid strangness or leave it ?
How is it possible to extend this extension (expanding it) to any
childrens that have the same list to remove?

I agree with the comments. Probably speed is not necessary so the code + the scanning to the children nodes could be something like:
public extension SKNode {
func removeAllAction(in list:[String]) {
list.forEach { if self.action(forKey: $0) != nil { self.removeAction(forKey: $0)}}
self.children
.filter { $0.hasActions() }
.forEach { $0.removeAllAction(in: list) }
}
}

Related

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.

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.

Multiple bindings and disposal using "Boxing"-style

This is a very specific and long question, but I'm not smart enough to figure it out by myself..
I was very intrigued by this YouTube-video from raywenderlich.com, which uses the "boxing" method to observe a value.
Their Box looks like this:
class Box<T> {
typealias Listener = T -> Void
var listener: Listener?
var value: T {
didSet {
listener?(value)
}
init(_ value: T) {
self.value = value
}
func bind(listener: Listener?) {
self.listener = listener
listener?(value)
}
}
It's apparent that only one listener is allowed per "box".
let bindable:Box<String> = Box("Some text")
bindable.bind { (text) in
//This will only be called once (from initial setup)
}
bindable.bind { (text) in
// - because this listener has replaced it. This one will be called when value changes.
}
Whenever a bind like this is set up, the previous binds would be disposed, because Box replaces the listener with the new listener.
I need to be able to observe the same value from different places. I have reworked the Box like this:
class Box<T> {
typealias Listener = (T) -> Void
var listeners:[Listener?] = []
var value:T{
didSet{
listeners.forEach({$0?(value)})
}
}
init(_ value:T){
self.value = value
}
func bind(listener:Listener?){
self.listeners.append(listener)
listener?(value)
}
}
However - this is also giving me trouble, obviously.. There are places where I want the new binding to remove the old binding. For example, if I observe a value in an object from a reusable UITableViewCell, it will be bound several times when scrolling. I now need a controlled way to dispose specific bindings.
I attempted to solve this by adding this function to Box:
func freshBind(listener:Listener?){
self.listeners.removeAll()
self.bind(listener)
}
This worked in a way, I could now use freshBind({}) whenever I wanted to remove the old listeners, but this isn't exactly what I want either. I'd have to use this when observing a value from UITableViewCells, but I also need to observe the same value from elsewhere. As soon as a cell was reused, I removed the old observers as well as the other observers I needed.
I am now confident that I need a way to retain a disposable object wherever I would later want to dispose them.
I'm not smart enough to solve this on my own, so I need help.
I've barely used some of the reactive-programming frameworks out there (like ReactiveCocoa), and I now understand why their subscriptions return Disposable objects which I have to retain and dispose of when I need. I need this functionality.
What I want is this: func bind(listener:Listener?)->Disposable{}
My plan was to create a class named Disposable which contained the (optional) listener, and turn listeners:[Listener?] into listeners:[Disposable], but I ran into problems with <T>..
Cannot convert value of type 'Box<String?>.Disposable' to expected argument type 'Box<Any>.Disposable'
Any smart suggestions?
The way I like to solve this problem is to give each observer a unique identifier (like a UUID) and use that to allow the Disposable to remove the observer when it's time. For example:
import Foundation
// A Disposable holds a `dispose` closure and calls it when it is released
class Disposable {
let dispose: () -> Void
init(_ dispose: #escaping () -> Void) { self.dispose = dispose }
deinit { dispose() }
}
class Box<T> {
typealias Listener = (T) -> Void
// Replace your array with a dictionary mapping
// I also made the Observer method mandatory. I don't believe it makes
// sense for it to be optional. I also made it private.
private var listeners: [UUID: Listener] = [:]
var value: T {
didSet {
listeners.values.forEach { $0(value) }
}
}
init(_ value: T){
self.value = value
}
// Now return a Disposable. You'll get a warning if you fail
// to retain it (and it will immediately be destroyed)
func bind(listener: #escaping Listener) -> Disposable {
// UUID is a nice way to create a unique identifier; that's what it's for
let identifier = UUID()
// Keep track of it
self.listeners[identifier] = listener
listener(value)
// And create a Disposable to clean it up later. The Disposable
// doesn't have to know anything about T.
// Note that Disposable has a strong referene to the Box
// This means the Box can't go away until the last observer has been removed
return Disposable { self.listeners.removeValue(forKey: identifier) }
}
}
let b = Box(10)
var disposer: Disposable? = b.bind(listener: { x in print(x)})
b.value = 5
disposer = nil
b.value = 1
// Prints:
// 10
// 5
// (Doesn't print 1)
This can be nicely expanded to concepts like DisposeBag:
// Nothing but an array of Disposables.
class DisposeBag {
private var disposables: [Disposable] = []
func append(_ disposable: Disposable) { disposables.append(disposable) }
}
extension Disposable {
func disposed(by bag: DisposeBag) {
bag.append(self)
}
}
var disposeBag: DisposeBag? = DisposeBag()
b.bind { x in print("bag: \(x)") }
.disposed(by: disposeBag!)
b.value = 100
disposeBag = nil
b.value = 500
// Prints:
// bag: 1
// bag: 100
// (Doesn't print "bag: 500")
It's nice to build some of these things yourself so you get how they work, but for serious projects this often is not really sufficient. The main problem is that it isn't thread-safe, so if something disposes on a background thread, you may corrupt the entire system. You can of course make it thread-safe, and it's not that difficult.
But then you realize you really want to compose listeners. You want a listener that watches another listener and transforms it. So you build map. And then you realize you want to filter cases where the value was set to its old value, so you build a "only send me distinct values" and then you realize you want filter to help you there, and then....
you realize you're rebuilding RxSwift.
This isn't a reason to avoid writing your own at all, and RxSwift includes a ton of features (probably too many features) that many projects never need, but as you go along you should keep asking yourself "should I just switch to RxSwift now?"

Index Of in Cocoa

I'm used to making iPhone apps but now I need to make a Mac app.
I thus have to switch to the Cocoa framework.
Is there anything similar to the array.index(of: ) in Cocoa?
iOS example:
let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.index(of: needle) {
let pos = string.characters.distance(from: string.startIndex, to: idx)
print("Found \(needle) at position \(pos)")
}
else {
print("Not found")
}
The actual code I'm having problems with is here:
func checkBoxTapped(_ sender: NSButton) {
if (sender.state == 1) {
self.selectedReferrals.append(self.referralApplicationList[sender.tag])
}
if (sender.state == 0) {
// Remove
// self.referralApplicationList[sender.tag]
// let foofoo = self.referralApplicationList.indexOf(self.referralApplicationList[sender.tag])
}
}
Your question has nothing to do with Cocoa or macOS versus iOS at all. Really what you are asking is how to get an index(of:) function on a Swift array, and the answer to that is the elements in the array need to be Equatable.
In your iOS example, characters is an instance of String.CharacterView which explicitly has an index(of:) function.
In your Mac example, in which you don't provide enough info, the selectedReferrals variable is presumably an Array of some Referral type; eg a [Referral]. For there to be a similar index(of:) your Referral type needs to implement the Equatable protocol. Otherwise you'd need to use something like index(where:) to find the one you want.

Swift 2.x: searching the node tree for multiple hits

I want to search my SKScene class for childNodes that begin with "spaceship". Basically I have several spaceship nodes named "spaceship1", "spaceship2", "spaceship3", etc...
However I'm not getting the syntax right. This:
self.subscript("spaceship[0-9]")
results to :
Expected ',' separator
And this:
self.objectForKeyedSubscript("spaceship[0-9]")
Results to :
'objectForKeyedSubscript' is unavailable: use subscripting
There's a better approach to this problem than assigning tags like "spaceship" + counter.
The Spaceship class
Yes, for a number of reasons you should create a Spaceship class, like this
class Spaceship: SKSpriteNode {
init() {
let texture = SKTexture(imageNamed: "spaceship")
super.init(texture: texture, color: .clearColor(), size: texture.size())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Retrieving all the spaceships
class GameScene:SKScene {
var spaceships: [Spaceship] {
return self.children.flatMap { $0 as? Spaceship }
}
}
Why a custom class is better than "numbered tags"
For several reasons
You don't get crazy assigning a new tag-with-counter value to each new sprite that should act like a Spaceship
You can add behaviour to your spaceship entity simply adding methods to the Spaceship class.
The compiler will block you if you erroneously use another node as a spaceship
You code is cleaner
This is a great handy reference for working with Strings in Swift.
http://useyourloaf.com/blog/swift-string-cheat-sheet/
As per that site
let spaceshipString = "spaceship1"
spaceshipString.hasPrefix("spaceship") // true
spaceshipString.hasSuffix("1") // true
With that in mind, you can just enumerate through all your nodes to find the ones with spaceship by doing the following.
func findSpaceShipNodes() {
self.enumerateChildNodesWithName("//*") {
node, stop in
if (( node.name?.hasSuffix("spaceship") ) != nil) {
// Code for these nodes in here //
}
}
}

Resources