Search This Blog

Showing posts with label Swift. Show all posts
Showing posts with label Swift. Show all posts

Sunday, July 1, 2018

Generating random and using It in Swift

Default Methods

For generating random numbers in swift, the following functions are used:
  • arc4random() returns a random integer number in the range of 0 to 4 294 967 295 (it is UInt32.max)
  • drand48() returns a random float point number in the range of 0.0 to 1.0
  • arc4random_uniform(N) returns a random integer number in the range of 0 to N - 1

Let's see examples of how this functions work:
arc4random() // 107985684

drand48() // 0.3964647737602753

arc4random_uniform(10) // 4
But what if we need to implement more advanced requirements for generating random numbers. Let's explore possible situations where we generating random numbers and using it.

Generate Random Integer with Upper Bound

For generating random integer in range between 0 and upper bound we use function

rc4random_uniform(N)

This function will return random integer number in the range of 0 to N - 1. So, for example, if N = 42 then maximum number that can be generate will be 41 and minimum will be 0.
rc4random_uniform(42)
This function takes one parameter of type UInt32 and returns value of type UInt32
func arc4random_uniform(_ __upper_bound: UInt32) -> UInt32
So, if you work with values of type Int you have to convert values to type UInt32 when parameter passed to function and convert back to Int when getting result.
let number: Int
number = Int(arc4random_uniform(42))

let inputNumer: Int = 100
let outputNumber: Int
outputNumber = Int(arc4random_uniform(UInt32(inputNumer)))
It is not very handy always do this conversion when generating random numbers. Let's make a function for this:
func random(n: Int) -> Int {
    return Int(arc4random_uniform(UInt32(n)))
}

let n: Int
n = random(n: 99)
This function takes one parameter of type Int and return result in range between 0 and N-1 of type Int.

To make it more useful we can create extension for type Int and generating of random integers will be availbale everywhere in code. We also can check for sign before passed parameter because UInt32 type is unsigned type and in case of negative value passed we need to get absolute value from input passed parameter. This method is static and will be available for whole Int type, there is no need to create instance of Int type.
extension Int {
    
    static func random(_ n: Int) -> Int {
        if n < 0 {
            return -Int(arc4random_uniform(UInt32(abs(n))))
        } else if n > 0 {
            return Int(arc4random_uniform(UInt32(n)))
        } else {
            return 0
        }
    }
}

Int.random(-100) // return in range between -99 and 0
But we can create also an instance method in extension. In this case instance of Int type will be used as upper bound for random number generation function.
extension Int {
   
    func random() -> Int {
        if n < 0 {
            return -Int(arc4random_uniform(UInt32(abs(self))))
        } else if n > 0 {
            return Int(arc4random_uniform(UInt32(self)))
        } else {
            return 0
        }
    }
}

65.random()

Tuesday, February 6, 2018

iOS Swift. All ways to unwrap optional

There I want to enumerate all possible ways to unwrap optional variables that possible in Swift.

  1. Force Unwrapping
  2. Check for nil (with !=) and Force Unwrapping
  3. Ternary Operator
  4. if let Construction or Single Optional Binding
  5. Multiple Optional Binding
  6. Guard Operator
  7. Nil Coalescing Operator
  8. Switch Operator
  9. Optional Chaining

Let's review each of them.


Force Unwrapping


When you have optional and you are sure that this optional has a value you always can just use force unwrapping with ! (exclamation point operator). But it always for your responsibility use this because there can be situations when optional will have no value and force unwrapping will produce error and app crashing.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
let name1 = dict[1]
let name4 = dict[4]
print(name1!) // "Jack"
print(name4!) // Fatal error: Unexpectedly found nil while unwrapping an Optional value

Check for nil


But there is solution for ensure from errors when using force unwrapping - just check optional for nil before that.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]

print(dict[1]) // Optional("Jack")
print(dict[4]) // nil

func getName(for index: Int) -> String {
    if dict[index] != nil {
        return dict[index]!
    }
    return "No name for such index"
}

print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index

Ternary Operator


The same check for nil can be done with ternary operator.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]

print(dict[1]) // Optional("Jack")
print(dict[4]) // nil

func getName(for index: Int) -> String {
    return dict[index] != nil ? dict[index]! : "No name for such index"
}

print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index

if let Construction or Optional Binding


This construction if let allows you safety unwrap optional value. If optional has value then this value will be unwrapped to constant/variable. Unwrapped variable/constant will be available only in scope inside this if let statement.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]

print(dict[1]) // Optional("Jack")
print(dict[4]) // nil

func getName(for index: Int) -> String {
    if let name = dict[index] {
        return name
    }
    return dict[index] ?? "No name for such index"
}

print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index

Multiple Optional binding


There can be optional property of optional object, so you can do some chaining of if let statements. All this if let statements can be written in on line with one if and with commas. Every next statement will be executed only if previous value is unwrapped.
struct A {
    let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
    func getName(for index: Int) -> String? {
        return dict[index]
    }
}

struct B {
    var names: A?
    
    init() {
        self.names = A()
    }
}

let b: B? = B()

if let b = b {
    if let names = b.names {
        if let name = names.getName(for: 1) {
            print(name) // Jsck
        }
    }
}

if let b = b, let names = b.names, let name = names.getName(for: 1) {
    print(name) // Jack
}

Guard Operator


Guard operator or "Early Exit" check that optional has value and can be unwrapped. If optional has no value then else statement will be executed. Unwrapped value will be available in scope next to guard, opposite to if let statement.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]

print(dict[1]) // Optional("Jack")
print(dict[4]) // nil

func getName(for index: Int) -> String {
    guard let name = dict[index] else {
        return "No name for such index"
    }
    return name
}

print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index

Nil Coalescing Operator or ??


Special operator ?? works following way: if there is a value in optional then this value will be return else default value will be return.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]

print(dict[1]) // Optional("Jack")
print(dict[4]) // nil

func getName(for index: Int) -> String {
    return dict[index] ?? "No name for such index"
}

print(getName(for: 1)) // "Jack"
print(getName(for: 4)) // No name for such index

Switch Operator


We can use switch operator for unwrapping optionals because if we open source code of Optional we can see that Optional is an enum with 2 cases: none and some.
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
}

In case of .none - there is no value (it means nil). In case of .some - there is a value that we can use and this value will be unwrapped.
let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]

func getName(for index: Int) -> String {
    let name =  dict[index]
    switch name {
    case .some(let value):
        return value
    case .none:
        return "No name for such index"
    }
}

print(getName(for: 1)) // Jack
print(getName(for: 4)) // No name for such index

Optional Chaining


In some cases optional instance of struct or class can have optional properties. So, when we need to get optional property of optional object it is a chain of optionals. Every next optional value will be unwrap only if previous is can be unwrapped. For, example
b?.names?.getName(for: 1)?.count
Property count will be getting only if b can be unwrapped, names can be unwrapped and value which will be returned by getNames() can be unwrapped too. If any of values in chain cannot be unwrapped then property count will be nil.
struct A {
    let dict = [1: "Jack", 2: "John", 9: "Kate", 77: "Charlie"]
    func getName(for index: Int) -> String? {
        return dict[index]
    }
}

struct B {
    var names: A?
    
    init() {
        self.names = A()
    }
}

let b: B? = B()
b?.names?.getName(for: 4)?.count

print(b?.names?.getName(for: 1)?.count) // Optional(4) because "Jack" has 4 letters
print(b?.names?.getName(for: 4)?.count) // nil because no name for such index

Monday, October 30, 2017

iOS Swift. Swipe gesture recogniser in multiple directions

For swiping in each direction we need to create Swipe gesture recognizer and specify direction that we need for this recognizer. For each direction we need create separate recognizer.
override func viewDidLoad() {
    super.viewDidLoad()
        
    // Swipe Left
    let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(swipingLeft))
    swipeLeft.direction = .left
    self.view.addGestureRecognizer(swipeLeft)
        
    // Swipe Right
    let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(swipingRight))
    swipeRight.direction = .right
    self.view.addGestureRecognizer(swipeRight)
}
    
@objc func swipingLeft(sender: UISwipeGestureRecognizer) {
    print("Swipe Left")
}
    
@objc func swipingRight(sender: UISwipeGestureRecognizer) {
    print("Swipe Right")
}

We can make code more universal. When we swiping in different directions we can handle all this swipes in one method and switch between directions.
override func viewDidLoad() {
    super.viewDidLoad()
        
    let swipeRight = UISwipeGestureRecognizer(target: self,
                                              action: #selector(self.handleSwipe))
    swipeRight.direction = UISwipeGestureRecognizerDirection.right
    self.view.addGestureRecognizer(swipeRight)
        
    let swipeDown = UISwipeGestureRecognizer(target: self,
                                             action: #selector(self.handleSwipe))
    swipeDown.direction = UISwipeGestureRecognizerDirection.down
    self.view.addGestureRecognizer(swipeDown)
}
    
@objc func handleSwipe(gesture: UIGestureRecognizer) {
    if let swipeGesture = gesture as? UISwipeGestureRecognizer {
        switch swipeGesture.direction {
        case UISwipeGestureRecognizerDirection.right:
            print("Swiped right")
        case UISwipeGestureRecognizerDirection.down:
            print("Swiped down")
        case UISwipeGestureRecognizerDirection.left:
            print("Swiped left")
        case UISwipeGestureRecognizerDirection.up:
            print("Swiped up")
         default:
            break
        }
    }
}

Monday, October 23, 2017

iOS Swift. How to Animate a Bar Button Item

Today I will show you how to make animated bar buttons and make it with coding. First of all we need icons for our navigation bar. Let's get them from google material design icons.

Google Material Design Icons

Adding icons to project


The only thing that we make with Storyboard is embed our view controller in navigation controller. We need it for navigation bar as container for bar buttons.


Declare two bar button items
var settingsBarButton: UIBarButtonItem?
    
var favoriteBarButton: UIBarButtonItem?

And Bool variable for switch icon of favourite button.
var favorite: Bool = false

Creating images from Assets. One image for settings icon and two images for favourite icon (favourite and unfavourite).
let settingsImage = UIImage(named: "ic_settings_48pt")?.withRenderingMode(.alwaysTemplate)
let favoriteBorderImage = UIImage(named: "ic_favorite_border_48pt")?.withRenderingMode(.alwaysTemplate)
let favoriteFullImage = UIImage(named: "ic_favorite_48pt")?.withRenderingMode(.alwaysTemplate)

Inside viewDidLoad method let's create UIButton, configure it and initialize settings bar button item with this UIButton. We put it to the left part of navigation item of view controller.
let settingsButton = UIButton(type: .system)
settingsButton.tintColor = .black
settingsButton.setImage(self.settingsImage, for: .normal)
settingsButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
settingsButton.addTarget(self, action: #selector(settingsButtonTapped), for: .touchUpInside)
self.settingsBarButton = UIBarButtonItem(customView: settingsButton)
self.navigationItem.setLeftBarButton(settingsBarButton, animated: false)

It is touch handler for button. Here we make animation. There are two steps:

  1. Rotate view to some angle
  2. With animation we restore initial view rotation as it was before first rotation

@objc func settingsButtonTapped(_ sender: UIButton) {
    self.settingsBarButton?.customView?.transform =
        CGAffineTransform(rotationAngle: CGFloat(CGFloat.pi * -3/4))
    UIView.animate(withDuration: 0.8) {
        self.settingsBarButton?.customView?.transform = .identity
    }
}

The same for favourite bar button. Initially we create it with favourite empty icon (unfavourite). We put it to the right part of navigation item of view controller.
let favoriteButton = UIButton(type: .system)
favoriteButton.tintColor = .black
favoriteButton.setImage(self.favoriteBorderImage, for: .normal)
favoriteButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
favoriteButton.addTarget(self, action: #selector(favoriteButtonTapped), for: .touchUpInside)
self.favoriteBarButton = UIBarButtonItem(customView: favoriteButton)
self.navigationItem.setRightBarButton(favoriteBarButton, animated: false)

Touch handler for favourite button. The same logic as before, but here we use other type of transformation(previously we use rotation), now we use scaling transformation:
  1. Change button scale.
  2. Restore button scale with animation. Also inside animation we change icon of button. 
Here we use spring animation. It means that here will be appear special effects during animations. Such as velocity and damping changing and etc.
@objc func favoriteButtonTapped(_ sender: UIButton) {
        self.favoriteBarButton?.customView?.transform = CGAffineTransform(scaleX: 0, y: 0)
        UIView.animate(withDuration: 0.5,
                       delay: 0.0,
                       usingSpringWithDamping: 0.6,
                       initialSpringVelocity: 10,
                       options: .curveEaseInOut,
                       animations: {
                        self.favorite = !self.favorite
                        let image = self.favorite ? self.favoriteFullImage : self.favoriteBorderImage
                        if let button = self.favoriteBarButton?.customView as? UIButton {
                            button.setImage(image, for: .normal)
                        }
                        self.favoriteBarButton?.customView?.transform = .identity
        }, completion: nil)
}

How it all work





GitHub Link

Sunday, October 22, 2017

How to remove all subviews of a view in Swift?

Following task - remove all subViews of current view. Let's see how we can implement this.

For example we create UIView and add UILabel to it.
var containerView = UIView()
containerView.addSubview(UILabel(frame: CGRect.zero))

1. Simple For Loop
for subView in containerView.subviews {
    subView.removeFromSuperview()
}

2. ForEach Loop
containerView.subviews.forEach {
    $0.removeFromSuperview()
}

3. Using Map Function
containerView.subviews.map { $0.removeFromSuperview() }

4. Making Extension

If we want to make removing enable for all views in project then let's create Extension.
extension UIView {   
    func removeAllSubView() {
        self.subviews.forEach { $0.removeFromSuperview() }
    }
}

containerView.removeAllSubView()

5. Going Universal with Generics

We can go further and make our Extension universal with Generics. Now You can specify which type of subViews You want to remove.
extension UIView {
    
    func removeAllSubViewOfType<T: UIView>(type: T.Type) {
        self.subviews.forEach {
            if ($0 is T) {
                $0.removeFromSuperview()
            }
        }
    }
}

containerView.removeAllSubViewOfType(type: UILabel.self)

6. The Same but Using Filter and Map Functions

The same as above. But here we use chain of High-order functions.
extension UIView {
    
    func removeAllSubViewOfTypeUsingHOF<T: UIView>(type: T.Type) {
        self.subviews.filter({ $0 is T }).map({ $0.removeFromSuperview() })
    }
}

containerView.removeAllSubViewOfTypeUsingHOF(type: UILabel.self)

Thursday, October 19, 2017

Flat nested array recursively in Swift

Following task - make flat array from nested array. Because there are could be any levels of nested array we use recursive approach.

For example - given array [1, [2, [3, 4, 5]]]. We need to get this [1, 2, 3, 4, 5]

Standard Approach

Algorithm
  1. In for loop go through the array
  2. If element of array is int then add it to new array
  3. If element of array is array too then make recursive invoke of function and send this array
We use Any type as type of input array because input array can hold as integers as array. They both are value types, so Any is suitable for us in this function.

Let's get coding.
import UIKit

let array: [Any] = [1, 2, [3]]

func makeFlatArray(_ array: [Any]) -> [Int] {
    var flatArray = [Int]()
    for item in array {
        if let item = item as? Int {
            flatArray.append(item)
        } else if let item = item as? [Any] {
            let result = makeFlatArray(item)
            flatArray += result
        }
    }
    return flatArray
}

print(makeFlatArray([1, 2, 3]))
// [1, 2, 3]
print(makeFlatArray([1, [2, 3, 4]]))
// [1, 2, 3, 4]
print(makeFlatArray([1, [2, [3, 4]]]))
// [1, 2, 3, 4]
print(makeFlatArray([[1], [2, [3, 4, [5]]]]))
// [1, 2, 3, 4, 5]

Using Generics

What if we want to use this function with array of any type. We should use Generics:
func makeFlatArrayGeneric<T>(_ array: [Any]) -> [T] {
    var flatArray = [T]()
    for item in array {
        if let item = item as? T {
            flatArray.append(item)
        } else if let item = item as? [Any] {
            let result: [T] = makeFlatArrayGeneric(item)
            flatArray += result
        }
    }
    return flatArray
}

Now we can use this function with any kind of array. With integer array as previous
let array: [Any] = [1, 2, [3], [4, [5]]]
let items: [Int] = makeFlatArrayGeneric(array)
// [1, 2, 3, 4, 5]

And with array of strings
let array: [Any] = ["A", "BB", ["CCC"], ["DD", ["EE"]]]
let items: [String] = makeFlatArrayGeneric(array)
// ["A", "BB", "CCC", "DD", "EE"]

Make Extension for Array 

What if we want this feature for any array as built in method. Let's create extension for Array Type. Here we use Swift method flatMap.
extension Array {
    
    func makeFlat() -> [Element] {
        let flatArray = self.flatMap { (element) -> [Element] in
            if let array = element as? Array {
                return array.makeFlat()
            }
            return [element]
        }
        return flatArray
    }
}

Result of using array extension.
let array: [Any] = ["A", "BB", ["CCC"], ["DD", ["EE"]]]
print(array.makeFlat())
// ["A", "BB", "CCC", "DD", "EE"]