ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 148
Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 80
Abonament PDF

Extensiile din Swift

Mihai Fischer
mihaifischer.com



PROGRAMARE

Proiectele iOS pot deveni foarte ușor pline de clase foarte mari, greu de citit și de înțeles. Deși în Swift putem spune că am scăpat de jumătate din fișierele dintr-un proiect, aglomerarea din interiorul lor, în special a viewControllerelor a rămas cel puțin la fel.

În acest articol, voi prezenta trei modalități în care extensiile pot ajuta în păstrarea ordinii și lizibilității codului.

Extensiile din Swift sunt folosite în mod normal, pentru a extinde funcționalitatea unui tip, unei clase, unui struct, enum sau al unui protocol.

Asemănător categoriilor din Objective-C, extensiile pot fi folosite și la extinderea unor tipuri la care nu avem acces la codul sursă. Dar, spre deosebire de categorii, sunt anonime:

// se foloseste cuvantul cheie extension urmat de 
// tipul extins
extension UIViewController {
   // noua funcționalitate
}

Conformarea la noi protocoale se face la fel ca la clase sau structuri:

extension UIViewController: Protocol, AltProtocol {
   // implementarea noilor protocoale
}

Separarea metodelor private de partea publică a clasei

În Objective-C am avut fișierele .h și .m. Cu toate că era anevoios de le menținut pe ambele, era ușor să vezi interfața publică a clasei respective doar aruncând o scurtă privire în .h. În timp ce tot ce era privat, se afla în fișierul de implementare .m.

Cum în Swift avem un singur fișier, ne putem folosi de extensii pentru a obține o implementare asemănătoare.

// așa e ușor să vezi care metode și proprietăți ale 
// structurii sunt publice
struct TodoItemViewModel {    
    let item: TodoItem
    let indexPath: NSIndexPath

    var delegate: ImageWithTextCellDelegate {
        return TodoItemDelegate(item: item)
    }

    var attributedText: NSAttributedString {
        // logica itemContent e în extensia privată
        // făcând codul mult mai ușor de citit
        return itemContent
    }
}

// toată logica e în afara API-ului public
// MARK: Metode Private
private extension TodoItemViewModel {

  static var spaceBetweenInlineImages: 
   NSAttributedString {
        return NSAttributedString(string: "   ")
    }

  var itemContent: NSAttributedString {
    let text = NSMutableAttributedString(
      string: item.content, attributes: 
      [NSFontAttributeName : 
  Monsetrat.regularFontOfSize(16.0)])

    if let dueDate = item.dueDate {
      appendDueDate(dueDate, toText: text)
    }

    for assignee in item.assignees {
      appendAvatar(ofUser: assignee, toText: text)
    }

      return text
    }

    func appendDueDate(dueDate: NSDate, 
      toText text: NSMutableAttributedString) {
        //...
    }

    func appendAvatar(ofUser user: User, 
      toText text: NSMutableAttributedString) {
       //...
    }

    func downloadAvatarImage(
      forResource resource: Resource?) {
        //...
    }

    func appendDefaultAvatar(ofUser user: 
      User, toText text: NSMutableAttributedString) {
        //...
    }

    func appendImage(image: UIImage, 
      toText text: NSMutableAttributedString) {
      //...

}

Conformarea la protocoale

Cum afirmam și la începutul articolului, în Swift se pot folosi extensiile pentru conformarea unui tip existent la un protocol.

Vom da ca exemplu protocoalele UITableViewDalagate si UITableViewDataSource. Este un caz foarte des întâlnit, care poate duce la îngreunarea controllerului în care se face implementarea.

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    //...

}

Putem organiza mai bine codul, creând o extensie pentru fiecare protocol ce urmează implementat:

import UIKit

class ViewController: UIViewController {
    //...
}

extension ViewController: UITableViewDataSource {
    //...
}

extension ViewController: UITableViewDelegate {
    //...
}

În acest fel, inclusiv navigarea în fișier devine mai ușoară.

Salvarea funcțiilor de inițializare implicite

Vom da ca exemplu o structură simplă cu două constante. Deși este ușor de trecut cu vederea peste, Swift ne creează în mod implicit o funcție de inițializare.

struct Person {
    // MARK: - Proprietăți

    let first: String
    let last: String

}

// inițializare implicită:
let person = Person(first: "Ionica", last: "Magie")

În momentul în care vrem să facem o funcție de inițializare personalizată, cea implicită nu mai este disponibilă.

struct Person {

    // MARK: - Proprietăți

    let first: String
    let last: String

    // MARK: - Inițializare

    init(dictionary: [String: String]) {
        self.first = dictionary["first"] ?? "Ionica"
        self.last = dictionary["last"] ?? "Magie"
    }
}

Putem preveni acest lucru cu extensii. E bine să avem disponibile toate funcțiile de inițializare, chiar și cea implicită, lucru care duce la o transparență mai bună a ce se întâmplă în proiect.

struct Person {
    // MARK: - Proprietăți
    let first: String
    let last: String
}
extension Person {
    // MARK: - Inițializare

    init(dictionary: [String: String]) {
        self.first = dictionary["first"] ?? "Ionica"
        self.last = dictionary["last"] ?? "Magie"
    }
}

Concluzie

Extensiile folosite în acest mod mai puțin clasic, pot face un proiect mult mai ușor de înțeles și de citit. Dar important este să menținem echilibrul și să nu exagerăm cu întrebuințarea lor.

Conferință TSM

NUMĂRUL 147 - Automotive

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects