Swift!

JWT для Vapor

Адаптировал библиотеку для работы с токенами по стандарту JWT под фреймворк Vapor на Mac os и Ubuntu. Теперь можно делать крутую stateless аутентификацию в своем REST web сервисе.
https://github.com/Landan/JSONWebToken.swift

2016   json   Swift   token

Линзы или геттеры и сеттеры в функциональном свифте

В идеальном функциональном мире состояние объекта не может изменяться после создания, а каждая функция работает только с теми данными, которые она получает, не имея никакой информации о существовании всего, что есть в програме, кроме других функций. И поскольку одна из самых мощных функций Свифта является возможность писать код в функциональном стиле, то почему бы не разобраться ради интереса как можно работать с не изменяемыми обьектами? Главная идея в том, что поскольку данные не изменяемые, то надо создавать копию этих данных с нужными нам изменениями при помощи пары геттеров и сеттеров, которые называются линзами.

Для начала напишем две структуры:

struct User {
    let name: String
    let age: Int
}
struct Corp{
    let name: String
    let user: User
}

let john = User(name: "John", age: 18)
let corp = Corp(name: "Google", user: john)

Эти структуры полностью immutable, то есть их состояние после создания никак изменить нельзя. Теперь напишем сеттер и геттер для поля name в структуре User, геттер просто возвращает имя, а сеттер создает нового юзера с новым именем:

extension User{
    func getName() -> String {
        return self.name
    }
    func setName(name: String) -> User {
        return User(name: name, age: age)
    }
}

То же самое сделаем для структуры Corp, проверяем:

john.getName() // => "John"
john.setName("John M").getName() // => "John M"
john.getName() // => "John"
corp.setUser(corp.getUser().setName("John M")).getUser().getName() // => "John M"

Теперь введем новую абстракцию — линзу, которая позволит вынести повторяемый код сеттеров и геттеров:

struct Lens <A, B> {
    let get: A -> B
    let set: (B, A) -> A
}

Создадим линзу для имени юзера:

let userNameLens = Lens<User, String>(
    get: { user in user.name },
    set: { (name, user) in User(name:name, age: user.age) }
)
userNameLens(john) // => "John"
userNameLens("Johg 2", corp.getUser()).name // => "John 2"

Линза для структуры Corp будет возвращать пользователя и создавать новую структуру с заданым пользователем.

let corpUserLens = Lens<Corp, User>(
    get: { corp in corp.user },
    set: { user, corp in Corp(name: corp.name, user: user) }
)

corpUserLens.set(
    userNameLens.set(
        "Test User",
        corpUserLens.get(corp)
    ),
    corp
)

Теперь напишем функцию для композиции линз, что-бы можно было легко получить доступ напрямую к имени пользователя структуры Corp. Функция get — это результат get'ов двух линз, set чуть сложнее, на примере композицию двух линз corpUserLens и userNameLens происходит следующее: сначала corpUserLens получит пользователя из Corp, затем userNameLens создаст нового пользователя с новым именем и еще раз линза corpUserLens создаст новую корпорацию с новым пользователем.

func compose<A, B, C>(lhs: Lens<A, B>, _ rhs: Lens<B, C>) -> Lens<A, C> {
    return Lens<A, C> (
        get: { a in rhs.get(lhs.get(a)) },
        set: { (c, a) in lhs.set(rhs.set(c, lhs.get(a)), a) }
    )
}
let composedLens = compose(corpUserLens, userNameLens)

composedLens.get(corp)
composedLens.set("test", corp).getUser().getName()

Для удобства и большей наглядности напишем функцию-оператор для композиции линз, так как из выражения composedLens.set(«test», corp) не понятно, что в конечном счете будет использована линза изменения имени у пользователя.

func * <A, B, C> (lhs: Lens<A, B>, rhs: Lens<B, C>) -> Lens<A, C> {
    return compose(lhs, rhs)
}

Теперь все намного лучше:

(corpUserLens * userNameLens).get(corp)
(corpUserLens * userNameLens).set("User", corp)

Добавим еще немного синтаксического сахара, заменим set() на оператор:

infix operator ~> { associativity left precedence 100 }
func ~> <A, B> (lhs: Lens<A, B>, rhs: B) -> A -> A {
    return { a in lhs.set(rhs, a) }
}

(userNameLens ~> "Test name")(john)

Для комбинирования линз добавим еще один оператор:

infix operator |> { associativity left precedence 80 }
func |> <A, B> (x: A, f: A -> B) -> B {
    return f(x)
}
func |> <A, B, C> (f: A -> B, g: B -> C) -> A -> C {
    return { g(f($0)) }
}

john |> userNameLens ~> "New Name"

В итоге получится удобная конструкция:

corpUserLens * userNameLens ~> "test name"
|> corpUserLens * ageUserLens ~> 26
|> someOtherLens * someOtherLens2 ~> String()
2016   Lens   Swift

Перечисления

В свифте enum'ы почти как класы, они могут иметь свои методы и переменные. Enum очень удобно использовать для построения Table View в случае когда все ячейки известны зарание и меняться будут редко, например это может быть список настроек.

Допустим у нас в настройках будет три пункта:

enum Settings: Int {
    case FirstName
    case LastName
    case Age
}

Теперь для метода делегата numberOfRowsInSection необходимо указать количество ячеек и поскольку стандартным способом узнать количество кейсов в перечислении не получиться, а константы — лишняя зависимость, то прийдется сделать небольшой хак, добавим метод в enum:

static let count: Int = {
        var max: Int = 0
        while let _ = Settings(rawValue: max) { max += 1 }
        return max
    }()

Дальше добавим отображаемое имя в ячейке для каждого кейса:

var name: String {
        switch self {
        case .FirstName:
            return "First Name"
        case .Last Name:
            return  "Last Name"
        case .Age:
            return "Age"
        }
    }

И последнее, добавим метод для создания ячейки:

func cellForRowAtIndexPath(indexPath: NSIndexPath, tableView: UITableView) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(name, forIndexPath: indexPath)
        cell.titleLabel.text = name
        return cell
    }

Теперь надо вызвать метод перечисления cellForRowAtIndexPath в методе делегата cellForRowAtIndexPath, для каждого кейса будет создана своя уникальная ячейка.
Что-бы добавить новую ячейку в таблицу нужно просто добавить новый кейс в перечисление и все. Еще этот способ удобен тем, что не нужно хардкодить константы индексов для конкретных ячеек, так как можно сделать Settings(rawValue: indexPath.row) и получить нужные значения.

2016   enum   Swift

Сабскрипт

Сабскрипт — это сокращенный способ получить доступ к елементам коллекции, например array[0], где [0] — это сабскрипт. В свифте есть возможность писать свои сабскрипты и это круто, если бы мы писали гру шахматы, то было бы удобно оперировать доской как двумерной матрицей и задавать ходы в таком виде:

board[move: (4,5)] = (2,3)

Еще удобно, в случае когда индекс выходит за пределы массива, выводить ошибку, для этого можно сделать простенький сабскрипт:

struct Board {
    var positions = [[Position]]()
    
    subscript(x row: Int, y column: Int) -> Position? {
        get {
            if row < positions.count {
                if column < positions[row].count {
                    return positions[row][column]
                }
            }
            print("Range error")
            return nil
        }
    }
}

И обращаться к нему вот так:

board[x: 0, y:3]

Вместо:

board.positions[0][0]

Также сабскрипты можно использовать в расширениях, этот пример добавляет сабскрипт целого числа во встроеный тип String:

extension String{
    subscript(index: Int) -> Character {
        let _index = startIndex.advancedBy(index)
        return characters[_index]
    }
}

Теперь можно получить символ из строки указав индекс:

"436575"[1]
=> 3
2016   subscript   Swift

Апликативные функторы

В отличие от обычных функторов, у апликативных функторов функция так же упакована в контекст как и значения к которым она будет применяться. По дефолту в свифте нет апликативных функторов, но можно легко добавить самому, например для типа Optional добавим функцию apply, которая знает как применить функцию, обернутую в контекст к значениям, которые обернуты в тот же контекст.

extension Optional {
  func apply<U>(f: (Wrapped -> U)?) -> U? {
    switch f {
      case .Some(let someF): return self.map(someF)
      case .None: return .None
    }
  }
}

Теперь можно взять функцию сложения plusTwoNumbers из предыдущего поста об функторах и что-бы применить ее к значению, нужно обернуть в тот же контекст Optional и передать функции apply:

let plus = plusTwoNumbers(5)
Optional.Some(10).apply(Optional.Some(plus))
// => .Some(15)

Или для удобства написать свой оператор:

infix operator <*> { associativity left }

func <*><T, U>(f: (T -> U)?, a: T?) -> U? {
  return a.apply(f)
}

Optional.Some(plus) <*> Optional.Some(10)
// => .Some(15)
2016   applicatives   functor   Swift

Предикат

Предикат — это логический оператор, который сравнивает два выражения с помощью специального синтаксического анализатора (Тут можно детальнее почитать о синтаксе предиката). Когда приходится работать со строками, сложной выборкой и регулярными выражениями, то предикат — незаменимая штука. Для себя я написал два оператора, которые представляют простенький шаблон для использования предиката, вот как он выглядит:

Выражение предиката |*| Шаблон <=> Проверяемое значение

Первый оператор — это каррированая функция, которая собственно и создает предикат

infix operator |*| { associativity left}
func |*| <T: AnyObject> (pattern: String, b: T) -> (T) -> Bool {
    return { (a: T) -> (Bool) in
        return NSPredicate(format:pattern, argumentArray:[b]).evaluateWithObject(a)
    }
}

Второй оператор — функция, которая заменит скобки на <=>

infix operator <=> { associativity left }
func <=> <A, B>(f: A -> B, g: A) -> B {
    return f(g)
}

Примеры использования:

"SELF MATCHES %@" |*| "([0-9])\\w+" <=> "345"
// => true
"SELF == %@" |*| 3 <=> 4
// => false
"SELF CONTAINS %@" |*| "c" <=> "abc"
// => true

с фильтром:

struct User {
    let name: String
}

let users = [User(name: "text"), User(name: "787")]

users.filter({ "SELF MATCHES %@" |*| "([0-9])\\w+" <=> $0.name })
// => User(name: "787")
2016   predicate

Функторы

Функтор — это такой клас типов, которые реализуют функцию map. Это некий контейнер, к элементам которого можно применить функцию и в результате получить новый точно такой же контейнер (структура остается неизменной), но с новыми елементами. Иными словами, функтор применяет функцию к значениям, которые он содержит, а не к себе.

Функтором является тип Optional а также все последовательние типи в свифте: Dictionary, Array и Set.

К примеру, обычное сложение с использованием функтора будет выглядеть так:

func plusTwoNumbers(firstNumber: Int) -> (Int)-> Int {
    return { (secondNumber: Int) -> (Int) in
        return firstNumber + secondNumber
    }
}
let plus = plusTwoNumbers(5)
Optional.Some(10).map(plus)
// => .Some(15)

Функтор определяет, как будет будет реализована функция map и поскольку Optional это функтор, то у него есть своя реализация:

func map<U>(f: T -> U) -> U? {
  switch self {
  case .Some(let x): return f(x)
  case .None: return .None
}

Получается, что если мы в начале имеем None, то и в результате также будет иметь None. В случае с языками, которые не имеют опциональных типов, приходится делать проверку:

let user = Users.findByID(1)
if user != nil {
  return user.name
} else {
  return nil
}

Когда в свифте с исплозованием функтора Optional тоже самое можно сделать проще:

findByID(1).map(getUserName)
2016   functor   Swift

Интересные ссылки #0

Решил записывать интересные ссылки, как на статьи так и на видео.

Почитать:

  1. Best architectural approaches for building iOS networking applications (REST clients). Очень полезные ответы на вопрос о том, как правильно организовать структуру клиент приложения.
  2. Functors, Applicatives, and Monads in Plain English. Кратко и понятно о таких базовых вещах в функциональном программироовании как функторы, апликативные функторы и монады.
  3. Swift Alternatives to C-style for-loops. В третьей версии свифта больше не будет цыклов в стиле C, поэтому неплохо бы ознакомиться с альтернативами.
  4. Hello Server Side Swift. О бекенде на свифте с фреймворком Vapor.
  5. Как передать сообщение, понятное через 10 000 лет. Такой вопрос поставили в 1990 году перед группой учёных, чтобы решить проблему долгосрочного обслуживания хранилища ядерных отходов Waste Isolation Pilot Plant (WIPP). На английском — Episode 114: Ten Thousand Years.
  6. How the Colors Got Their Names. О том, как цвета получили свои названия.

Посмотреть:

  1. Запись собеседования. Интересно и познавательно, почуствовал себя на месте собеседуемого.
2016   ссылки

Композиция функций

В функциональном программировании часто используется построение функций, например построение функции f3 из двух функций f1 и f2 применив к ним функцию F, это и называется композицией функций. Расмотрим на простом примере. Создадим несколько функций, которые будут передаваться как параметры последовательно, первая функция создает и возвращает массив с работниками:

func createTeam(count: Int) -> [Employee] {
    var team = [Employee]()
    for i in 1...count {
        let f = i % 2 == 0
        team.append(Employee(id: i, pay: f ? i * 200 : i * 100, type: f ? .Dev : .QA))
    }
    return team
}

Вторая функция выбирает из масива только разработчиков и возвращает новый массив:

func getDevs(employees: [Employee]) -> [Employee] {
    return employees.filter({$0.type == .Dev})
}

И третья функция цепочки возвращает сумарную зарплату работников:

func getAverageSalary(employees: [Employee]) -> Int {
    return employees.reduce(0){$0 + $1.pay}
}

Осталось написать последнюю функцию, которая и будет композицией:

let salary = {count in getAverageSalary(getDevs(createTeam(count)))}

Теперь можно вызвать salary передав ей параметр count:

salary(5)

Также можно создать свой оператор для композиции, например >>> с левой асоциативностью (об кастомных операторах):

infix operator >>> { associativity left }
func >>> <A, B, C>(f: B -> C, g: A -> B) -> A -> C {
    return { x in f(g(x)) }
}

В итоге конструкция получится более изящной:

let salary2 = getAverageSalary >>> getDevs >>> createTeam
salary2(5)

Расширения и протоколы

Представим себе ситуацию, вам нужно добавить метод смены цвета для стандартных классов вроде UIView, UIImageView. Поскольку UIImageView наследуется от UIView, то написав расширение для UIView, все обьекты типа UIImageView также получат новый функционал, но проблема в том, что цвет у обычной вью и картинки меняется разными способами, у UIView это backgroundColor, когда у UIImageView это tintColor. Решить проблему можно используя протокол:

protocol ColorCustomizable {
    func setColor()
}

extension UIView: ColorCustomizable {
    func setColor() {
        backgroundColor = UIColor.blackColor()
    }
}

Указывать протокол для UIImageView не нужно, он уже реализован в родительском классе UIView, осталось только переопределить метод:

extension UIImageView {
    override func setColor() {
        tintColor = UIColor.blackColor()
    }
}
2016   extension   protocol   Swift
Ctrl + ↓ Earlier