Lucrul cu DDXMLElement si DDXMLNode – Swift

Se da urmatorul XML generat:

{"message":"bubu","name":"tester","contact_id":"7","to":"tester"}


Vreau sa scot din XML nodul "message": "bubu"
Solutie:
1. do {
completeMessage = try element.nodes(forXPath: "body")
} catch _ {
completeMessage = [DDXMLNode]()
}

In acest moment completeMessage este sub forma vectoriala:
completeMessage - [{"message":"bubu","name":"tester","contact_id":"7","to":"tester"}]
2. let mesg = (completeMessage.first)?.child(at: 0)
let finalMessage = ((mesg?.stringValue)?.convertToDictionary())?["message"]

Extrag primul si singurul element din vector, il convertesc in dictionar si extrag campul “message”
finalMessage este in acest moment Optional(“bubu”) insa tipul este Any?
3. let bodyMessage = finalMessage as? String ?? ""
print("bodyMessage - \(bodyMessage)")

In acest moment am valoarea sub forma de string si anume “bubu”

Share Button

Swift 3 – Xcode -View – Aspect Fit, Aspect Fill si Scale To Fit

Nu de putine ori atunci cand adaugam o poza (image) intr-un ImageView Controller avem probleme cu dimensionarea acesteia. In mod normal o poza este deformata pentru a intra in ImageView si nu se tine cont de ratio in acest caz. E cazul VIEW -> MODE -> SCALE TO FIT

 

Daca vreau sa imi pastrez original ratio pentru poza astfel incat ea sa se incadreze in ImageView Controller-ul meu, solutia este VIEW -> MODE -> ASPECT FIT. Poza este corect redimensionata pe dimensiunea controller-ului.

 

O alta varianta ar fi l VIEW -> MODE -> ASPECT FILL, caz in care poza este incadrata pe original ratio insa nu se mai respecta granita ImageView-ului, imaginea fiind incadrata corect pe dimensiunea ecranului.

 

Share Button

Coding Standards – Swift

Incep acum o lista de standarde de codare pentru Swift 3 pe care o s-o actualizez in timp.

1. Swift Collection Types

incerc cat se poate sa evit folosirea tipurilor din Objective C – NSArray, NSDictionary, and NSSet. Exemplul cel mai des intalnit este prelucraerea unui JSON:
Calea pe care o folosesc de cele mai multe ori este:
var arrayOfJSONObjects = [[String: AnyObject]]()
...
let names: AnyObject? = (arrayOfJSONObjects as NSArray).value(forKeyPath: "name")

O pot adapta intr-un mod cat mai Swifty prin utilizarea flatMap astfel:

var arrayOfJSONObjects = [[String: AnyObject]]()
...
let names: [String] = arrayOfJSONObjects.flatMap { object in
return object["name"] as? String
}

2. Optionals

Discutia despre optionals este lunga si la inceput m-am uitat cu mirare la acest nou tip de date. De ce ar mai fi nevoie de inca un tip de date in plus care sa contina doua valori, nil daca in “borcanel” nu e nimic sau valoarea propriu-zisa, daca “borcanelul” e plin?
Numai utilizand si codand, mi-am dat seama de puterea optioanls.
Discutiile pe tema lor ar include urmatoarele aspecte:

2.1. Force unwrapping

In general evit sa fortez unwrapping-ul optional-ului. Utilizarea lui as! sau ! poate genera un crash in prezenta unui nil. Swift recomanda utilizarea uneia dintre variantele: guard let, if let, guard let as?, if let as? sau optional chaining.

Un astfel de caz:

// URL init(string:) este un failable initializer, adica putem avea genera un nil datorat lipsei string-ului

let url = URL(string: “http://www.example.com/”)!
UIApplication.shared.open(url)

O solutie Swifty in acest caz ar fi:

// prezenta guard-ului imi garanteaza faptul ca open(url) se va executa doar in cazul in care url nu este nil

guard let url = URL(string: "http://www.example.com/") else {
return
}
UIApplication.shared.open(url)

O alta situatie cu care m-am confruntat este folosirea incorecta a unui optional chaining.
O astfel de abordare ca cea de mai jos poate forta existenta metodei delegatului, in conditiile in care delegatului poate da in nil.:
self.delegate!.didSelectItem(item)
Corect ar fi sa scriu:
self.delegate?.didSelectItem(item)

2.2. Pyramid of Doom – if let

Deseori m-am confruntat cu acest caz de Pyramid of Doom:
if let id = jsonObject[Constants.id] as? Int {
if let firstName = jsonObject[Constants.firstName] as? String {
if let lastName = jsonObject[Constants.lastName] as? String {
if let initials = jsonObject[Constants.initials] as? String {
let user = User(id: id, firstName: name, lastName: lastName, initials: initials)
// ...
}
}
}
}

Pentru a evita imbricarea if let-urilor, corect ar fi:

if

let id = jsonObject[Constants.Id] as? Int,
let firstName = jsonObject[Constants.firstName] as? String,
let lastName = jsonObject[Constants.lastName] as? String,
let initials = jsonObject[Constants.initials] as? String {
let user = User(id: id, name: name, initials: initials)
// …
}

2.3 guard let vs. if let

Chiar daca la inceput foloseam din reflex if let, am realizat in timp puterea lui guard let. Codul arata mai bine, e mult mai clar cu guard let
Astfel in loc de

func openURL(string: String) {
if let url = URL(string: string) {
UIApplication.shared.open(url)
}
}

prefer sa folosesc pentru claritate:

func openURL(string: String) {
guard let url = URL(string: string) else {
return
}
UIApplication.shared.open(url)
}

3. Tratarea erorilor

Este de evitat folosirea unei secvente try! sub aceasta forma:
let json = try! JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)

Pot aparea erori pe parsarea de date astfel incat try! sa duca in crash.
Este recomandata o sintaxa sub forma urmatoare:
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
} catch {
print(error)
}

Share Button

Core Data – Swift 3

Voiam sa fac un articol detaliat despre cum pot folosi Core Data in Swift3. Plecand de la acest articol unde este explicat motivul pentru care a fost introdus NSPersistentContainer in IOS 10, am ajuns la un material care explica extraordinar cat de versatila este utilizarea Core Data in IOS 10 si Swift 3.




Ca si observatie, pentru a nu aparea surprize atunci cand este folosit numele clasei in atributul forEntityName
let student:Student = NSEntityDescription.insertNewObject(forEntityName: "Student", into: DatabaseController.getContext()) as! Student
este recomandata definirea numelor claselor astfel:
let studentClassName:String = String(describing: Student.self)
let courseClassName:String = String(describing: Course.self)

Observatia este facuta la minutul 51:50.

Share Button

SQLite – Swift3

Ca si solutie de persistenta, pe langa Core Data si NSUserDefault, IOS utilizaza SQLite. Nu insist pe povesti despre SQLite, insa fiind disponibil si pe Android, am considerat oportuna folosirea lui.
Pe Swift, pentru a folosi SQLite e nevoie de un wrapper, o aplicatie de background care sa parseze cererile din aplicatie catre SQLite.
Solutii disponibile in acest sens: FMDB sau SQLite Swift.
Pentru un cod suplu al aplicatiei este de recomandat construirea unui wrapper propriu, de fapt a unui set de functii care vor apela comenzi SQL.
Cum folosim SQLite in Swift?

1. In General Setting specificam faptul ca vom lega libsqlite3.tbd la aplicatie.


2. Adaugam un header file la proiect care va permite bridging catre API-ul C al lui SQLite. Numim de exemplu BridgingHeader.h acest header file si includem in el
#include

3. In Build Settings specificam faptul ca vom folosi acest bridge. Pentru aceasta, adaugam in sectiunea “Objective-C Bridging Header” header file pe care l-am generat la pasul 2.



4. Aplicatia noastra este pregatita in acest moment pentru a folosi SQLite.

In urmatorul post voi detalia calea prin care voi defini modelul pe care il voi folosi pentru a putea “executa” comenzi SQL

Share Button