WWDC 2020, novedades en el mundo del desarrollo Apple. (1ª parte)

0
540
WWDC 2020

Índice de contenidos

  • 1. Introducción
  • 2. Transición a ARM (Apple Silicon)
  • 3. Xcode 12
  • 4. Mac Catalyst
  • 5. Novedades en SwiftUI
  • 6. Novedades en UIKit
  • 7. Conclusiones

 

1. Introducción.

La navidad para los desarrolladores de entornos Apple ha llegado y este año de una forma totalmente virtual debido a las complicaciones del COVID. Sin público, y todas las sesiones  pre grabadas y con posibilidad de hablar con los ingenieros de Apple mediante un sistema de preselección de las dudas que podemos presentar. Vamos a ver por encima las novedades más destacas desde el punto de vista del desarrollo.

 

2.Transición a ARM (Apple Silicon)

A día 23 de Junio de 2020 Apple ha dado un paso hacia la independencia total de su producción de hardware anunciando la transición en Mac a sus propios procesadores con arquitectura ARM llamados Apple Silicon. 

En la keynote han usado un Mac con un chip A12Z (los que llevan los actuales iPad Pro) en la que mostraban aplicaciones ya corriendo en los mac con esta arquitectura. Apple ya está trabajando con las grandes de la industria del desarrollo para que porten sus aplicaciones a la nueva arquitectura, de todas formas si no se lleva a cabo la importación, Apple ha creado Roseta 2.0 que se encargará de traducir de una arquitectura a otra en tiempo de instalación.

Con este cambio también llegan las apps universales, esto es, que con un solo binario vamos a poder desplegar en todas las plataformas de Apple,  y ya que corren la misma arquitectura, vamos a poder ejecutar aplicaciones de iOS e iPadOS sin modificación alguna en nuestros mac.

Todo indica que Apple tiene muy claro su objetivo de unificar su ecosistema, con el proyecto Catalyst y con SwiftUI cada vez tenemos más fácil los desarrolladores escribir una sola aplicación compatible con todas las plataformas de la manzana.

 

3. Xcode 12

Las novedades de Xcode tampoco han pasado desapercibidas, el nuevo IDE de Apple ahora contiene las siguientes características:

  • Fuentes personalizables en el navegador.
  • Mejoras en el auto completado.
  • Pestañas para documentos.
  • Creación de apps universales, con plantillas multiplataforma.
  • Nuevo Organizer.
  • Mejoras para testar StoreKit en local.
  • Mejoras en la auto-indentación.
  • Previsualización de widgets, App clips y contenido de paquetes (SPM).
  • Soporte a formato SVG en Assets catalogs.
  • Playgrounds ahora soporta Swift Package Manager.

 

4. Mac Catalyst

Las novedades que encontramos en Mac Catalyst son:

  • Mejoras en el escalado de la aplicación.
  • Nuevos frameworks disponibles para la migración(HomeKit y AVCapture).
  • Mejoras en la API de teclado.
  • Adaptación a la apariencia de MacOS Big Sur automática.

 

5. Novedades en SwiftUI

Apple está apostando muy fuerte con esta tecnología, hemos que tener claro que éste es el futuro para ellos y todos los esfuerzos se están enfocando a ello. Los nuevos frameworks de WidgetKit y Appclip están escritos enteramente en SwiftUI para SwiftUI.

Hay muchas, muchísimas novedades en SwiftUI que nos ha traído la WWDC2020, vamos a ver las más destacas:

  • Ahora podemos acceder a la vista vista de debug desde las preview sin necesidad de compilar el código.
  • Las apps escritas en SwiftUI ocupan mucho menos espacio.
  • Podemos despedirnos de los storyboards por completo en una aplicación 100% SwiftUI modificando el nuevo parámetro del plist Launch Screen.

5.1 Nuevos protocolos, estructuras y property wrappers:

  • App: Hasta ahora para iniciar nuestra app construida exclusivamente mediante SwiftUI teníamos que usar UIHostingViewController, ahora ya no es necesario gracias al protocolo App y la anotación @main:
@main
struct Mail: App {
    var body: some Scene {
        WindowGroup {
            MailViewer() // Declare a view hierarchy here.
        }
    }
}
  • Scene: El protocolo Scene es el que deben adoptar las vistas que construyan nuestra escena, que son el análogo de UIScene en UIKit, en el ejemplo del protocolo App, WindowGroup es un tipo que adopta este protocolo para vistas normales, también tenemos DocumentGroup para las escenas de apps basadas en documentos o Settings para dirigir a la pantalla de ajustes de nuestra app. También podemos crear nuestras propias implementaciones de este protocolo:
struct MyScene: Scene {
    @Environment(\.scenePhase) private var phase

    var body: some Scene {
        WindowGroup {
            TabView {
                FirstView()
                SecondView()
            }
        }
        .onChange(of: phase) { newPhase in
            switch newPhase {
            case .active:
                // App became active
            case .inactive:
                // App became inactive
            case .background:
                // App is running in the background
            @unknown default:
                // Fallback for future cases
            }
        }
    }
}
  • @SceneStorage: Ahora se pueden almacenar los datos específicos de una escena usando SceneStorage. Lo podemos usar por ejemplo para mantener la pestaña seleccionada de un tabView entre sesiones:
struct ContentView: View {
    
    @SceneStorage("selectedTab") var selectedTab: Tab = .first
    
    var body: some View {
        TabView(selection: $selectedTab) {
            FirstView()
                .tabItem {
                    Image(systemName: "nose.fill")
                }.tag(Tab.first)
            SecondView()
                .tabItem {
                    Image(systemName: "mustache.fill")
                }.tag(Tab.second)
        }
    }
    
    enum Tab: String {
        case first
        case second
    }
}
  • @StateObject: Un tipo de contenedor de propiedades que crea instancias de un objeto observable. Es como un @ObservableObject pero que se mantendrá vivo durante las actualizaciones de la vista que lo contiene:
@StateObject var dataSource = DataSource()
  • @AppStorage: Un property wrapper que encapsula el UserDefaults e invalida la vista que lo usa en función a los cambios de éste:

struct ContentView: View {
    @AppStorage("termsAndConditions") var termsAndConditions: Bool = false

    var body: some View {
        VStack {
            Toggle(isOn: termsAndConditions) {
                self.termsAndConditions.toggle()
            }
        }
    }
}
  • @FocusedBinding: Se trata de un property wrapper que nos soluciona el problema de tener varios publicadores que usan la misma key, éste identificará el más cercano al foco:

@propertyWrapper struct FocusedBinding<Value>
  • @ScaledMetric: Nos sirve para calcular números con la escala proporcionada por el escalado dinámico del sistema que configura el usuario, es decir, en función al zoom que selecciona un usuario en la configuración de su dispositivo el valor de esta variable cambiará, nosotros solo hemos de poner un valor por defecto:

@ScaledMetric var size: CGFloat = 50
  • @UIApplicationDelegateAdaptor: Este property wrapper nos va a facilitar el acceso al AppDelegate de nuestra aplicación en caso de que lo necesitemos, si creamos una app 100% swiftUI hemos de crear previamente la clase:
@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {
    // app delegate methods...
}

 

5.2 Nuevos componentes:

  • Toolbar y ToolbarItem: Hay un nuevo modificador toolbar() en la vista NavigationView que nos sirve para poner una barra de herramientas:
NavigationView {
    Text("Hello world").padding()
        .navigationTitle("Title")
        .toolbar {
            ToolbarItem(placement: .bottomBar) {
                HStack {
                   Button("First") {
                       print("Pressed")
                   }
                   Button("Second") {
                       print("Pressed")
                   }
                }
            }
        }
}
  • TextEditor: Es la nueva vista de SwiftUI análoga a UITextView de UIKit, es decir, un componente para poder escribir en varias lineas de texto. Es altamente configurable como el componente Text:
struct ContentView: View {
    @State private var description: String = ""

    var body: some View {
        TextEditor(text: $description)
    }
}
  • Menu: Se trata de una vista que tendrá la apariencia del menú de la plataforma en la que lo ejecutemos y contiene un modificador para darle distintos estilos:
Menu("Actions") {
    Button("Duplicate", action: duplicate)
    Button("Rename", action: rename)
    Button("Delete…", action: delete)
    Menu("Copy") {
        Button("Copy", action: copy)
        Button("Copy Formatted", action: copyFormatted)
        Button("Copy Library Path", action: copyPath)
    }
}
  • SignInWithAppleButton: Es la vista que se encarga de generar el botón de login con Apple, así como de recibir los eventos en ella:
SignInWithAppleButton(
    .signIn,
    onRequest: { request in
        request.requestedScopes = [.fullName, .email]
    },
    onCompletion: { result in
        switch result {
        case .success (let authResults):
            print("Authorization successful.")
        case .failure (let error):
            print("Authorization failed: " + error.localizedDescription)
        }
    }
)
  • ColorPicker: Es un componente para seleccionar colores, se debe almacenar en una variable @State o similar.
struct ContentView: View {
    @State private var color = Color.white

    var body: some View {
        VStack {
            ColorPicker("Set color", selection: $color)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(color)
    }
}
  • ProgressView: Una vista para mostrar el progreso de un valor sobre un total, es como el Activity Indicator de UIKit pero vitaminado ya que es altamente configurable, podemos mostrar un progreso lineal o circular:
struct ShadowedProgressViews: View {
    var body: some View {
        VStack {
            ProgressView(value: 0.25)
            ProgressView(value: 0.75)
        }
        .progressViewStyle(DarkBlueShadowProgressViewStyle())
        VStack {
            ProgressView ("Text", value: 10, total: 100)
        }
    }
}

struct DarkBlueShadowProgressViewStyle: ProgressViewStyle {
    func makeBody(configuration: Configuration) -> some View {
        ProgressView(configuration)
            .shadow(color: Color(red: 0, green: 0, blue: 0.6),
                    radius: 4.0, x: 1.0, y: 2.0)
    }
}
  • Gauge: Una vista que muestra un valor dentro de un rango:
struct Gauge<Label, CurrentValueLabel, BoundsLabel, MarkedValueLabels> where Label : View, CurrentValueLabel : View, BoundsLabel : View, MarkedValueLabels : View
  • Label: Una vista para mostrar una imagen y una etiqueta:
Label("mustache", systemImage: "mustache.fill")

//

Label {
    Text("Text")
        .foregroundColor(.primary)
        .font(.largeTitle)
        .padding()
        .background(Color.black)
} icon: {
    RoundedRectangle(cornerRadius: 10)
        .fill(Color.red)
        .frame(width: 54, height: 54)
}
  • Link: Una vista con la apariencia de un botón que abre una URL, permite personalización como la fuente o los colores y la posibilidad de crearla manualmente con su propio @ViewBuilder:
Link("adictos", destination: URL(string: "https://www.adictosaltrabajo.com/")!)
  • ScrollViewReader: Una vista que nos permite desplazarnos a una determinada posición de un ScrollView:
struct ContentView: View {

    var body: some View {
        ScrollView {
            ScrollViewReader { value in
                Button("Jump to 4") {
                    value.scrollTo(8)
                }

                ForEach(0..<10) { i in
                    Text("Example \(i)")
                        .id(i)
                }
            }
        }
    }
}
  • LazyViews y GridItem: Es el análogo a los collection views de iOS, ahora disponemos de una forma de cargar perezosamente nuestras listas, tanto verticales como horizontales, la diferencia con las stack views es que éstas cargan todo el contenido antes de presentarse, ahora las gridViews lo cargan bajo demanda en función a los items que se están mostrando.
var rows: [GridItem] =
        Array(repeating: .init(.fixed(20)), count: 2)
ScrollView(.horizontal) {
    LazyHGrid(rows: rows, alignment: .top) {
        ForEach((0...79), id: \.self) {
            let codepoint = $0 + 0x1f600
            let codepointString = String(format: "%02X", codepoint)
            Text("\(codepointString)")
                .font(.footnote)
            let emoji = String(Character(UnicodeScalar(codepoint)!))
            Text("\(emoji)")
                .font(.largeTitle)
        }
    }
}
  • DisclosureGroup: Una vista desplegable que muestra u oculta otra vista de contenido, basada en el estado de un control de tipo Bool.
struct ToggleStates {
    var oneIsOn: Bool = false
    var twoIsOn: Bool = true
}
@State private var toggleStates = ToggleStates()
@State private var topExpanded: Bool = true

var body: some View {
    DisclosureGroup("Items", isExpanded: $topExpanded) {
        Toggle("Toggle 1", isOn: $toggleStates.oneIsOn)
        Toggle("Toggle 2", isOn: $toggleStates.twoIsOn)
        DisclosureGroup("Sub-items") {
            Text("Sub-item 1")
        }
    }
}
struct ContentView: View {
    @State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 40,6146, longitude: -3,7211), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))

    var body: some View {
        Map(coordinateRegion: $region)
    }
}
  • SceneViewSpriteView: Ahora podemos representar escenas de SceneKit o vistas de SpriteKit directamente en SwiftUI:
SpriteView(scene: scene)
    .frame(width: 300, height: 400)
  • VideoPlayer: Vista de reproducción de video de AVKit para SwiftUI, puede visualizar tanto local como remoto:
VideoPlayer(player: AVPlayer(url:  Bundle.main.url(forResource: "video", withExtension: "mp4")!))
//
VideoPlayer(player: AVPlayer(url:  URL(string: "https://adictos.com/video")!))
//
VideoPlayer(player: AVPlayer(url:  URL(string: "https://adictos.com/video")!)) {
    VStack {
        Spacer()
        Text("descripción del video")
            .font(.caption)
            .foregroundColor(.white)
            .background(Color.black)
            .clipShape(Capsule())
    }
}

5.3 Cambios destacados en vistas existentes:

  • List: ahora en iOS 14 se comporta de forma «lazy» al igual que los grids.
  • Text: Nuevos constructores para iniciar la vista con una Imagen o fechas (sin DateFormatter ✌️) y modificadores para cambiar a mayúsculas o minúsculas.
  • Group: Muchos modificadores nuevos, por ejemplo, ahora se le puede asignar un navigationTitle para propósitos de navegación.
  • TabView: Ahora retiene el estado de navegación de una pestaña mientras cambias entre ellas.

6. Novedades en UIKit

En Apple no se han olvidado de la veterana UIKit, y lo han demostrado dándola una serie de mejoras que merecen mucho la pena, veamos algunas de ellas:

  • UIDatePicker: El selector de fecha recibe un lavado de cara con un estilo mucho más cómodo mostrando un calendario completo, además permite muchas formas de personalización:
private let picker = UIDatePicker(frame: .zero)
  • UIColorPickerViewController: En nuevo selector de color como el de SwiftUI, pero éste funciona mediante delegación o mediante publicadores con Combine:
let colorPicker = UIColorPickerViewController()

let cancellable = colorPicker.publisher(for: \.selectedColor)
.sink() { [weak self] color in
    self?.view.backgroundColor = color
}
  • UIAction: Se ha potenciado el uso de esta clase, incluyéndola en la mayoría de constructores de los componentes de UIKit
let dismiss = UIAction(title: "done") { [weak self] action in
    self?.dismiss(animated: true)
}
let navItem = UIBarButtonItem(systemItem: .done, primaryAction: dismiss)
  • UIMenu: Ahora los botones (UIButton, UIBarButton…)  aceptan menús, esto es muy practico por ejemplo para casos como poner el historial de navegación en un botón «atrás»:
let navItem = UIBarButtonItem(systemItem: .done)
let menu = UIMenu(title: "", children: [UIAction(title: "done") { handler in print("done")}])

navItem.menu = menu
  • UIListContentView: Se trata de una vista que está pensada para ser insertada en un contenedor tipo UIStackView o UITableView, seguramente sea la nueva forma en la que Apple nos invita a crear «celdas»:
var config:UIListContentConfiguration = UIListContentConfiguration.subtitleCell()
config.text = "Title"
config.secondaryText = "Subtitle"
        
let list:UIListContentView = UIListContentView(configuration: config)
list.frame = view.bounds
        
let stackView = UIStackView(frame: view.bounds)
stackView.addArrangedSubview(list)
let splitVC = UISplitViewcontroller()

let masterVC = MasterViewController()
let suppVC = SupplementaryViewController()
let detailVC = DetailViewController()

splitVC.setViewController(masterVC, for: .primary)
splitVC.setViewController(suppVC, for: .supplementary)
splitVC.setViewController(detailVC, for: .secondary)
  • UIScribbleInteraction: La novedad más destacada en iPad es la posibilidad de escribir a mano en los campos texto, esta característica la vamos a tener de forma automática, no obstante disponemos de la clase UIScribbleInteraction para poder jugar con la interacción en sí:
let scribble = UIScribbleInteraction(delegate: self)
let text = UITextField(frame: .zero)
text.addInteraction(scribble)

func scribbleInteraction(_ interaction: UIScribbleInteraction, shouldBeginAt location: CGPoint) -> Bool {
    return true
}

 

7. Conclusiones

Una WWDC cargada de contenido muy potente, enfocada al desarrollo (como debe ser), y sin hardware. Con todo lo que han presentado tenemos horas y horas para indagar en la documentación nueva y seguir aprendiendo. En el siguiente artículo veremos el nuevo framework de Widgets, las Apps Clips, las novedades en SFSymbols, y muchos más 😜.

Puedes leer la segunda parte aquí:

WWDC 2020, novedades en el mundo del desarrollo Apple. (Parte 2)

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad