Category

Open Source

Fibonacci Sequence with Swift

The aim of this post is showing how to conform to Sequence protocol and create a custom sequence. To make it less boring, we can create a new type and call it FibonacciSequence which gets nth desired number then iterate over values.

The requirement to conform to Sequence protocol is fairly simple, you need to provide a makeIterator() method that returns an iterator. The code should look like this:

struct FibsSequence: Sequence {
    private var upTo: Int
    
    init(upTo: Int) {
        self.upTo = upTo
    }
    
    func makeIterator() -> FibsIterator {
        return FibsIterator(upTo: upTo)
    }
}

Our makeIterator() returns another custom type which conforms to IteratorProtocol. This type obliges the conformer to provide a method to supply the values of the sequence one at a time. The only method should be implemented is next() which advances to the next element and returns it, or returns nil if no next element exists:

struct FibsIterator: IteratorProtocol {
    private var state:(UInt, UInt) = (0, 1)
    private var upTo: Int
    private var counter = 0
    
    init(upTo: Int) {
        self.upTo = upTo
    }
    
    mutating func next() -> UInt? {
        guard upTo > counter else {return nil}
        guard upTo > 0 else {return nil}
        
        let upcomingNumber = state.0
        state = (state.1, state.0 + state.1)
        counter += 1
        
        return upcomingNumber
    }
}

In this type we have three private variables, which are clearly named, and a mutating function. Being mutating is not strictly necessary in general but since we want to keep track of iterator and update it, it is required here. The next() function returns optional UInt, optional because at some point we want to exit the function unless we deliberately want to make the code crash!

There are two exit points in the function, first one checks against nth element, if it is bigger than what user initially asked, it quits. The other one is to makes sure the nth element is a positive one.

Now we have a custom type which returns Fibonacci sequence up to a certain index in the series, indicated during initialization.

for (index, fib) in FibsSequence(upTo: 15).enumerated() {
    print("fib: \(fib), index: \(index + 1)")
}

****************************
fib: 0, index: 1
fib: 1, index: 2
fib: 1, index: 3
fib: 2, index: 4
fib: 3, index: 5
fib: 5, index: 6
fib: 8, index: 7
fib: 13, index: 8
fib: 21, index: 9
fib: 34, index: 10
fib: 55, index: 11
fib: 89, index: 12
fib: 144, index: 13
fib: 233, index: 14
fib: 377, index: 15

Whole code is available on github.

Update:

After posting this on reddit a user, Nobody_1707, suggested a shorter version of the code which is:

public struct FibSequence: Sequence, IteratorProtocol {
    private var state: (UInt, UInt) = (0, 1)
    public init() { }
    public mutating func next() -> UInt? {
        guard state.1 >= state.0 else { return nil }
        defer { state = (state.1, state.0 &+ state.1) }
        return state.0
    }
}

for (i, fib) in zip(0..., FibSequence().prefix(15)) {
    print("fib(\(i)) = \(fib)")
}

Swift: An app to search through movie titles using The Open Movie Database API

This demo app uses OMDb APIs to search in movie titles and show details of the selected movie. First of all, you need to get your API key, it’s free. All right, this is the plan:

  • Create a class to make network requests using URLSession
  • Test the class
  • Create the UI

NetworService will handle the network requests. This is a singleton class which uses the builtin URLSession with a handful of configuration options. An extension to this class contains the required methods to search. Since I have created this as a reusable utility class, there are some extra features which won’t be used in this tutorial, maybe you need them later.

class NetworkService {
    //MARK: - Internal structs
    private struct authParameters {
        struct Keys {
            static let accept = "Accept"
            static let apiKey = "apikey"
        }
        
        static let apiKey = "YOURKEY"
    }
    
    //An NSCach object to cache images, if necessary
    private let cache = NSCache()
    
    //Default session configuration
    private let urlSessionConfig = URLSessionConfiguration.default
    
    //Additional headers such as authentication token, go here
    private func configSession(){
        self.urlSessionConfig.httpAdditionalHeaders = [
            AnyHashable(authParameters.Keys.accept): MIMETypes.json.rawValue
        ]
    }
    
    private static var sharedInstance: NetworkService = {
        return NetworkService()
    }()
    
    //MARK: - Public APIs
    class func shared() -> NetworkService {
        sharedInstance.configSession()
        return sharedInstance
    }

    //MARK: - Private APIs
    private func createAuthParameters(with parameters:[String:String]) -> Data? {
        guard parameters.count > 0 else {return nil}
        return  parameters.map {"\($0.key)=\($0.value)"}.joined(separator: "&").data(using: .utf8)
    }
}

This is the skeleton of our class, shared() function returns a static instance of the class after running the internal configSession() function. authParameters structure is used to store keys and values for authentication, just to prevent writing a messy code. Now we can create an instance of the class using, let networkService = NetworkService.shared().

Now the we need another public method to make network requests:

    private func request(url:String,
                 cachePolicy: URLRequest.CachePolicy = .reloadRevalidatingCacheData,
                 httpMethod: RequestType,
                 headers:[String:String]?,
                 body: [String:String]?,
                 parameters: [URLQueryItem]?,
                 useSharedSession: Bool = false,
                 handler: @escaping (Data?, URLResponse?, Int?, Error?) -> Void){
        
        if var urlComponent = URLComponents(string: url) {
            urlComponent.queryItems = parameters
            var session = URLSession(configuration: urlSessionConfig)
            if useSharedSession {
                session = URLSession.shared
            }
            
            if let _url = urlComponent.url {
                
                var request = URLRequest(url: _url)
                request.cachePolicy = cachePolicy
                request.allHTTPHeaderFields = headers
                
                if let _body = body {
                    request.httpBody = createAuthParameters(with: _body)
                }
                request.httpMethod = httpMethod.rawValue
                
                session.dataTask(with: request) { (data, response, error) in
                    let httpResponsStatusCode = (response as? HTTPURLResponse)?.statusCode
                    handler(data, response, httpResponsStatusCode, error)
                    }.resume()
            }else{
                handler(nil, nil, nil, Failure.invalidURL)
            }
        }else{
            handler(nil, nil, nil, Failure.invalidURL)
        }
    }

This method has the basic functionality of a request session, getting parameters and headers and returning response, data, status code and an error object if exists, asynchronously. For the httpPart, you can replace createAuthParameters(:_) with an array of URLQueryItems.

For errors, I’m using a enumerator which is inherited from Error protocol, here is it:

import Foundation
public enum Failure:Error {
    case invalidURL
    case invalidSearchParameters
    case invalidResults(String)
    case invalidStatusCode(Int?)
}

extension Failure: LocalizedError {
    public var errorDescription: String? {
        switch self {
        case .invalidURL:
            return NSLocalizedString("The requested URL is invalid.", comment: "")
        case .invalidSearchParameters:
            return NSLocalizedString("The URL parameters is invalid.", comment: "")
        case .invalidResults(let message):
            return NSLocalizedString(message, comment: "")
        case .invalidStatusCode(let message):
            return NSLocalizedString("Invalid HTTP status code:\(message ?? -1)", comment: "")
        }
    }
}

OMDb works with simple queries, it doesn’t have many end points! but to keep everything structured, let’s create another enumerator:

import Foundation

enum EndPoints {
    case Search
}

extension EndPoints {
    var path:String {
        let baseURL = "http://www.omdbapi.com"
        
        struct Section {
            static let search = "/?"
        }
        
        switch(self) {
        case .Search:
            return "\(baseURL)\(Section.search)"

        }
        
    }
    
}

The next step is testing:

import XCTest
@testable import OpenMovie

class OpenMovieTests: XCTestCase {
    
    private let networkService = NetworkService.shared()
    
    func testSearch() {
        let promise = expectation(description: "Search for batman movies")
        networkService.search(for: "batman", page: 1) { (searchObject, error) in
            XCTAssertNil(error)
            XCTAssertTrue(searchObject?.response == "True")
            promise.fulfill()
        }
        waitForExpectations(timeout: 2, handler: nil)
    }
    
    func testSearchByIMDBID() {
        let promise = expectation(description: "Search for Batman: Dark Night Returns")
        networkService.getMovie(with: "tt2313197") { (movieObject, error) in
            XCTAssertNil(error)
            XCTAssertTrue(movieObject?.imdbID == "tt2313197")
            promise.fulfill()
        }
     
        waitForExpectations(timeout: 2, handler: nil)
    }
    
}

And the results:

Now the last step, isn’t it better you look at it yourself? It’s TL;DR sort of thing, download it from github and give it a try.

This is how it looks:

iOS: Show activity indicator in UISearchBar

Updated for iOS 13 and Swift 5

When you are using a search controller, most probably it’s a network call. Isn’t it nice to show a tiny loading animation instead of the magnifier icon on the left side of the search while your app is still waiting for data from the internet? Indeed.

To do this, you can use either use private API, which is frowned upon, or use UITextField‘s built-in methodsetImage(), I go for the latter one.

First of all, let’s see how it looks:

To show the loading animation we have to:

  1. find the search bar within the search controller
  2. remove the magnifier icon
  3. add an UIActivityIndicatorView to the search bar’s leftView

And to hide it it’s almost the same steps but in reverse.

To make the code reusable, let’s do it as an extension:

import Foundation
import UIKit

extension UISearchBar {
  //
}

First of all, we need to find the text field which is basically a UITextField within UISearchBar subviews. this computed property does the job for us and returns an option UItextField:

private var textField: UITextField? {
    let subViews = self.subviews.flatMap { $0.subviews }
    if #available(iOS 13, *) {
        if let _subViews = subViews.last?.subviews {
            return (_subViews.filter { $0 is UITextField }).first as? UITextField
        }else{
            return nil
        }
        
    } else {
        return (subViews.filter { $0 is UITextField }).first as? UITextField
    }
    
}

The next step is finding the current magnifier image, it’s similar to the previous step:

    private var searchIcon: UIImage? {
        let subViews = subviews.flatMap { $0.subviews }
        return  ((subViews.filter { $0 is UIImageView }).first as? UIImageView)?.image
    }

Let’s get hold of our loading animation, which is an UIActivityIndicatorView:

   private var activityIndicator: UIActivityIndicatorView? {
        return textField?.leftView?.subviews.compactMap{ $0 as? UIActivityIndicatorView }.first
    }

Now let’s add a public variable to show/hide the loading animation:

    var isLoading: Bool {
        get {
            return activityIndicator != nil
        } set {
            let _searchIcon = searchIcon
            if newValue {
                if activityIndicator == nil {
                    let _activityIndicator = UIActivityIndicatorView(style: .gray)
                    _activityIndicator.startAnimating()
                    _activityIndicator.backgroundColor = UIColor.clear
                    let clearImage = UIImage().imageWithPixelSize(size: CGSize.init(width: 14, height: 14)) ?? UIImage()
                    self.setImage(clearImage, for: .search, state: .normal)
                    textField?.leftViewMode = .always
                    textField?.leftView?.addSubview(_activityIndicator)
                    let leftViewSize = CGSize.init(width: 14.0, height: 14.0)
                    _activityIndicator.center = CGPoint(x: leftViewSize.width/2, y: leftViewSize.height/2)
                }
            } else {
                self.setImage(_searchIcon, for: .search, state: .normal)
                activityIndicator?.removeFromSuperview()
            }
        }
    }

This piece of code looks for an existing activity indicator and if there is none, first creates one, cleans the default image for seach mode, the magnifier image, sets a new transparent image with 14 point in 14 point in size then adds the recently created UIActivityIndicatorView to left search bar’s leftView; And to hide it, smiple removes the  UIActivityIndicatorView and set back the image for search mode.

To use it simple set searchController.searchBar.isLoading = true  but be careful if you are using it inside another block, call it withing the main queue.

You can download the whole code from github. The UIImage extension, imageWithPixelSize(), is also available on github

بوت استرپ

بوت استرپ احتمالا پرطرفدار ترین فریم ورک طراحی سمت کاربره (front-end). دوتا از توسعه دهنده‌های توییتر این پروژه رو شروع کردن و سال ۲۰۱۱ متن بازش کردن.

Read More