skip to Main Content

I have literally no knowledge on Swift or OOP but I have to build a basic BLE Scanner app. I found an example, I read it, tried to understand and develop from where it stands.

Now, I have a function like this

 @objc private func startSearch() {
    bluetoothProvider.startScanning()
        .filter { [weak self] newPeripheral in
            guard let self = self else { return false }
            return !self.scannedPeripherals.contains(where: { $0.peripheral.identifier == newPeripheral.peripheral.identifier
            })
        }
    
        .subscribe(onNext: { [weak self] in self?.scannedPeripherals.append($0) })
        .disposed(by: disposeBag)
}

I literally didn’t understand anything from week, guard part… But I guess I got the the other stuffs. Now I wanna add at least an other filter, but it doesn’t look any other filter mechanism that I can find in tutorials or examples. Can anyone explain the format here or help to add another filter on it?

I tried those these

        .filter { [weak self] filteredPeripheral in
            guard let self = self else { return false }
            return !self.scannedPeripherals.contains(where: { _ in filteredPeripheral.rssi.intValue < 0
            })
                }


        .filter{ [weak self] filteredPeripheral in
            guard let self = self else { return false }
            return !self.scannedPeripherals.contains(where: { $0.peripheral.identifier.uuidString.contains("0863")
            })
                }

but nothing looks effected.

2

Answers


  1. I’m not 100% positive what you are trying to do without more context, but my hunch is you are trying to filter out certain peripherals that don’t match these patterns. Your approach is generally, correct, but you are checking the already scanned peripherals, rather than the new incoming ones. Try adding this filter:

    .filter { newPeripheral in
        return (newPeripheral.rssi.intValue < 0 &&
                newPeripheral.peripheral.identifier.uuidString.contains("0863"))
    }
    

    The first filter from the code you found seems to just be checking for duplicates, which is why it is looking at scannedPeripherals (and also why it needs a weak self specifier).

    Login or Signup to reply.
  2. You certainly can chain together filters. From your comments, I don’t think you need the any references to self in the second or third filters:

    @objc private func startSearch()
     {
        // You don't need [weak self]
        bluetoothProvider.startScanning()
        .filter
        { newPeripheral in
            return !self.scannedPeripherals.contains {
                $0.peripheral.identifier == newPeripheral.peripheral.identifier
            }
        }.filter { $0.rssi.intValue >= 0 }
        .filter { $0.peripheral.identifier.uuidString.contains("0863") }
    }
    

    Since you’re not composing the filters dynamically and not mapping to different types in between, you’ll get slightly better performance by condensing them into one filter.

    @objc private func startSearch()
     {
        bluetoothProvider.startScanning().filter
        { newPeripheral in
            !self.scannedPeripherals.contains {
                $0.peripheral.identifier == newPeripheral.peripheral.identifier
             }
            && newPeripheral.rssi >= 0
            && peripheral.identifier.uuidString.contains("0863")
        }
    }
    

    Since you’re new to Swift, I’d like to make a recommendation for learning closures. Start by writing a named function that you pass to filter (or other methods that take a closure parameter). Get that to work. Once you have the named function working, then turn it into a closure.

    You could even take it a step further, by writing out a loop that does what you want it to do. The body of the loop is essentially what will go into your closure. Then move the loop’s body into a named function, so your loop just calls it for each element. When that works, change the loop into a call to filter (or map or forEach), and pass your named function to it. When that works, change your function into a closure.

    Over time you’ll become comfortable enough with closures that the intermediate refractoring won’t be needed. But at first it’s helpful because it starts with something you already understand and works toward the new thing.

    Just in case you haven’t picked up on it yet, I’ll also mention that where you see $0, that’s a reference to the first parameter passed to a closure when the closure’s definition doesn’t assign a name to it. In your the first closure, ignoring the unnecessary [weak self], you start with newPeripheral in. That gives a name to the parameter for that closure, so you can refer to it by newPeripheral, but then when you call contains you’re passing another closure nested inside the first one. That nested closure doesn’t name its parameter, so it uses $0 to refer to it. For closures that take more than one parameter, there can also be $1, $2, etc…

    Also, if you’re familiar with the idea of "callback" functions in C, that’s basically what closures are often used for, except unlike C function pointers, they don’t have to refer to a named function, but can be defined on the fly, and closures can capture variables from the surrounding scope.

    weak

    Although your question wasn’t actually about weak and guard you mention that you didn’t understand them so I thought I’m explain them briefly.

    [weak self] is "capture syntax" – basically it’s a way of telling the compiler how to import symbols from the surrounding context into the closure you’re passing to filter. In this case you’re capturing self as a weak reference.

    To understand what a weak reference is, it’s useful to know what a reference is. A reference is Swift’s way to referring to something that’s dynamically allocated in the heap. In that way, it’s like the main use of pointers in C. All of the primitive types, plus Array, Dictionary, String, Set and any struct or enum you define are all value types, so it doesn’t make sense to take a weak reference to them. In fact, the only reference that occurs for them is when they’re passed as an inout parameter.

    Unless you venture into the realm of the UnsafePointer family of types, the only reference types you’re likely to use in Swift are some kind of class.

    A normal reference, often called a strong reference, increases a reference count for the thing being referred to. When it goes out of scope it decrements the reference count. When the reference count goes to 0, the thing it refers to is deinitialized and deallocated. That’s the heart of Swift’s memory management.

    However, there are circumstances where you can create strong reference cycles. Imagine a tree data structure where children keep a reference to their parents. In that case, if the root node goes out of scope, the tree, if it has more than just a root node, would never be deallocated, because the reference counts never go to 0, because the children hold strong references to the parent.

    weak references are the way to solve that problem, because they don’t increase the reference count, so in the tree data structure, you’d want each node to have strong references to its children, but a weak reference to its parent. That way, when the root of the tree goes out of scope, the whole tree is deallocated. Same idea would be true for a doubly-linked list. Each node would have a strong reference for its next node, and weak reference for its previous node (or the other way around, as long as there’s only one strong reference).

    weak references use the Optional syntax because the thing they refer to might not exist when you want to access it.

    Strong reference cycles can also happen with a type of closure called an "escaping" closure. Sometimes you’ll see @escaping in the declaration for functions that take closure parameters, often for completion handlers. An "escaping" closure is one that is stored away to be called later. In that case any strong references to self inside the closure could prevent the object its referring to from being deinitialized and deallocated as long as the closure is being kept somewhere. Basically a memory leak.

    filter doesn’t take an @escaping closure. It doesn’t store the closure, it calls it before returning, and then it’s done with it. So its declaration doesn’t include @escaping, and there’s no need to capture a weak reference to self in this case.

    I didn’t mention unowned references, but they’re kind of like weak references that are implicitly unwrapped.

    guard

    guard is basically an inverted if statement with the added requirement that its else block must return, throw or call a function that returns Never, which is how a function indicates that it never returns, either because it terminates the program, like fatalError() does, or because it enters an infinite loop for some reason. In loops, guard‘s else block can also break or continue, and in switch statements, it can break or fallthrough.

    You mention in comments that you’ve done embedded C. C programs often have "guard" ifs to exit early for error conditions, or for easy cases to move them out of the main logic.

    int find(char x, const char* s)
    {
        if (s == NULL) return -1; // This is a guard "if" in C
    
        for(int i = 0; s[i] != 0; ++i) {
            if (s[i] == x) return i;
        }
    
        return -1;
    }
    

    Basically

    guard condition else { return }
    

    is equivalent to

    if !condition { return }
    

    Think of guard as saying that its condition must be true in order to get to the main code that follows, otherwise control passes to the else block, which has to somehow get out of the current context.

    Often you could use either one with equal ease, but if you combine guard or if with optional binding, there is a difference.

    guard let x = foo() else { return }
    
    // x is defined from here on
    
    if let y = foo() {
        // y is defined here
    }
    
    // y is not defined here
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search