SWIFT – VALUE TYPE vs REFERENCE TYPE

Conform Apple “Structures and Enumerations Are Value Types” iar mai jos că “Classes Are Reference Types”.

Explicatie pe scurt!

struct Informatii {
      var info: Int; 
}
var firstStruct = Informatii(info:3)
var secondStruct = firstStruct						
firstStruct.info = 10						
print("\(firstStruct.info), \(secondStruct.info)")	// prints "10, 3"

Struct-ul, la fel ca si ENUM si TUPLU în SWIFT, fiind de tip valoare, atunci cand este copiata va crea o instanță independentă care va avea propriul său conținut independent de cel al sursei din care s-a copiat. Un fel de clonă independentă am putea spune.

class Informatii {
      var info: Int; 
}
var firstObject = Informatii(info:3)
var secondObject = firstObject						
firstObject.info = 10						
print("\(firstObject.info), \(secondObject.info)")	// prints "10, 10"

Deși în acest caz vorbim doar de înlocuirea unui cuvânt din STRUCT în CLASS, deosebirile sunt remarcabile.

firstObject este o instanță de tip Informatii. Are o zonă de memorie alocată și practic este un pointer către acea zonă.
Atunci când executăm secondObject = firstObject, ambele fiind deci de instanțe de clasă adică de tip referință, secondObject va pointa de fapt către aceeași zonă de memorie în care pointa și firstObject. Orice modificare efectuată de firstObject se va oglindi în secondObject.

Share Button

UPDATE – Protocoale & extensii Swift

Vreau să exemplific puterea unui protocol în Swift.
Avem două clase, aparent fără nimic în comun:

class Pescar {
    var nume : String
    var numarPermis: String
}
class Color {
    var rosu : Int
    var verde : Int
    var albastru : Int
}

Vreau să le convertim pe fiecare la String pentru a avea un output decent și corect pentru situații de genul:

print(Pescar(...)) 
print(Color(...))

Swift implementează un protocol exact pentru acest caz numit CustomStringConvertible. Prin urmare, vom extinde clasele noastre cu ajutorul acestui protocol CustomStringConvertible astfel încât print-ul pe clase sa fie custom:

extension Pescar : CustomStringConvertible {
    var description : String { get { return "Nume pescar: \(nume) - numar de permis: \(numarPermis)" } }
}
extension Color : CustomStringConvertible {
    var description : String { get { return "Color: \(rosu) -  \(verde) - \(albastru)" } }
}

Prin urmare, vom avea:

let pescarulGica = Pescar(nume: "Gica", numarPermis: "1234")

print(pescarulGica) // Nume pescar: Gica - numar de permis: 1234

și

let codCuloareCustom = Color(rosu: 23, verde: 45, albastru: 52)

print(codCuloareCustom) // Color: 23 -  45 - 52
Share Button

Protocoale si extensii – Swift 3

De ce este nevoie să introducem protocoale în Swift atât timp cât avem de-a face cu clase și putem subclasa?
Swift este Protocol Object Oriented. În Swift protocoalele reprezintă o modalitate bună de a defini un set de funcționalități necesare pe care clasele sau structurile le pot adopta.
Pentru a fi mai clari, un protocol oferă informații despre ce poate face o clasă, nu neapărat ce este o clasă.

De ce protocol și nu sub-clasare?

Să luăm un exemplu de sub-clasare:

class Animal {
    func ceZgomotFace() { fatalError("Implementeaza cod pentru functie!") }
}

Bun, avem deci de-a face cu o clasa abstractă care nu poate fi instanțiată direct ci de către subclase.

Acum introducem o subclasă numită Caine care va încărca metoda ceZgomotFace

class Caine: Animal {
    override func ceZgomotFace() { print("HamHam!") }
}

let rex = Caine()
rex.ceZgomotFace() //prints "HamHam!"

Ce se întâmplă dacă ori uităm să încărcăm în subclasă metoda ceZgomotFace sau să apelăm direct o instanță de Animal?

let iepure = Animal()
iepure.ceZgomotFace() //CRASH

class Cat: Animal { }
let pisi = Cat()
pisi.ceZgomotFace() //CRASH

Prin urmare, aspectul cel mai deranjant este că în orice subclasă am avea pentru o clasă abstractă, trebuie să implementăm metodele din clasa mamă – Animal.

Swift vine cu o abordare interesantă în acest caz – protocolul.

protocol Sunet {
    func ceZgomotFace() 
}

Nu ne interesează în acest moment de ce tip este și cine va implementa protocolul Sunet, important pentru noi va fi doar faptul că metoda ceZgomotFace va fi definită în clasa conformă cu protocolul.

class Caine: Sunet {
    func ceZgomotFace() {
        print("Woof!")
    }
}
 
class Arbore: Sunet {
    func ceZgomotFace() {
        print("Fasssss!")
    }
}
 
class iPhone: Sunet {
    func ceZgomotFace() {
        print("Clinc-clinc!")
    }
}

Nimic spectaculos până acum. Cam același lucru-l fac și subclasele.

Dar… avem protocol extensions care de fapt definește Swift ca și limbaj obiectual orientat protocol.

class Fiinta {
    func mananca() {
    print("mi-e foame!")
    }
}

protocol Sunet {
    func ceZgomotFace() 
}

Avem o clasă Fiinta care implementează metoda mananca() și un protocol Sunet pe care dacă cineva îl va folosi va trebuie să implementeze ceZgomotFace()

extension Sunet where Self: Fiinta {
    func rade() {
        print("HAHA!");
    }

    func plange() {
        print("HOHO!");
    }
}

Extensia protocolului Sunet va fi valabilă doar pentru clasa Ființă și pentru subclasele sale, doar ele având acces la metodele rade și plange

Și acum să vedem puterea protocolului.

class Om : Fiinta, Sunet { 
    func ceZgomotFace() {
        print("Pot face ce zgomot vreau!");
    }
}

class Iphone : NSObject, Sunet { 
    func ceZgomotFace() {
        print("Clinc-clinc!");
    }
}

Clasa Om va mosteni clasa de baza Fiinta și, de asemenea, va putea folosi toate metodele din extensia Sunet.
Prin urmare, o instanta Om va putea accesa metodele din extensia Sunet: rade() și plange() dar va implementa și metoda din protocol – ceZgomotFace().

Prin urmare,

let georgel = Om()
georgel.ceZgomotFace() // Pot face ce zgomot vreau!
georgel.rade() // HAHA!
georgel.plange() // HOHO!

Clasa Greiere va putea folosi doar metodele definite în protocolul Sunet, neavând acces la extensia de protocol deoarece nu are ca Fiinta ca și clasă de bază.

Share Button

NotificationCenter – transmiterea de mesaje în interiorul aplicației – SWIFT 3

Am următorul context: atunci când un utilizator se autentifică, aplicația trebuie să “știe” acest lucru și să execute un task în consecință.
Acest lucru poate fi realizat prin “postarea” unei notificări la autentificare reușită.
În acest sens:

1. Definim un Notification.Name – “UtilizatorAutentificat”. Acesta nu este String, el putând fi folosit oriunde e cerut un Notification.Name.

let myNotification = Notification.Name(rawValue:"UtilizatorAutentificat")

2. “Observăm” apariția notificării mele numită anterior myNotification, atunci când ea va apărea fiind apelată metoda catchNotification

let nc = NotificationCenter.default
nc.addObserver(self,selector: #selector(catchNotification),name: myNotification,object: nil)

3. Definim metoda catchNotification, apelată la generarea notificării:

func catchNotification() {
    print("Catch notification")
}

4. În momentul în care user-ul s-a autentificat postăm notificarea noastră definită ca myNotification

let nc = NotificationCenter.default
nc.post(name: Notification.Name(myNotification), object: nil)
Share Button

sendSynchronousRequest Swift 3

Am de migrat o aplicație din Swift 2.3 în Swift 3 pe un Deployment Target 10, adică aduc aplicația astfel încât ea sa folosească avantajele IOS 10+.
Aplicația pe care o upgradez folosește la un moment dat următoarea funcție de verificare în mod sincron a conectivității la server:


private func isServerReachable_Swift2() -> Bool {

        var Status:Bool = false

        let url = NSURL(string: "URL dorit")

        let request = NSMutableURLRequest(url: url! as URL)

        request.httpMethod = "POST"

        request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData

        request.timeoutInterval = 10.0

        request.allowsCellularAccess = true

        var response: URLResponse?

        _ = (try? NSURLConnection.sendSynchronousRequest(request as URLRequest, returning: &response)) as NSData?

        if let httpResponse = response as? HTTPURLResponse {

            if httpResponse.statusCode == 200 {

                Status = true

                Log.d("server reachable")

            }

        }

        return Status;

    }

Ceea ce este depreciat în IOS 9+ este tocmai folosirea acestui sendSynchronousRequest(_:returning:). Corectă ar fi fost și folosirea unui try-catch pentru sendSynchronousRequest sub forma:

do {
    let data = try NSURLConnection.sendSynchronousRequest(request as URLRequest, returning: &response)
} catch (let e) {
    print(e)
}

Chiar dacă această problemă legată de synchronous este destul de delicată, SWIFT oferă întotdeauna o cale de a rezolva o astfel de problemă printr-un asynchronous pattern, adică prin adăugarea unui completion handler funcției mele.

Însă am preferat să explorez partea de Semaphores din SWIFT care mă va ajuta să realizez un request synchronous și să astept răspunsul de la server.

   private func isServerReachable() -> Bool {
        var status:Bool = false
        let dispatchSemaphore = DispatchSemaphore(value: 0)

        let url = NSURL(string: "URL dorit")
        let request = NSMutableURLRequest(url: url! as URL)
        request.httpMethod = "POST"
        request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData
        request.timeoutInterval = 10.0
        request.allowsCellularAccess = true
        
        let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
            if let httpResponse = response as? HTTPURLResponse {
                if httpResponse.statusCode == 200 {
                    status = true
                    dispatchSemaphore.signal()
                    Log.d("server reachable")
                }
            }
            
        })
        task.resume()
        // blochez thread-ul pana cand apare signal pe dispatchSemaphore
        dispatchSemaphore.wait()
        
        Log.d("Response in main thread status = \(status)")
        return status;
    }
Share Button