Um tour pelo Swift

A tradição sugere que o primeiro programa em uma nova língua deve imprimir "Olá, mundo!"na tela. Em Swift, isso pode ser feito em uma única linha de código:

Se você já escreveu algum código em C ou Objective-C, essa sintaxe deve parecer familiar para você, em Swift, esta linha de código é um programa completo. Você não precisa importar uma biblioteca separada para funcionalidades como entrada / saída ou gerenciamento de Strings. O código escrito no escopo global é usado como ponto de entrada para o programa, portanto, você não precisa de um método principal. Você também não precisa escrever ponto-e-vírgula (;) no final de cada comando.

Este tour lhe dará informações suficientes para começar a escrever seu código em Swift, mostrando-lhe como realizar uma variedade de tarefas de programação. Não se preocupe se você não entender algo, pois tudo apresentado neste tour será explicado em detalhes no restante deste livro.

Nota

Para a melhor experiência, abra este capítulo como um arquivo do Playground do Xcode, ele permite que você edite as listas de códigos e veja o resultado imediatamente.

Download Playground

Valores simples

Use a palavra chave let para criar constantes e var para criar variáveis. O valor de uma constante não precisa ser fornecido no momento da compilação mas ela so pode receber esse valor uma única vez, o que significa que você pode usar constantes para nomear um valor que você especifica somente uma vez mas usa em vários lugares do seu código.

Uma constante ou variável deve ter o mesmo tipo que o valor que você deseja atribuir a elas, entretanto, você nem sempre precisa escrever o tipo explicitamente pois fornecendo um valor ao criar uma constante ou variável, o compilador pode descobrir seu tipo. No exemplo acima, o compilador descobre que myVariable é um número inteiro porque seu valor inicial é um número inteiro.

Se o valor inicial não fornecer informações suficientes (ou se não houver valor inicial), especifique o tipo, escrevendo-o após a declaração da variável ou constante, separados por dois pontos.

Experimento

Crie uma constante com o tipo explícito Float e atribua a ela o valor 4.

Os valores em Swift nunca são convertidos de forma implícita para outro tipo. Se você precisa converter um valor para um tipo diferente, faça explicitamente uma instância do tipo desejado.

Experimento

Tente remover a conversão para String do final do comando, que erro você recebe?

Existe uma maneira ainda mais simples de inserir valores dentro de Strings: Escreva o valor entre parênteses e escreva uma barra invertida (\) antes dos parênteses, por exemplo:

Experimento

Use “\( )” para incluir um cálculo de um dado com casas decimais em uma String e para incluir o nome de alguém em uma saudação.

Use três aspas duplas(""") para seqüências de Strings que ocupam várias linhas. A indentação no início de cada linha citada é removida, desde que corresponda ao recuo das aspas de fechamento. Por exemplo:

Crie arrays (listas de objetos ou valores) e dictionaries (armazenamento de objetos ou valores com uso de chaves identificadoras) usando colchetes([ ])e acesse seus elementos escrevendo o índice ou chave entre colchetes. Usar vírgula após o último elemento é permitido.

Para criar um objeto do tipo array ou dictionary, use a sintaxe de inicialização:

Se o tipo do dado pode ser descoberto pelo compilador, você pode escrever uma matriz vazia como [ ] e um dicionário vazio como [ : ] - por exemplo, quando você insere um novo valor para uma variável ou passa um argumento para uma função.

Controle de Fluxo

Use os comandos if e switch para fazer fluxos condicionais e use for-in, while e repeat-while para fazer fluxos repetitivos (loops). Os parênteses em torno da variável condição ou loop são opcionais, porém as chaves ao redor do corpo são obrigatórias.

Em um comando if, a comparação deve ser uma expressão booleana, isto é, que retorne um valor verdadeiro ou falso (true ou false). Isso quer dizer que um código utilizando a variável do exemplo acima como if score { ... } dispara um erro de compilação, pois não é uma expressão válida.

Você pode usar if e let juntos para trabalhar com valores que podem não existir (nil). Esses valores são representados como opcionais. Um valor opcional contém um valor ou contém nulo (nil) para indicar que falta um valor. Escreva um ponto de interrogação (?) após o tipo do valor para marcar ele como opcional.

Experimento

Mude o valor da variável optionalName para nil, que valor a variável greeting apresentará? Agora adicione um bloco else que atribui um valor diferente para essa variável caso o valor deoptionalName seja nil.

Se o valor de uma variável opcional for nulo, a comparação condicional resultará em falso e o código dentro das chaves não é executado. Caso contrário, o valor opcional é desembrulhado e atribuído à constante nomeada após o let, o que torna o valor desembrulhado disponível dentro do bloco de código.

Outra maneira de lidar com valores opcionais é fornecer um valor padrão usando o operador ??. Se o valor da variável opcional for nil, o valor padrão será usado.

O comando switch suporta qualquer tipo de dados e uma grande variedade de operações de comparação, eles não estão limitados a números inteiros e testes de igualdade.

Experimento

Tente remover o bloco default, que erro você recebe?

Observe no exemplo acima como uma constante letpode ser usada em um bloco case para receber um valor caso ele corresponda a um padrão (neste caso o padrão é ter o sufixo “pepper” no seu valor).

Depois de executar o código dentro de um bloco case que é correspondido, o programa sai da instrução switch. A execução não continua para o próximo case, portanto, não há necessidade do comando break ser utilizado explicitamente do final de cada bloco case.

Você pode usar o comando for-in para iterar sobre itens de um dictionary fornecendo um par de valores a serem usados ​​para cada par chave-valor. Os dicionários são uma coleção não ordenada, portanto suas chaves e valores são iterados em uma ordem arbitrária.

Experimento

Adicione outra variável para acompanhar qual tipo de número foi o maior (prime, fibonacci ou square).

Use o comando while para repetir um bloco de código até uma condição mudar. A condição de um loop pode ser no final do bloco while, garantindo que o loop seja executado pelo menos uma vez.

Você pode manter dois valores numéricos dentro de um loop usando o operador ..< para criar um range de indexes.

Use o operador ..< para criar um range que não inclua o seu último valor e utilize o operador para criar um range que inclua ambos os valores.

Functions e Closures

Functions (funções) são blocos de código que executam uma função específica retornando valores para quem a chamou ou não (em algumas linguagens funções que não retornam valores são chamadas de procedimentos). No Swift, use o comando func para declarar uma função e para chamá-la utilize seu nome seguido dos nomes dos parâmetros entre parênteses. Use o operador -> para separar os parâmetros do valor de retorno da função quando o mesmo existir.

Experimento

Remova o parâmetro day e adicione um parâmetro para incluir o especial de almoço de hoje na saudação.

Por padrão, as funções usam seus nomes de parâmetros como rótulos externos para seus argumentos, isso quer dizer que quem chamar essa função verá o nome do parâmetro e precisa informá-lo como do exemplo acima. Escreva um rótulo de um argumento personalizado antes do nome do parâmetro, ou use um _ para não usar nenhum rótulo de argumento.

Use uma tupla para criar valores compostos, como por exemplo, para retornar mais de um valor de uma função. Os elementos da tupla podem ser referenciados pelo seu nome ou pelo seu índice na tupla.

Funções podem ser aninhadas (funções dentro de outra função) sendo que essas funções tem acesso as variáveis que foram declaradas na sua função externa. Você pode utilizar esse recurso para deixar sua lógica mais centralizada e evitar funções gigantes.

No Swift, funções são consideradas tipos de dados (assim como Strings, Ints, e suas próprias classes customizadas) isso significa que funções podem retornar outras funções.

E também podem receber outras funções como argumentos.

As funções são, na verdade, um caso especial de closures (blocos de código que podem ser chamados depois de algum processamento). O código em uma closure têm acesso a coisas como variáveis ​​e funções que estavam disponíveis no escopo onde ela foi criado, mesmo que a closure tenha um escopo diferente quando for executada (você já viu um exemplo disso com funções aninhadas). Você pode definir uma closure sem um nome somente envolvendo seu código com chaves {}. Use o comando in para separar os argumentos e o retorno do corpo da closure.

Experimento

Mude a closure para retornar o número 0 para todos os números ímpares.

Existem várias opções para escrever closures de forma mais concisa. Quando o tipo da closure já é conhecido, como um callback de um delegate (esses tópicos serão abordados futuramente no livro), você pode omitir o tipo de seus parâmetros, o tipo de retorno ou ambos. As closures de um comando só retornam o valor do seu processamento em uma única declaração implicitamente sem a necessidade de utilizar o comando return.

Você pode referenciar os parâmetros pelo número de ordem em que aparecem em vez de por nome, essa abordagem é especialmente útil em closures muito curtas. Uma closure passada como último parâmetro para uma função pode aparecer imediatamente após os parênteses. Quando uma closure é o único parâmetro para uma função, você pode omitir os parênteses inteiramente.

Objetos e Classes

Use o comando class seguido pelo nome da classe para criar uma classe. Uma declaração de propriedade em uma classe é escrita da mesma forma que uma declaração de uma constante ou variável comum, exceto que são criados no contexto de uma classe. Do mesmo modo, as declarações de métodos e funções são escritas da mesma maneira.

Experimento

Adicione uma propriedade constante com o comando let e outro método que receba parâmetros.

Crie uma instância de uma classe colocando parênteses após o nome dela. Use a sintaxe de pontos . para acessar as propriedades e os métodos da instância que você criou.

Esta versão da classe Shape está faltando algo importante: um inicializador para inicializar a classe quando uma instância é criada. Use o comando init para criar um.

Observe como o operador self é usado para diferenciar a propriedade que pertence a classe do parâmetro para o inicializador. Os parâmetros para o inicializador são passados ​​como uma chamada de função quando você cria uma instância da classe. Toda propriedade precisa de um valor atribuído, seja em sua declaração (como a variável numberOfSides) ou no inicializador (como com a variável name). Use o comando deinit para criar um desinicialiador se você precisar executar alguma lógica antes que o objeto seja removido da memória.

Subclasses precisam incluir o nome da sua superclasse depois do seu próprio nome separado por dois-pontos :. Não há necessidade que as classes precisem ter uma superclasse em Swift, para que você possa incluir ou omitir uma superclasse conforme necessário.

Métodos de uma subclasse que sobrescrevem a implementação da superclasse são marcados com o modificador override , sobrescrever um método por acidente, sem o modificador override, é detectado pelo compilador como um erro. O compilador também detecta os métodos com override que na verdade não sobrescrevem qualquer método na superclasse.

Experimento

Crie outra subclasse de NamedShapechamadaCircle que recebe o raio e o nome como parâmetros em seu construtor. Implemente os métodos area() e simpleDescription() na classe Circle.

Além das propriedades simples que são armazenadas em classes,elas podem ter um getter e um setter.

Dentro do setter da propriedade perimeter, o novo valor recebido tem o nome implícito de newValue, sendo possível mudar esse nome fornecendo um novo entre parênteses depois do comando set.

Perceba que o inicializador da classe EquilateralTriangle possui três diferentes passos:

1 - Setar o valor das propriedades que a subclasse declara.

2 - Chamar o inicializador da superclasse.

3 - Mudar o valor das propriedades definidas na superclasse. Qualquer mudança adicional no estado ou nas propriedades da classe que usem métodos ou getters e setters também podem ser feitas nesse momento.

Se você não precisa calcular a propriedade, mas ainda precisa fornecer um código que seja executado antes e depois de setar um novo valor, use os operadores willSet e didSet. O código que você fornecer dentro desses blocos é executado sempre que o valor muda fora de um inicializador. Por exemplo, a classe abaixo garante que o comprimento lateral do seu triângulo seja sempre o mesmo que o comprimento lateral do seu quadrado.

Ao trabalhar com valores opcionais, você pode utilizar o operador ? antes de operações como métodos, propriedades e subscripts. Se o valor antes do ? for nulo, tudo depois do ? é ignorado e o valor de toda a expressão é nulo. Caso contrário o valor opcional será desembrulhado e tudo depois do ? atua no valor desembrulhado. Em ambos os casos, o valor de toda a expressão é um valor opcional.

Enumerations e Structures

Em programação, uma enumeração (enum) é um tipo de dado abstrato, cujos valores são atribuídos a exatamente um elemento de um conjunto finito de identificadores escolhidos pelo programador. Esse tipo é geralmente usado para variáveis categóricas. Use o operador enum para criar uma enumeração. Como classes e todos os outros tipos de dados nomeados, as enumerações podem ter métodos associados a eles.

Experimento

Crie uma função que compare dois valores do tipo Rank pelo seus rawValues.

Por padrão, o Swift atribui os rawValues começando em zero e aumentando gradativamente, mas você pode alterar esse comportamento especificando os valores. No exemplo acima, ace é explicitamente dado um valor bruto de 1, e o resto dos valores brutos são atribuídos em ordem (2, 3, 4). Você também pode usar seqüências de caracteres ou números com vírgulas como rawValues de um enum. Use a propriedade rawValue para acessar o valor bruto de cada case de um enum.

Use o inicializador init?(rawValue: ) para criar uma instância de um enum a partir de um rawValue. Ele retorna um case do enum, que corresponde ao rawValue ou nil se não houver um valor correspondente.

Os valores dos cases de um enum são realmente valores e não somente uma outra forma de escrever os seus rawValues. Na verdade, nos cases em que não existe um rawValue significativo, você não precisa fornecer um.

Experimento

Adicione um método color( ) para o enum Suit que retorna "black" para spades e clubs e retorna "red" para hearts e diamonds.

Observe as duas maneiras pelas quais o case hearts do enum é mencionado acima: Ao atribuir um valor à constante hearts, o case do enumSuit.hearts é referenciado pelo seu nome completo porque a constante não possui um tipo explícito especificado. Dentro do bloco switch, o case do enum é referenciado pela forma abreviada .hearts porque o valor de self já é conhecido dentro do próprio enum. Você pode usar a forma abreviada sempre que o tipo do valor já é conhecido.

Se um enum tiver rawValues, esses valores são determinados como parte da declaração e não da instância, o que significa que cada instância de um case do enum sempre possui o mesmo rawValue. Outra escolha para os cases de um enum é ter valores associados ao case, esses valores são determinados quando você cria uma instância, e eles podem ser diferentes para cada instância de um case de um enum. Você pode pensar nos valores associados como propriedades de uma instância de um case específico de um enum. Por exemplo, considere o case de solicitar os horários de nascer e pôr do sol a partir de um servidor, o servidor responde com as informações solicitadas, ou responde com uma descrição do que deu errado.

Experimento

Adicione um terceiro case ao enum e também ao bloco switch.

Observe como os horários do pôr e do nascer do sol são extraídos do valor do enum ServerResponse para dentro de uma constante para ser comparado com os cases de um switch.

Use o comando struct para criar uma estrutura. Em Swift, as estruturas suportam muitos dos mesmos comportamentos que as classes, incluindo métodos e inicializadores mas uma das diferenças mais importantes entre estruturas e classes é que as estruturas são sempre copiadas quando são passadas como parâmetro no seu código, sendo que as classes são passadas por referência.

Experimento

Adicione um método a estrutura Card que cria um baralho cheio de cartas, com uma carta de cada combinação dos enums Rank e Suit.

Protocols e Extensions

Um protocolo define um modelo de métodos, propriedades e outros requisitos que se adequam a uma determinada tarefa ou parte de alguma funcionalidade. mas não se preocupe se não ficou claro, pois explicaremos detalhadamente durante o livro.Use o comando protocol para criar um protocolo.

Classes, enums e estruturas podem implementar quantos protocolos forem necessários:

Experimento

Crie um enum que também implemente este protocolo.

Observe o uso da palavra-chave mutating na declaração da estrutura SimpleStructure para marcar um método que modifica a estrutura, isto é, muda seus atributos internamente. A declaração do SimpleClass não precisa de nenhum dos seus métodos marcados como mutating porque os métodos em uma classe sempre podem modificar as suas propriedades.

Use o comando extension para adicionar funcionalidade a um tipo existente, como novos métodos e computed properties. Você pode usar uma extension para fazer um tipo declarado em outro lugar implementar um protocolo, ou mesmo a um tipo que você importou de uma biblioteca ou framework.

Experimento

Crie uma extension para o tipo Double e adicione uma propriedade absoluteValue.

Você pode usar um nome de um protocolo como qualquer outro tipo de nome, por exemplo, para criar uma coleção de objetos que tenham diferentes tipos, mas que todos implementem um único protocolo. Quando você trabalha com valores cujo tipo é um tipo de protocolo, os métodos fora da definição do protocolo não estão disponíveis.

Mesmo que a variável protocolValue seja do tipo SimpleClass no momento da execução, o compilador irá tratá-lo como o tipo dado de ExampleProtocol. Isso significa que você não pode acessar métodos ou propriedades acidentalmente que a classe implementa além dos disponíveis no protocolo.

Tratamento de Erros

Você pode representar erros usando qualquer tipo de dado que implemente o protocolo Error.

Use o comando throw para lançar um erro e throws para indicar que uma função pode lançar um erro. Se você lançar um erro em uma função, a função para sua execução imediatamente, ou seja, os comandos que seriam executados após o erro não são, e o código que chamou a função lida com o erro.

Existem várias maneiras de lidar com erros. Uma maneira é usar os comandos do-catch. Dentro do bloco do, você marca o código que pode lançar um erro escrevendo o comando try na frente dele. Dentro do bloco catch, o erro recebe automaticamente o nome error, a menos que você dê um nome diferente.

Experimento

Mude o parâmetro toPrinter para a String “Never Has Toner” para que a função send() lance um erro.

Você pode fornecer múltiplos blocos do tipo catch para tratar erros específicos e para isso você pode escrever o tipo do erro que o bloco trata logo após o comando catch(assim como é feito nos blocos cases de um switch).

Experimento

Adicione um código que lance um erro dentro do bloco do. Que tipo de erro você precisa lançar para ser tratado pelo primeiro bloco catch? E para o segundo e terceiro bloco?

Outra maneira de lidar com erros é usar o comando try? para converter o resultado em um opcional. Se a função lançar um erro, o erro específico é descartado e o resultado é nulo, caso contrário, o resultado é um opcional contendo o valor que a função retornou.

Use o comando defer para escrever um bloco de código que é executado após todos os outros códigos na função, exatamente antes do retorno da função. O código é executado independentemente se função lançar um erro. Você pode usar o defer para escrever o código de setup e cleanup da função um ao lado do outro, mesmo que eles precisem ser executados em momentos diferentes.

Generics

Escreva o nome dentro dos sinais de <> (maior e menor) para criar uma função ou um tipo genérico.

Você pode criar formas genéricas de funções e métodos, bem como classes, enums e estruturas.

Use o comando where antes do corpo para especificar uma lista de requisitos, por exemplo, exigir que o tipo implemente um protocolo, exigir que dois tipos sejam iguais ou exigir que uma classe tenha uma superclasse particular.


Experimento

Modifique a função anyCommonElements (_: _ :) para criar uma função que retorna um array dos elementos que as duas seqüências têm em comum.

Escrever <T: Equatable> é o mesmo que escrever <T> … where T: Equatable.

results matching ""

    No results matching ""