Guia do desenvolvedor iOS: do Objective-C ao aprendizado Swift

Publicados: 2022-03-11

Em 2008, a Apple anunciou e lançou o iPhone SDK 2.0. Este evento iniciou outra revolução no desenvolvimento de software, e uma nova geração de desenvolvedores nasceu. Eles agora são reconhecidos como desenvolvedores iOS.

Muitos desses desenvolvedores nunca haviam usado Objective-C antes, e esse foi o primeiro desafio que a Apple lançou a eles. Apesar da sintaxe desconhecida e do gerenciamento manual de memória, foi imensamente bem-sucedido, ajudando a preencher a App Store com dezenas de milhares de aplicativos. A Apple melhorou continuamente o Objective-C a cada versão, adicionando blocos e literais, gerenciamento de memória simplificado com contagem automática de referência e muitos outros recursos indicativos de uma linguagem de programação moderna.

E depois de 6 anos melhorando e trabalhando no Objective-C, a Apple decidiu lançar outro desafio aos desenvolvedores. Mais uma vez, os desenvolvedores iOS precisarão aprender uma nova linguagem de programação: Swift . O Swift remove o gerenciamento de ponteiro inseguro e apresenta novos recursos poderosos, mantendo a interação com Objective-C e C.

O Swift 1.0 já é uma plataforma de desenvolvimento estável e forte, que certamente evoluirá de maneiras interessantes nos próximos anos. É um momento perfeito para começar a explorar esta nova linguagem, pois é obviamente o futuro do desenvolvimento iOS.

O objetivo deste tutorial é fornecer aos desenvolvedores do Objective-C uma visão geral rápida dos novos recursos da linguagem Swift, ajudando você a dar o próximo passo e começar a adotar o Swift em seu trabalho diário. Não vou gastar muito tempo explicando Objective-C, e vou assumir que você está familiarizado com o desenvolvimento iOS.

Swift mudou o jogo para desenvolvedores iOS de Objective-C.

Experimentando Swift vs. Objective-C

Para começar a explorar o Swift, tudo o que você precisa fazer é baixar o XCode da App Store e criar um playground para experimentar. Todos os exemplos mencionados neste artigo são feitos desta forma.

A página inicial do Swift da Apple é a melhor referência para aprender programação Swift. Você achará isso inestimável e, até que esteja totalmente atualizado com o desenvolvimento do Swift, acredito que você voltará aqui com frequência.

Variáveis ​​e Constantes

Declarar uma variável no Swift é feito usando a palavra-chave var .

 var x = 1 var s = "Hello"

Você notará que duas variáveis x e s são de tipos diferentes. x é um Integer, enquanto s é uma String. Swift é uma linguagem de tipo seguro e deduzirá tipos de variáveis ​​do valor atribuído. Se você deseja tornar seu código mais legível, você pode anotar opcionalmente o tipo da variável:

 var y: Int y = 2

Constantes são semelhantes, mas você as declara usando let em vez de var . O valor de uma constante não precisa ser conhecido em tempo de compilação, mas você deve atribuir um valor a ela exatamente uma vez.

 let c1 = 1 // Constant known at compile time var v = arc4random() let c2 = v // Constant known only at run time

Como o nome sugere, eles são imutáveis, portanto, o código a seguir causará um erro em tempo de compilação.

 let c = 1 c = 3 // error

Outros tipos também podem ser declarados como constantes. Por exemplo, o código a seguir declara um array como uma constante e, se você tentar modificar qualquer um dos elementos, o compilador Swift reportará um erro:

 var arr2 = [4, 5, 6] arr2[0] = 8 print (arr2) // [8, 5, 6] let arr = [1, 2, 3] a[0] = 5 // error

Opcionais

As constantes precisam ser inicializadas ao declará-las e as variáveis ​​precisam ser inicializadas antes do uso. Então, onde está o equivalente nil do Objective-C? Swift introduz valores opcionais . Os valores opcionais podem ter um valor ou ser nil . Se você der uma olhada no código a seguir, notará que x recebeu um valor Optional de 2014 . Isso significa que o compilador Swift estava ciente de que x também pode ser nil .

 var s = "2014" var x = s.toInt() print(x) // Optional(2014)

Se você fizer uma alteração neste código e atribuir o valor "abc" a s , que não pode ser convertido em um Integer, você notará que x agora é nil .

 var s = "abc" var x = s.toInt() print(x) // nil

O tipo de retorno da função toInt() é Int? , que é um Int opcional . Vamos tentar chamar uma função padrão em x :

 var x = "2014".toInt() print(x.successor()) // error

O compilador sinaliza um erro, pois x é opcional e pode ser nil . Temos que testar x primeiro e garantir que a função successor seja invocada em um número real, e não em um valor nil :

 var x = "2014".toInt() if x != nil { print(x!.successor()) // 2015 }

Observe que temos que desembrulhar x acrescentando um ponto de exclamação (!) . Quando temos certeza de que x contém um valor, podemos acessá-lo. Caso contrário, obteremos um erro de tempo de execução. Também podemos fazer o que Swift chama de ligação opcional , convertendo o opcional em uma variável não opcional

 let x = "123".toInt() if let y = x { print(y) }

O código na instrução if só será executado se x tiver um valor e o atribuirá a y . Observe que não precisamos desembrulhar y , seu tipo não é opcional, pois sabemos que x não é nil .

Verifique o tutorial Swift da Apple para ler mais detalhes sobre opcionais e recursos interessantes, como encadeamento opcional

Interpolação de String

Em Objective-C, a formatação de strings geralmente é feita com o método stringWithFormat: ::

 NSString *user = @"Gabriel"; int days = 3; NSString *s = [NSString stringWithFormat:@"posted by %@ (%d days ago)", user, days];

O Swift possui um recurso chamado interpolação de strings para fazer o mesmo, mas é mais compacto e fácil de ler:

 let user = "Gabriel" let days = 3 let s = "posted by \(user) \(days) ago"

Você também pode usar expressões:

 let width = 2 let height = 3 let s = "Area for square with sides \(width) and \(height) is \(width*height)"

Para saber mais sobre a interpolação de strings do Swift e outros novos recursos, acesse aqui.

Funções

A definição de função em Swift é diferente de C. Uma definição de função de exemplo está abaixo:

 func someFunction(s:String, i: Int) -> Bool { ... // code }

As funções Swift são tipos de primeira classe . Isso significa que você pode atribuir funções a variáveis, passá-las como parâmetros para outras funções ou fazê-las retornar tipos:

 func stringLength(s:String) -> Int { return countElements(s) } func stringValue(s:String) -> Int { if let x = s.toInt() { return x } return 0 } func doSomething(f:String -> Int, s:String) -> Int { return f(s).successor() } let f1 = stringLength let f2 = stringValue doSomething(f1, "123") // 4 doSomething(f2, "123") // 124

Novamente, Swift infere os tipos de f1 e f2 ( String -> Int ), embora pudéssemos defini-los explicitamente:

 let f1:String -> Int = stringLength

As funções também podem retornar outras funções:

 func compareGreaterThan(a: Int, b: Int) -> Bool { return a > b } func compareLessThan(a: Int, b: Int) -> Bool { return a < b } func comparator(greaterThan:Bool) -> (Int, Int) -> Bool { if greaterThan { return compareGreaterThan } else { return compareLessThan } } let f = comparator(true) println(f(5, 9))

Um guia para funções no Swift pode ser encontrado aqui.

Enumerações

Enumerações em Swift são muito mais poderosas do que em Objective-C. Como estruturas Swift, elas podem ter métodos e são passadas por valor:

 enum MobileDevice : String { case iPhone = "iPhone", Andro, WP8 = "Windows Phone8", BB = "BlackBerry" func name() -> String { return self.toRaw() } } let m = MobileDevice.Android print(m.name()) // "Android"

Ao contrário do Objective-C, as enumerações do Swift podem atribuir Strings, caracteres ou floats como valores para cada membro, além de inteiros. O conveniente método toRaw() retorna o valor atribuído a cada membro.

As enumerações também podem ser parametrizadas:

 enum Location { case Address(street:String, city:String) case LatLon(lat:Float, lon:Float) func description() -> String { switch self { case let .Address(street, city): return street + ", " + city case let .LatLon(lat, lon): return "(\(lat), \(lon))" } } } let loc1 = Location.Address(street: "2070 Fell St", city: "San Francisco") let loc2 = Location.LatLon(lat: 23.117, lon: 45.899) print(loc1.description()) // "2070 Fell St, San Francisco" print(loc2.description()) // "(23.117, 45.988)"

Mais informações sobre enumerações estão disponíveis aqui.

Tuplas

As tuplas agrupam vários valores em um único valor composto. Os valores dentro de uma tupla podem ser de qualquer tipo e não precisam ser do mesmo tipo uns dos outros.

 let person = ("Gabriel", "Kirkpatrick") print(person.0) // Gabriel

Você também pode nomear os elementos de tupla individuais:

 let person = (first: "Gabriel", last: "Kirkpatrick") print(person.first)

Tuplas são extremamente convenientes como tipos de retorno para funções que precisam retornar mais de um valor:

 func intDivision(a: Int, b: Int) -> (quotient: Int, remainder: Int) { return (a/b, a%b) } print(intDivision(11, 3)) // (3, 2) let result = intDivision(15, 4) print(result.remainder) // 3

Ao contrário do Objective-C, o Swift suporta correspondência de padrões em uma instrução switch:

 let complex = (2.0, 1.1) // real and imaginary parts switch complex { case (0, 0): println("Number is zero") case (_, 0): println("Number is real") default: println("Number is imaginary") }

No segundo caso, não nos importamos com a parte real do número, então usamos um _ para corresponder a qualquer coisa. Você também pode verificar condições adicionais em cada caso. Para isso, precisamos vincular os valores do padrão às constantes:

 let complex = (2.0, 1.1) switch complex { case (0, 0): println("Number is zero") case (let a, 0) where a > 0: println("Number is real and positive") case (let a, 0) where a < 0: println("Number is real and negative") case (0, let b) where b != 0: println("Number has only imaginary part") case let (a, b): println("Number is imaginary with distance \(a*a + b*b)") }

Observe como precisamos vincular apenas os valores que usaremos na comparação ou na instrução case.

Para ler mais sobre tuplas, clique aqui.

Classes e Estruturas

Ao contrário do Objective-C, o Swift não exige que você crie interfaces e arquivos de implementação separados para classes e estruturas personalizadas. À medida que você aprende Swift, você aprenderá a definir uma classe ou estrutura em um único arquivo, e a interface externa para essa classe ou estrutura é automaticamente disponibilizada para outro código usar.

Definindo classes

As definições de classe são muito simples:

 class Bottle { var volume: Int = 1000 func description() -> String { return "This bottle has \(volume) ml" } } let b = Bottle() print(b.description())

Como você pode ver, declaração e implementação estão no mesmo arquivo . Swift não usa mais arquivos de cabeçalho e implementação. Vamos adicionar um rótulo ao nosso exemplo:

 class Bottle { var volume: Int = 1000 var label:String func description() -> String { return "This bottle of \(label) has \(volume) ml" } }

O compilador reclamará porque label é uma variável não opcional e não manterá um valor quando um Bottle for instanciado. Precisamos adicionar um inicializador:

 class Bottle { var volume: Int = 1000 var label:String init(label:String) { self.label = label } func description() -> String { return "This bottle of \(label) has \(volume) ml" } }

Ou podemos usar o tipo Optional para uma propriedade, que não deve ser inicializada. No exemplo a seguir, transformamos o volume em um Optional Integer :

 class Bottle { var volume: Int? var label:String init(label:String) { self.label = label } func description() -> String { if self.volume != nil { return "This bottle of \(label) has \(volume!) ml" } else { return "A bootle of \(label)" } } }

Estruturas

A linguagem Swift também possui structs , mas elas são muito mais flexíveis do que em Objective-C. O tutorial de código a seguir define um struct :

 struct Seat { var row: Int var letter:String init (row: Int, letter:String) { self.row = row self.letter = letter } func description() -> String { return "\(row)-\(letter)" } }

Assim como as classes em Swift, as estruturas podem ter métodos, propriedades, inicializadores e estar em conformidade com protocolos. A principal diferença entre classes e estruturas é que classes são passadas por referência, enquanto structs são passadas por valor .

Este exemplo demonstra a passagem de classes por referência:

 let b = Bottle() print(b.description()) // "b" bottle has 1000 ml var b2 = b b.volume = 750 print(b2.description()) // "b" and "b2" bottles have 750 ml

Se tentarmos um caso semelhante com struct , você notará que as variáveis ​​são passadas por valor:

 var s1 = Seat(row: 14, letter:"A") var s2 = s1 s1.letter = "B" print(s1.description()) // 14-B print(s2.description()) // 14-A

Quando devemos usar struct e quando devemos usar class ? Como em Objective-C e C, use structs quando precisar agrupar alguns valores e esperar que eles sejam copiados em vez de referenciados. Por exemplo, números complexos, pontos 2D ou 3D ou cores RGB.

Uma instância de uma classe é tradicionalmente conhecida como um objeto. No entanto, as classes e estruturas do Swift são muito mais próximas em funcionalidade do que em outras linguagens, e muitas funcionalidades podem ser aplicadas a instâncias de uma classe ou de um tipo de estrutura. Por causa disso, o termo mais geral usado na referência do Swift é instance , que se aplica a qualquer um desses dois.

Aprenda o básico das classes e estruturas do Swift aqui.

Propriedades

Como vimos anteriormente, as propriedades em Swift são declaradas com a palavra-chave var dentro de uma classe ou definição de struct. Também podemos declarar constantes com uma instrução let .

 struct FixedPointNumber { var digits: Int let decimals: Int } var n = FixedPointNumber(digits: 12345, decimals: 2) n.digits = 4567 // ok n.decimals = 3 // error, decimals is a constant

Lembre-se também de que as propriedades de classe são fortemente referenciadas, a menos que você as prefixe com a palavra-chave weak . No entanto, existem algumas sutilezas com propriedades não opcionais fracas, então leia o capítulo de contagem automática de referências no guia Swift da Apple.

Propriedades computadas

Propriedades computadas na verdade não armazenam um valor. Em vez disso, eles fornecem um getter e um setter opcional para recuperar e definir outras propriedades e valores indiretamente.

O código a seguir fornece uma amostra de um sign de valor calculado:

 enum Sign { case Positive case Negative } struct SomeNumber { var number:Int var sign:Sign { get { if number < 0 { return Sign.Negative } else { return Sign.Positive } } set (newSign) { if (newSign == Sign.Negative) { self.number = -abs(self.number) } else { self.number = abs(self.number) } } } }

Também podemos definir propriedades somente leitura apenas implementando um getter:

 struct SomeNumber { var number:Int var isEven:Bool { get { return number % 2 == 0 } } }

Em Objective-C, as propriedades geralmente são apoiadas por uma variável de instância, declarada explicitamente ou criada automaticamente pelo compilador. Em Swift, por outro lado, uma propriedade não possui uma variável de instância correspondente . Ou seja, o armazenamento de apoio de uma propriedade não pode ser acessado diretamente. Suponha que temos isso em Objective-C

 // .h @interface OnlyInitialString : NSObject @property(strong) NSString *string; @end // .m @implementation OnlyInitialString - (void)setString:(NSString *newString) { if (newString.length > 0) { _string = [newString substringToIndex:1]; } else { _string = @""; } } @end

Como, em Swift, as propriedades computadas não possuem um backing store, precisamos fazer algo assim:

 class OnlyInitialString { var initial:String = "" var string:String { set (newString) { if countElements(newString) > 0 { self.initial = newString.substringToIndex(advance(newString.startIndex, 1)) } else { self.initial = "" } } get { return self.initial } } }

As propriedades são explicadas com mais detalhes aqui

Continua

Há muitas coisas novas e importantes para aprender no Swift, como Generics, interação com bibliotecas Objective-C, closures, encadeamento opcional e sobrecarga de operadores. Um único tutorial não pode descrever completamente uma nova linguagem, mas não tenho dúvidas de que muito mais será escrito sobre o aprendizado de programação em Swift. No entanto, acredito que esta leitura rápida ajudará muitos desenvolvedores de Objective-C, que não conseguiram encontrar tempo e aprender detalhes da linguagem Swift, a entrar no caminho certo e deixar o pássaro Swift levá-los a novas alturas.