How can I return from closure in swift?
func closure(result: (Int -> Void)) {
var next = 1
while (true) {
result(next)
}
}
This is the invocation of the function
closure() { result in
// here I need to return
}
There is no return from the while loop in the question. In order to return there must be some conditional statement that will exit the while loop, something like:
while (true) {
result(next)
if something == false {
break;
}
}
or:
var i = 0
while (true) {
if i++ == 4 {
break;
}
print(i)
}
or:
var i = 0
while (i <
print(i)
}
Related
I'm trying to return a value produced from an asynchronous block of code (from my completion handler) for my function validateFields(), however I'm not sure how to do that.
func validateFields() -> Bool
{
//Other else if statements
//...
else if !(usernameTextField.text!.isEmpty)
{
var retVal = false
isUnique { (bool) in
retVal = bool
}
print("THIS IS THE RET VALUE: " + String(retVal))
//this print statement does not return the correct value
if retVal == false { return retVal }
}
errorLabel.text = " "
return true
}
As you can see, it doesn't work, I need to return bool in isUnique for my entire function.
You can't store the result of isUnique's closure then instantly return it, because isUnique will take as long as it takes to complete whatever task that is.
You want something like the following, where completion is called on all paths, but only once:
func validateFields(completion: (Bool) -> Void) {
//Other else if statements
//...
if ... {
/* ... */
} else if !(usernameTextField.text!.isEmpty) {
var retVal = false
isUnique { (bool) in
print("THIS IS THE RET VALUE: " + String(bool))
completion(bool)
}
} else {
errorLabel.text = " "
completion(true)
}
}
Caller:
validateFields { result in
print("result: \(result)")
}
How do I make a custom completion handler for the below function? This is writing to a websocket via Starscream and I want to receive a response if it isn't nil.
open func write(string: String, completion: (() -> ())? = nil) {
guard isConnected else { return }
dequeueWrite(string.data(using: String.Encoding.utf8)!, code: .textFrame, writeCompletion: completion)
}
and here is deqeueWrite func:
private func dequeueWrite(_ data: Data, code: OpCode, writeCompletion: (() -> ())? = nil) {
let operation = BlockOperation()
operation.addExecutionBlock { [weak self, weak operation] in
//stream isn't ready, let's wait
guard let s = self else { return }
guard let sOperation = operation else { return }
var offset = 2
var firstByte:UInt8 = s.FinMask | code.rawValue
var data = data
if [.textFrame, .binaryFrame].contains(code), let compressor = s.compressionState.compressor {
do {
data = try compressor.compress(data)
if s.compressionState.clientNoContextTakeover {
try compressor.reset()
}
firstByte |= s.RSV1Mask
} catch {
// TODO: report error? We can just send the uncompressed frame.
}
}
let dataLength = data.count
let frame = NSMutableData(capacity: dataLength + s.MaxFrameSize)
let buffer = UnsafeMutableRawPointer(frame!.mutableBytes).assumingMemoryBound(to: UInt8.self)
buffer[0] = firstByte
if dataLength < 126 {
buffer[1] = CUnsignedChar(dataLength)
} else if dataLength <= Int(UInt16.max) {
buffer[1] = 126
WebSocket.writeUint16(buffer, offset: offset, value: UInt16(dataLength))
offset += MemoryLayout<UInt16>.size
} else {
buffer[1] = 127
WebSocket.writeUint64(buffer, offset: offset, value: UInt64(dataLength))
offset += MemoryLayout<UInt64>.size
}
buffer[1] |= s.MaskMask
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
_ = SecRandomCopyBytes(kSecRandomDefault, Int(MemoryLayout<UInt32>.size), maskKey)
offset += MemoryLayout<UInt32>.size
for i in 0..<dataLength {
buffer[offset] = data[i] ^ maskKey[i % MemoryLayout<UInt32>.size]
offset += 1
}
var total = 0
while !sOperation.isCancelled {
let stream = s.stream
let writeBuffer = UnsafeRawPointer(frame!.bytes+total).assumingMemoryBound(to: UInt8.self)
let len = stream.write(data: Data(bytes: writeBuffer, count: offset-total))
if len <= 0 {
var error: Error?
let errCode = InternalErrorCode.outputStreamWriteError.rawValue
error = s.errorWithDetail("output stream error during write", code: errCode)
s.doDisconnect(error)
break
} else {
total += len
}
if total >= offset {
if let queue = self?.callbackQueue, let callback = writeCompletion {
queue.async {
callback()
}
}
break
}
}
}
writeQueue.addOperation(operation)
}
So right now I can call this function like this:
socket.write(string: frameJSONSring) { () -> Void in
}
But I'd like a response in that handler so that I can read the response data (if there is any) from the socket. Apparently I can pass a custom response handler as a parameter when calling:
socket.write(string: frameJSONSring) { (CUSTOM_HANDLER_HERE) -> Void in
}
open func write(string: String, completion: ((Int) -> ())?) {
guard isConnected else { return }
let someParameter = 5
dequeueWrite(string.data(using: String.Encoding.utf8)!, code: .textFrame, writeCompletion: completion(someParameter))
}
Notice I:
added an Int as a parameter you pass to the handler.
changed completion to completion(someParameter)
You can then use it like such:
socket.write(string: frameJSONSring) { number in
print(number)
}
You can replace the Int with any other type you like.
Also no need to do = nil. When something is an optional then it's already defaulted to nil.
For a String which have both String and Int values (one of each) is it possible to do simple sort that will give the items ordered in numerical order as the primary order and alphabetical as the secondary order
var nameArray = ["Dave7", "Bob8", "Cathy9", "Henry10", "Susan10", "Pat11", "Steve12", "Dan12", "Ken1", "Sean2", "Howard3", "Dixie3", "Newman5", "Billy6"]
var sortedNameArray = nameArray.sort { $0.compare($1, options: .NumericSearch) == .OrderedAscending }
print(sortedNameArray) // gives the following:
Don't want this -> ["Billy6", "Bob8", "Cathy9", "Dan12", "Dave7", "Dixie3", "Henry10", "Howard3", "Ken1", "Newman5", "Pat11", "Sean2", "Steve12", "Susan10"]
Even though .NumericSearch was used the result is alphabetical.
I was able to get the desired result using a custom binary tree. Which gives the results:
Ken1 Sean2 Dixie3 Howard3 Newman5 Billy6 Dave7 Bob8 Cathy9 Henry10 Susan10 Pat11 Dan12 Steve12
But is there a simpler solution?
extension String {
var integerValue: Int? {
return Int(self)
}
}
func extractValueFromString(theString:String)->Int{
var catNumber: [Character] = []
//print("theString \(theString)")
for character in theString.characters{
var characterString = String(character)
if var value = characterString.integerValue { //if we don't check program crashes
//if numberSet.contains(Int(String(character))!) { //another way to check but redundant here
catNumber.append(character)
//print(catNumber)
// }
}
}
let numberString = String(catNumber)
return Int(numberString)!
}
class Node{
//nodes now only arrange strings
var data = ""
var value = Int()
var left:Node?;
var right:Node?;
deinit {
//print("deleting \(data)")
// print("node deleted")
}
init(data:String){
self.data = data;
//print(data)
}
}
class binaryTreeSort{
var root:Node?
init(){
}
deinit {
//print("tree deleted")
}
func getRoot()->Node{
return root!
}
func insertNewValue(data:String){
let newNode = Node(data:data)
var node:Node? = root
if (node == nil){
root = newNode
}
while (node != nil) {
let currentValue = node?.data
if currentValue == ""{
node?.data = data
return
}
if currentValue == data {
//we don't want duplicates.
return
}
if extractValueFromString(currentValue!) < extractValueFromString(data) {
if (node!.right != nil) {
node = node!.right
//print("Going Right at data \(node!.data)")
}else{
node!.right = newNode
//print("Going New Right at data \(node!.data)")
return
}
}else if extractValueFromString(currentValue!) == extractValueFromString(data){
if currentValue < data {
if (node!.right != nil) {
node = node!.right
//print("Going Right at data \(node!.data)")
}else{
node!.right = newNode
//print("Going New Right at data \(node!.data)")
return
}
}else{
if (node!.left != nil) {
//print("Going Left at data \(node!.data)")
node = node!.left
}else{
node!.left = newNode
//print("Going New Left at data \(node!.data)")
return
}
}
}
else{
if (node!.left != nil) {
//print("Going Left at data \(node!.data)")
node = node!.left
}else{
node!.left = newNode
//print("Going New Left at data \(node!.data)")
return
}
}
}
}
func inorderPrint(baseNode:Node){
if(baseNode.left != nil)
{
inorderPrint(baseNode.left!);
//print(" \(baseNode.data)")
}
print("\(baseNode.data)")
if(baseNode.right != nil)
{
inorderPrint(baseNode.right!)
//print(" \(baseNode.data)")
}
}
func reverseOrderPrint(baseNode:Node){
if(baseNode.right != nil)
{
reverseOrderPrint(baseNode.right!)
//print(" \(baseNode.data)")
}
print("\(baseNode.data)")
if(baseNode.left != nil)
{
reverseOrderPrint(baseNode.left!);
//print(" \(baseNode.data)")
}
}
}
var myBinaryTreeSort:binaryTreeSort? = binaryTreeSort()
for item in nameArray{
//print(item)
myBinaryTreeSort!.insertNewValue(item)
}
myBinaryTreeSort!.inorderPrint(myBinaryTreeSort!.getRoot())
print("---------------")
myBinaryTreeSort!.reverseOrderPrint(myBinaryTreeSort!.getRoot())
myBinaryTreeSort = nil //delete the tree
Use map to split the names into parts, sort to sort by number and name, and then map to restore the original:
func splitName(name:String) -> (String, Int) {
if let range = name.rangeOfCharacterFromSet(NSCharacterSet.decimalDigitCharacterSet()) {
return (name[name.startIndex..<range.startIndex], Int(name[range.startIndex..<name.endIndex])!)
} else {
return (name, 0)
}
}
print(nameArray.map(splitName).sort({ lhs, rhs in
if lhs.1 < rhs.1 {
return true
} else if lhs.1 > rhs.1 {
return false
} else {
return lhs.0 < rhs.0
}
}).map({ "\($0.0)\($0.1)" }))
Some other ways it could be done would be to maintain element 0 of the tuple as the full name (with numbers) and then the final map just becomes map({ $0.0 }) Depending on sizes, this may be more optimal than splitting the name each time it's compared.
If you have an array, you can sort with a custom closure.
For example:
nameArray.sort({extractValueFromString($0) < extractValueFromString($1)})
Will get you close. You just need to check if they are equal and return $0 < $1 instead.
Here's how I solved this, doing something similar to what #Lou-Franco alluded to:
func endInteger(word: String) -> Int {
if let range = word.rangeOfCharacterFromSet(NSCharacterSet.decimalDigitCharacterSet()){
let numberSubstring = word.substringFromIndex(range.startIndex)
return Int(numberSubstring) ?? 0
}
return 0
}
let sortedArray = yourArray.sort{endInteger($1) > endInteger($0)}
Say I have a function
func function1() -> (result:Bool, data:String){
return(false, "false")
}
and I want to use the return value of the Bool in an if let statement so,
if let value = function1 {
//code
} else {
//code
}
How would I get this to work? I can't seem to see it in the swift docs, as it just aludes to it being a returned tuple, which you can access with dot notation, but only if you set the return to be a tuple first - so for example this would work
var value = function1()
if value.result {
////code
} else {
//code
}
works, but I'd like to fit it all into the actual if else if possible. Any ideas?
I would suggest the use of a computed property.
func someFunc() {
if let value = computedProperty where value.result {
print(value.data)
} else {
// Do something else
}
}
var computedProperty: (result: Bool, data: String)? {
return (true, "FooBar")
}
or using a function
func someFunc() {
if let value = anotherFunc() where value.result {
print(value.data)
} else {
// Do something else
}
}
func anotherFunc() -> (result: Bool, data: String)? {
return (true, "FooBar")
}
Hope this helps :)
Swift 2.1 Properties
You could pattern match with a switch statement.
switch function1() {
case (true, let string):
print(string)
default:
// else stuff here
}
With this type on if you need optional return type from your function like this:
func function1() -> (result:Bool, data:String)? {
return(false, "false")
}
if let value = function1() {
//code
} else {
//code
}
My function is the one below
I'm trying to disable text when array reaches its limit.
I'm getting an error of array out of range. What type of condition can I use so that array is disabled when array1.count is equal to swipeCount.
This is my code:
let array1 = ["a","b","c","d"]
func getRandom1() {
for var i = 0; i < array1.count ; i++
{
array1.shuffle1()
}
}
func getText1() {
self.display.text = "\(array1[i++])"
swipeCount++
}
func getTextBack() {
self.display.text = "\(array1[i])"
}
func handleSwipes(sender:UISwipeGestureRecognizer) {
if (sender.direction == .Right)
{
if swipeCount != array1.count
{
getText1()
}
else
{
getTextBack()
}
}
}
func handleSwipes(sender:UISwipeGestureRecognizer) {
if (sender.direction == .Right) {
let aCount = array1.count - 1
if swipeCount < aCount
{
getText1()
}
else
{
getTextBack()
}
}
Change this line:
if swipeCount != array1.count
to
if swipeCount < array1.count - 1
I think you cannot use i in the getText1 and getTextBack. Instead of using i, you should use swipeCount like so:
func getText1() {
self.display.text = "\(array1[swipeCount++])"
}
func getTextBack() {
self.display.text = "\(array1[swipeCount])"
}