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
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:
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 aweak self
specifier).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:
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.
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
(ormap
orforEach
), 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 withnewPeripheral in
. That gives a name to the parameter for that closure, so you can refer to it bynewPeripheral
, but then when you callcontains
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
andguard
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 tofilter
. In this case you’re capturingself
as aweak
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, plusArray
,Dictionary
,String
,Set
and anystruct
orenum
you define are all value types, so it doesn’t make sense to take aweak
reference to them. In fact, the only reference that occurs for them is when they’re passed as aninout
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 ofclass
.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 aweak
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, andweak
reference for its previous node (or the other way around, as long as there’s only one strong reference).weak
references use theOptional
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 toself
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 toself
in this case.I didn’t mention
unowned
references, but they’re kind of likeweak
references that are implicitly unwrapped.guard
guard
is basically an invertedif
statement with the added requirement that itselse
block mustreturn
,throw
or call a function that returnsNever
, which is how a function indicates that it never returns, either because it terminates the program, likefatalError()
does, or because it enters an infinite loop for some reason. In loops,guard
‘selse
block can alsobreak
orcontinue
, and inswitch
statements, it canbreak
orfallthrough
.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.
Basically
is equivalent to
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 theelse
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
orif
with optional binding, there is a difference.