I'm calling a selector inside of a closure, but the selector won't trigger inside of the closure but outside of it works fine.
The closure functions "scrollAllFaces" works as expected
Could it be that #selector() can't work inside of closures?
Any way of making it work or workaround?
override class var defaultTestSuite: XCTestSuite {
let testSuite = XCTestSuite(forTestCaseClass: ClockFaceBridgeTests.self)
ClockFaceBridgeTests().app.launch()
ClockFaceBridgeTests().scrollAllFaces { sectionTitle, currentFace in
testSuite.addTest(ClockFaceBridgeTests(selector: #selector( ClockFaceBridgeTests().testAllFacesAndNewFacesAvailable )))
}
return testSuite
}
The "scrollAllFaces" function:
func scrollAllFaces(untilSectionIndex: Int = BridgeApplication.facesCaterogies.count, completion: #escaping (String, XCUIElement) -> Void) {
try? app.stimulateDataFetching()
var currentSectionIndex = 0
while currentSectionIndex < untilSectionIndex - 1 {
let section = BridgeFaceGalleryView.shared.table.cells.element(boundBy: currentSectionIndex)
let sectionTitle = section.staticTexts.firstMatch.label.description
guard sectionTitle != "Photos" ||
FaceDetailsView.shared.parentApp.staticTexts["You must choose photos."].doesNotExist else {
return
}
section.scrollToVisible()
for index in 0..<section.cells.count {
let currentCell = section.cells.element(boundBy: index)
currentCell.scrollToVisible()
completion(sectionTitle, currentCell)
}
currentSectionIndex += 1
}
}
The "testAllFacesAndNewFacesAvailable" function:
#objc func testAllFacesAndNewFacesAvailable() {
print("Function called")
}
Related
I hope this doesn't sound dumb but I'm trying to put:
let lowercasedQuery = query.lowercased()
let usersNew = users.filter({ $0.fullname.lowercased().contains(lowercasedQuery) || $0.username.contains(lowercasedQuery) })
into the DispatchQueue function but obviously, since they are constants declared in the function, the function is out of scope for the return line.
func filteredUsers(_ query: String) -> [User] {
let delay = 3.3
DispatchQueue.main.asyncAfter(deadline: .now() + delay)
{
}
let lowercasedQuery = query.lowercased()
let usersNew = users.filter({ $0.fullname.lowercased().contains(lowercasedQuery) || $0.username.contains(lowercasedQuery) })
return usersNew
}
Does anyone know how to solve this?
Thanks!
You need a closure... more info here. Instead of return, call the completion handler closure.
func filteredUsers(_ query: String, completion: #escaping (([User]) -> Void)) {
let delay = 3.3
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
let lowercasedQuery = query.lowercased()
let usersNew = self.users.filter({ $0.fullname.lowercased().contains(lowercasedQuery) || $0.username.contains(lowercasedQuery) })
completion(usersNew)
}
}
Usage:
viewModel.filteredUsers(searchText) { users in
print(users) /// get your users here!
}
If you are trying to return users inside another function, it won't work. You also need to a add a closure to that function:
/// another closure here
func mainFunction(_ query: String, completion: #escaping (([User]) -> Void)) {
viewModel.filteredUsers(query) { users in
completion(users) /// equivalent to `return users`
}
}
mainFunction("searchText") { users in
print(users) /// get your users here!
}
/// NOT `let users = mainFunction("searchText")`
I want to be able to chain functions as seen below and choose between them. In this contrived example, I hardcoded "function_b", but in the ideal world, I'd like it to be random or even controlled by a server. I know that I can create the same effect in different ways, but I like the way this code reads, so I want to do it like this.
ExecutionManager("function_b").add("function_a") {
//some code
}.add("function_b") {
//more code
}.add("function_c") {
//other code
}
Your snipped of code does not make much sense to me. E.g. what should mean this part?
ExecutionManager("function_b")
You are just passing a string to the initializer of ExecutionManager. What should be the meaning of that?
Let's try
However if you want to be able to add a list of functions with this type
() -> ()
and then execute them (all of them or just some of them) you could define your ExecutionManager like this
class ExecutionManager {
typealias FunctionType = () -> ()
private var functions = [(String, FunctionType)]()
func add(funcName: String, function: FunctionType) -> ExecutionManager {
functions.append(funcName, function)
return self
}
func runAll() {
functions.forEach { $0.1() }
}
}
Now you can
ExecutionManager().add("sayHello") {
print("hello")
}.add("sum 1 + 1") {
let sum = 1 + 1
print(sum)
}.add("say goodbye") {
print("goodbye")
}.runAll()
The result is
hello
2
goodbye
Random
To run a only a function based on some logic look at this code. here I am generating a random index and the executing only one function
Extension
extension ExecutionManager {
func runRand() {
guard functions.isEmpty == false else { return }
let rand = Int(arc4random_uniform(UInt32(functions.count)))
functions[rand].1()
}
}
Example
ExecutionManager().add("sayHello") {
print("hello")
}.add("sum 1 + 1") {
let sum = 1 + 1
print(sum)
}.add("say goodbye") {
print("goodbye")
}.runRand()
Output
2
Update
With this version we remove the invocation of runRand at the end
#HyperZ Thanks for the hint.
import Foundation
class ExecutionManager {
typealias FunctionType = () -> ()
private var functions = [(String, FunctionType)]()
func add(funcName: String, last:Bool=false, function: FunctionType) -> ExecutionManager {
functions.append(funcName, function)
if last {
runRand()
}
return self
}
func runRand() {
guard functions.isEmpty == false else { return }
let rand = Int(arc4random_uniform(UInt32(functions.count)))
functions[rand].1()
}
}
Code
ExecutionManager().add("sayHello") {
print("hello")
}.add("sum 1 + 1") {
let sum = 1 + 1
print(sum)
}.add("say goodbye", last:true) {
print("goodbye")
}
Output
hello
I'm trying to inject a fake instance into a unit test for a class depending on SimplePing, a NSObject subclass. My class has a property var simplePings: [SimplePing] which in my unit test I set as an array of FakeSimplePing. However, when the class goes in the array and calls simplePing.start(), it calls the SimplePing.start implementation instead of FakeSimplePing's, even though when I debug I see that the instance type is FakeSimplePing.
When the property is just a single SimplePing, the unit test uses the FakeSimplePing.start and the test passes. Does this have something to do with Swift and arrays of superclasses?
class Pinger : NSObject {
private var simplePings: [SimplePing] = []
func pingLocation(location: Location) -> Signal<Double, NoError> {
let simplePings = location.serverIPs.map { (serverIP: String) -> SimplePing in
let simplePing = SimplePing(hostName: serverIP)
simplePing?.delegate = self
return simplePing
}
configureDependencies(simplePings)
simplePings.forEach { $0.start() }
return signal
}
func configureDependencies(simplePings: [SimplePing]) {
if self.simplePings.isEmpty {
self.simplePings = simplePings
}
}
}
class FakeSimplePing: SimplePing {
var receivedStart = false
var receivedSendPingWithData = false
var fakeHostName: String!
override var hostName: String {
return fakeHostName
}
convenience init(hostName: String) {
self.init()
fakeHostName = hostName
}
override func start() {
// This does not get called
receivedStart = true
delegate?.simplePing?(self, didStartWithAddress: nil)
delegate?.simplePing?(self, didReceivePingResponsePacket: nil)
}
override func sendPingWithData(data: NSData!) {
receivedSendPingWithData = true
}
}
And the failing test:
beforeEach {
fakeSimplePing = FakeSimplePing(hostName: serverIP)
fakeSimplePing.delegate = pinger
pinger.configureDependencies([fakeSimplePing])
}
it("pings server with data") {
pinger.pingLocation(location)
expect(fakeSimplePing.receivedSendPingWithData).toEventually(beTrue())
}
The problem (I believe...) is in the naming in pingLocation
in the line
let simplePings = location.serverIPs.map { ....
you use the same name as of your property
private var simplePings: [SimplePing] = []
so you may think you're defining a new variable with the let, but actually, you may just use your property and change it on the way, so it got changed to SimplePing array, as it returns from the map
try to change your method into:
func pingLocation(location: Location) -> Signal<Double, NoError> {
let tempSimplePings = location.serverIPs.map { (serverIP: String) -> SimplePing in
let simplePing = SimplePing(hostName: serverIP)
simplePing?.delegate = self
return simplePing
}
configureDependencies(tempSimplePings)
simplePings.forEach { $0.start() }
return signal
}
I use playground to play a little with sequences and generators.
So I created function that returns GeneratorOf struct that suppose to implement GeneratorType and SequenceType. I expect that generate will give me new full generator that I can traverse through again. But it didn't for my countDownGen2.
I suppose that I misunderstood something.
What is wrong here? and how to use GeneratorOf properly?
func countDown(start: Int) -> GeneratorOf<Int>
{
var i = start
return GeneratorOf {return i < 0 ? nil : i-- }
}
var countDownGen = countDown(10)
for i in countDownGen
{
println(i)
}
var countDownGen2 = countDownGen.generate()
for i in countDownGen2
{
println(i)
}
GeneratorOf<T>.generate() returns a copy of itself, and in your code, every copy of it shares the same reference to one i
So, when you do countDownGen2 = countDownGen.generate() after countDownGen is exhausted, countDownGen2 is also already exhausted.
What you should do is something like:
func countDown(start: Int) -> SequenceOf<Int> {
return SequenceOf { () -> GeneratorOf<Int> in
var i = start
return GeneratorOf { return i < 0 ? nil : i-- }
}
}
let countDownSeq = countDown(10)
for i in countDownSeq {
println(i)
}
for i in countDownSeq {
println(i)
}
let countDownSeq2 = countDownSeq
for i in countDownSeq2 {
println(i)
}
I need to convert the following convenience initializer from Swift to Objective-C
convenience init(view: UIView, overlapHandler: (CGRect)->()) {
self.init(view: view, handler: { overlap -> () in
overlapHandler(overlap ?? CGRect.nullRect)
})
}
My first attempt was:
-(instancetype) initWithView:(UIView*)view overlapHandler:(Handler)handler
{
Handler overlapHandler = ^(CGRect overlap) {
if (CGRectIsNull(overlap)) {
return;
}
handler(overlap);
};
return [self initWithView:view andHandler:overlapHandler];
}
But it didn't work well as I don't see how an Objective-C block can receive a non-optional CGRect.
The context of the code is below:
import UIKit
class KeyboardOverlapNotifier:NSObject {
typealias Handler = (CGRect?) -> ()
let view:UIView
let handler:Handler
init(view:UIView, handler:Handler) {
self.view = view
self.handler = handler
super.init()
beginListening()
}
deinit {
stopListening()
}
func beginListening() {
token = token ?? nc.addObserverForName(UIKeyboardWillChangeFrameNotification,
object:nil, queue:nil, usingBlock:keyboardFrameDidChange)
}
func stopListening() {
if let observer = token {
nc.removeObserver(observer)
token = nil
}
}
private let nc:NSNotificationCenter = NSNotificationCenter.defaultCenter()
private var token:NSObjectProtocol?
}
private extension KeyboardOverlapNotifier {
func keyboardFrameDidChange(notification: NSNotification!) {
let duration = notification.userInfo!
[UIKeyboardAnimationDurationUserInfoKey] as NSTimeInterval
UIView.animateWithDuration(duration,
delay: 0,
options: .BeginFromCurrentState,
animations: {
let keyboard:CGRect = {
let global = (notification.userInfo!
[UIKeyboardFrameEndUserInfoKey] as NSValue).CGRectValue()
let local = self.view.convertRect(global, fromView: nil)
return local
}()
let overlap = self.view.bounds.rectByIntersecting(keyboard)
self.handler(overlap.nonEmptyOrNil)
},
completion: nil)
}
}
extension KeyboardOverlapNotifier {
convenience init(view: UIView, overlapHandler: (CGRect)->()) {
self.init(view: view, handler: { overlap -> () in
overlapHandler(overlap ?? CGRect.nullRect)
})
}
}
extension CGRect {
var nonEmptyOrNil:CGRect? { return !self.isEmpty ? self : nil }
}
Perhaps this should help? I'm still unsure what your question actually is so the only thing I can offer is references to each.
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables, hence the name “closures”. Swift handles all of the memory management of capturing for you.
Blocks vs Closures
https://www.codefellows.org/blog/writing-completion-blocks-with-closures-in-swift
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html