Programación funcional con swift. Se ven conceptos como funciones de primera clase, funciones de orden superior, métodos como filter, map y el patrón Result para la gestión de errores.
4. Las funciones imperativas pueden tener efectos secundarios,
como cambiar el valor de cálculos realizados previamente.
Imperativo
Las funciones declarativas, el valor generado por una función
depende exclusivamente de los argumentos alimentados a la función.
Al eliminar los efectos secundarios se puede entender
y predecir el comportamiento de un programa mucho más fácilmente.
Declarativo
5. Stateless
struct User {
var id: Int
var name: String
var isActive: Bool
}
class KeyboardViewController: UIInputViewController {
var nameActiveUsers:[String] = []
var users: [User] = []
override func viewDidLoad() {
super.viewDidLoad()
users = [User(id: 1, name: "Juan", isActive: true),
User(id: 2, name: "Pedro", isActive: false),
User(id: 3, name: "Alan", isActive: true),
User(id: 4, name: "Pablo", isActive: false)]
getNameActiveUsers()
print(nameActiveUsers)
}
func getNameActiveUsers(){
var activeUsers = [User]()
for user in users{
if user.isActive{
activeUsers.append(user)
}
}
activeUsers.sort { (user1, user2) -> Bool in
return user1.id < user2.id
}
for active in activeUsers{
nameActiveUsers.append(active.name)
}
}
}
6. Stateless
struct User {
var id: Int
var name: String
var isActive: Bool
}
class KeyboardViewController: UIInputViewController {
var nameActiveUsers:[String] = []
var users: [User] = []
override func viewDidLoad() {
super.viewDidLoad()
users = [User(id: 1, name: "Juan", isActive: true),
User(id: 2, name: "Pedro", isActive: false),
User(id: 3, name: "Alan", isActive: true),
User(id: 4, name: "Pablo", isActive: false)]
getNameActiveUsers()
print(nameActiveUsers)
}
func getNameActiveUsers(){
var activeUsers = [User]()
for user in users{
if user.isActive{
activeUsers.append(user)
}
}
activeUsers.sort { (user1, user2) -> Bool in
return user1.id < user2.id
}
for active in activeUsers{
nameActiveUsers.append(active.name)
}
}
}
Orientado
a instrucciones
Imperativo
Defino “cómo” se realiza un proceso para alcanzar un resultado.
7. Stateless
struct User {
var id: Int
var name: String
var isActive: Bool
}
Orientado
a expresiones
Declarativo
class KeyboardViewController: UIInputViewController {
override func viewDidLoad() {
super.viewDidLoad()
let users = [User(id: 1, name: "Juan", isActive: true),
User(id: 2, name: "Pedro", isActive: false),
User(id: 3, name: "Alan", isActive: true),
User(id: 4, name: "Pablo", isActive: false)]
print(getNameActiveUsers(users: users))
}
func getNameActiveUsers(users: [User]) -> [String]{
return users.filter{$0.isActive}
.sorted{$0.id < $1.id}
.map{$0.name}
}
}
“Di qué quieres, pero no cómo lo quieres”
8. Stateless
struct User {
var id: Int
var name: String
var isActive: Bool
}
class KeyboardViewController: UIInputViewController {
override func viewDidLoad() {
super.viewDidLoad()
let users = [User(id: 1, name: "Juan", isActive: true),
User(id: 2, name: "Pedro", isActive: false),
User(id: 3, name: "Alan", isActive: true),
User(id: 4, name: "Pablo", isActive: false)]
print(getNameActiveUsers(users: users))
}
func getNameActiveUsers(users: [User]) -> [String]{
return users.filter{$0.isActive}
.sorted{$0.id < $1.id}
.map{$0.name}
}
}
class KeyboardViewController: UIInputViewController {
var nameActiveUsers:[String] = []
var users: [User] = []
override func viewDidLoad() {
super.viewDidLoad()
users = [User(id: 1, name: "Juan", isActive: true),
User(id: 2, name: "Pedro", isActive: false),
User(id: 3, name: "Alan", isActive: true),
User(id: 4, name: "Pablo", isActive: false)]
getNameActiveUsers()
print(nameActiveUsers)
}
func getNameActiveUsers(){
var activeUsers = [User]()
for user in users{
if user.isActive{
activeUsers.append(user)
}
}
activeUsers.sort { (user1, user2) -> Bool in
return user1.id < user2.id
}
for active in activeUsers{
nameActiveUsers.append(active.name)
}
}
}
// [“Juan”, “Alan”]
// [“Juan”, “Alan”]
9. Funciones de primera clase
func getURLFromString(url:String) -> URL{
return URL(string: url)!
}
override func viewDidLoad() {
super.viewDidLoad()
let urlImage = “https://www.apple.com.mx"
let url = getURLFromString(url: urlImage)
}
Un lenguaje de programación se dice que tiene Funciones de primera clase cuando
las funciones en ese lenguaje son tratadas como cualquier otra variable.
10. Funciones de primera clase
func getImage(urlImage: String) -> UIImage{
let url = getURLFromString(url: urlImage)
let data = getDataFromURL(url:url)
let image = getImageFromData(data: data)
return image
}
func getURLFromString(url:String) -> URL{
return URL(string: url)!
}
func getDataFromURL(url: URL) -> Data{
return try! Data(contentsOf: url)
}
func getImageFromData(data:Data) -> UIImage{
return UIImage(data: data)!
}
11. Funciones de orden Super
Pueden tomar otra función como parámetro:
func getURLFromString(url:String) -> URL{
return URL(string: url)!
}
func getDataFromURL(url: URL) -> Data{
return try! Data(contentsOf: url)
}
func getImageFromData(data:Data) -> UIImage{
return UIImage(data: data)!
}
func getImage(url: String, completion: (Data) -> UIImage) -> UIImage{
let url = getURLFromString(url: url)
let data = getDataFromURL(url:url)
return completion(data)
}
let image = getImage(url: urlImage, completion: getImageFromData)
12. Funciones de orden Super
Pueden tomar otra función como parámetro:
func getURLFromString(url:String) -> URL{
return URL(string: url)!
}
func getDataFromURL(url: URL) -> Data{
return try! Data(contentsOf: url)
}
func getImageFromData(data:Data) -> UIImage{
return UIImage(data: data)!
}
func getImage(url: String, completion: (Data) -> UIImage) -> UIImage{
let url = getURLFromString(url: url)
let data = getDataFromURL(url:url)
return completion(data)
}
let image = getImage(url: urlImage, completion: getImageFromData)
Firma de la función
(Data) -> UIImage
13. Funciones de orden Super
Pueden devolver una función de salida:
func getImage(urlImage: String) -> (CGRect) -> UIImage {
return {
let image = self.getImageFromData(data: self.getDataFromURL(url: self.getURLFromString(url: urlImage)))
image.draw(in: $0)
return image
}
}
let imageWithSize = image(CGRect(x: 0, y: 0, width: 50, height: 50))
14. Funciones de orden Super
Pueden devolver una función de salida:
func getImage(urlImage: String) -> (CGRect) -> UIImage {
return {
let image = self.getImageFromData(data: self.getDataFromURL(url: self.getURLFromString(url: urlImage)))
image.draw(in: $0)
return image
}
}
let imageWithSize = image(CGRect(x: 0, y: 0, width: 50, height: 50))
18. Patron Result
Esto es similar al tipo Opcional nativo de Swift:
el “some” es que tiene un valor, y el “none” es que no tiene.
Es parecido a las promesas en Javascript
enum Result<S,E>{
case success(_ :S)
case failure(_ :E)
}
19. Patron Result
class NotificationParser {
private func serializeResponse(mapString:String) -> [String: Any]?{
return try! JSONSerialization.jsonObject(with: mapString.data(using: .utf8)!, options: [.allowFragments]) as? [String: Any]
}
func parseSomething(mapString: String, completion: @escaping(_ success: Bool, _ object: Any?) -> Void) {
guard let dataJSON = serializeResponse(mapString: mapString) else {
return completion(false, nil)
}
if dataJSON["success"] != nil {
if dataJSON["success"] as! Bool {
if let responseDictionary = dataJSON["data"] as? NSDictionary {
let response = User.from(responseDictionary)
print("CreateAccount: (response!)")
completion(true,response)
} else {
completion(false, nil)
}
}else{
if let responseDictionary = dataJSON["errors"] as? NSArray {
let errors = ErrorsResponse.from(responseDictionary)
print("Valores del modelo errors: (errors!)")
completion(false,errors?.first)
} else {
completion(false, ErrorsResponse.from(["status":0,"message":"duplicate"]))
}
}
} else {
completion(false, nil)
}
}
}
20. Patron Result
enum ErrorType{
case notSerialized
case notInternet
case serverError(String)
case messageErrorEmpty
}
extension ErrorType{
var description:String {
switch self {
case .notSerialize:
print("Not Serilized")
return "Error From Server"
case .notInternet:
print("there are not connection")
return "Not Internet Conection"
case .serverError (let message):
return message
case .messageErrorEmpty:
return ""
}
}
}
class NotificationParser{
private func serializeResponse(mapString:String) -> [String: Any]?{
return try! JSONSerialization.jsonObject(with: mapString.data(using: .utf8)!, options: [.allowFragments]) as? [String: Any]
}
func parseSomething(mapString:String, completion: @escaping (Result<User,ErrorType>) -> Void){
guard let dictionary = serializeResponse(mapString: mapString) else {
return completion(.failure(.notSerialized))
}
guard let success = dictionary["success"] as? Bool else{
guard let errorMessage = dictionary["message"] as? String else{
return completion(.failure(.messageErrorEmpty))
}
return completion(.failure(.serverError(errorMessage)))
}
guard let responseDictionary = dictionary["data"] as? Dictionary else{
return completion(.failure(.notSerialized))
}
let response = User.from(responseDictionary)
print("CreateAccount: (response!)")
completion(.success(response))
}
}
21. Patron Result
class NotificationViewControler:UIViewController {
let manager = Managers.shareInstance
override func viewDidLoad() {
super.viewDidLoad()
self.manager.createUser(email:email, password:password, completion: { [weak self] response in
guard let strongself = self else{ return }
switch response{
case .success(let user):
strongSelf.titleLabel.text = user.name
case .failure(let error):
showAlertOK(error.description)
}
}
}
}
26. Beneficios
- Código concurrente.
- Menos errores. Ya que la mayoría de tu código será con constantes.
- Fácil de testar. Ya que un objeto se puede considera valido desde su creación,
de ser así se va a considerar válido durante toda su existencia.
- Código paralelisable. Que se puede ejecutar en cores diferentes al mismo tiempo.
- Tolerante a fallos gracias al patron Result