Como usar namespaces no Swift?

144

A documentação menciona apenas tipos aninhados, mas não está claro se eles podem ser usados ​​como espaços para nome. Não encontrei nenhuma menção explícita a namespaces.

lassej
fonte
Uma rápida pesquisa com Ctrl-F no iBook deles não mostra instâncias de namespaces ... então vou com não?
Justin Niessner
1
Não sei por que essa pergunta está encerrada. Vi namespace no keynote no lado esquerdo do ícone da Swift, e eu ainda não consigo encontrar qualquer menção a partir da documentação ...
eonil
Não consegui encontrar nenhuma informação sobre isso, o Google me levou a esta pergunta :). Talvez uma das sessões da WWDC possa lançar um pouco mais de luz sobre isso.
Zyphrax
Também estou esperando alguém na WWDC apresentar uma boa explicação.
eonil
A resposta de Eonil está correta. Você usa módulos no Xcode para separar suas classes.
Zyphrax

Respostas:

113

Respondida por SevenTenEleven no fórum de desenvolvedores da Apple :

Os espaços para nome não são por arquivo; eles são por destino (com base na configuração de criação "Nome do módulo do produto"). Então você terminaria com algo assim:

import FrameworkA
import FrameworkB

FrameworkA.foo()

Todas as declarações do Swift são consideradas parte de algum módulo; portanto, mesmo quando você diz " NSLog" (sim, ele ainda existe), você está obtendo o que o Swift considera " Foundation.NSLog".

Também Chris Lattner twittou sobre namespacing .

O espaço para nome está implícito no Swift, todas as classes (etc) têm o escopo implicitamente definido pelo módulo (destino do Xcode) em que estão. Nenhum prefixo de classe é necessário

Parece ser muito diferente do que venho pensando.

eonil
fonte
6
Fóruns do Apple Dev ... Eu já vi tantas ervas daninhas que você não acreditaria!
Nicolas Miari 06/04
1
O link para os fóruns de desenvolvedores da Apple agora está quebrado e forums.developer.apple.com, infelizmente , a Apple não importou esse segmento para o novo site de fóruns.
Dai
2
@Dai Parece que é por isso que devemos evitar os fóruns da Apple para perguntas e respostas ... Mas os membros principais da equipe de desenvolvimento parecem não se importar muito com SO. Que tragédia.
eonil
1
o que você quer dizer com tumbleweed?
21818 Alexander Mills
148

Eu descreveria o namespace de Swift como aspiracional; recebeu muita publicidade que não corresponde a nenhuma realidade significativa no terreno.

Por exemplo, os vídeos da WWDC afirmam que, se uma estrutura que você está importando tem uma classe MyClass e seu código tem uma classe MyClass, esses nomes não entram em conflito porque "mangling de nome" fornece nomes internos diferentes. Na realidade, no entanto, eles fazem conflito, no sentido de que vitórias MyClass seu próprio código, e você não pode especificar "Não, não, eu quero dizer a MyClass no quadro" - dizendo TheFramework.MyClassnão funciona (o compilador sabe o que quer dizer , mas diz que não consegue encontrar essa classe na estrutura).

Minha experiência é que Swift, portanto, não é um espaço de nome nem um pouco. Ao transformar um dos meus aplicativos do Objective-C para o Swift, criei uma estrutura incorporada porque era muito fácil e legal de fazer. A importação da estrutura, no entanto, importa todo o material Swift na estrutura - então, presto, mais uma vez, existe apenas um espaço para nome e é global. E não há cabeçalhos Swift, portanto você não pode ocultar nenhum nome.

EDIT: Na semente 3, esse recurso está começando a ficar online, no seguinte sentido: se seu código principal contiver MyClass e sua estrutura MyFramework contiver MyClass, a primeira ofusca a última por padrão, mas você pode alcançá-la na estrutura usando a sintaxe MyFramework.MyClass. Assim, temos de fato os rudimentos de um espaço de nome distinto!

EDIT 2: Na semente 4, agora temos controles de acesso! Além disso, em um dos meus aplicativos eu tenho uma estrutura incorporada e, com certeza, tudo estava oculto por padrão e eu tive que expor explicitamente todos os bits da API pública. Esta é uma grande melhoria.

mate
fonte
4
Obrigado por esta resposta. Funciona não apenas com Frameworks, mas também com a Biblioteca Padrão. Você pode "substituir" a matriz, por exemplo. Em seguida, "Matriz" se refere à sua própria classe Array personalizada, e a Matriz da Biblioteca Padrão está disponível como "Swift.Array".
George
3
@ George E da mesma forma para o NSArray; se você o ofuscar, ainda poderá se referir a ele como Foundation.NSArray.
Matt
1
Portanto, sem ter que examinar a história do pensamento nos espaços para nome nos betas: onde está essa resposta agora?
Dan Rosenstark 26/01
1
@Yar como indicado nas edições. O nome do módulo é um espaço de nomes opcional se houver ambiguidade e agora existe privacidade, portanto os nomes dos módulos ficam ocultos, a menos que expostos e um nome possa ser confinado a um arquivo.
Matt
2
Isso está me incomodando há muito tempo, parece que qualquer projeto Swift não deve usar um prefixo por causa do que a Apple está reivindicando; no entanto, neste momento, ainda é necessário um prefixo, mesmo com os modificadores de acesso. Embora você não entre em conflito com o pacote ou as classes privadas na estrutura da Apple, qualquer coisa declarada pública, por exemplo, String, se você declarar isso novamente, ou qualquer nova classe, ela acabará usando a sua, a menos que você tenha o hábito de referindo-se a todas as classes com seu espaço para nome .... não é bom.
Oscar Gomez
19

Ao fazer algumas experiências com isso, acabei criando essas classes "namespaced" em seus próprios arquivos, estendendo o "pacote" raiz. Não tenho certeza se isso é contra as melhores práticas ou se tem alguma implicação de que estou ciente (())

AppDelegate.swift

var n1 = PackageOne.Class(name: "Package 1 class")
var n2 = PackageTwo.Class(name: "Package 2 class")

println("Name 1: \(n1.name)")
println("Name 2: \(n2.name)")

PackageOne.swift

import Foundation

struct PackageOne {
}

PackageTwo.swift

import Foundation

struct PackageTwo {
}

PackageOneClass.swift

extension PackageOne {
    class Class {
        var name: String
        init(name:String) {
            self.name = name
        }
    }
}

PackageTwoClass.swift

extension PackageTwo {
    class Class {
        var name: String
        init(name:String) {
            self.name = name
        }
    }
}

Editar:

Acabei de descobrir que a criação de "subpacotes" no código acima não funcionará se você estiver usando arquivos separados. Talvez alguém possa sugerir por que esse seria o caso?

Adicionando os seguintes arquivos ao acima:

PackageOneSubPackage.swift

import Foundation

extension PackageOne {
    struct SubPackage {
    }
}

PackageOneSubPackageClass.swift

extension PackageOne.SubPackage {
    class Class {
        var name: String
        init(name:String) {
            self.name = name
        }
    }
}

Está lançando um erro de compilador: 'SubPackage' não é um tipo de membro de 'PackageOne'

Se eu mover o código de PackageOneSubPackageClass.swift para PackageOneSubPackage.swift, ele funcionará. Qualquer um?

Edição 2:

Brincando com isso ainda e descobri (no Xcode 6.1 beta 2) que, ao definir os pacotes em um arquivo, eles podem ser estendidos em arquivos separados:

public struct Package {
  public struct SubPackage {
    public struct SubPackageOne {
    }
    public struct SubPackageTwo {
    }
  }
}

Aqui estão meus arquivos em um sumário: https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8

bWlrYWphdWhvbmVu
fonte
interessante, como foi seu teste?
user2727195
2
Não sei o que você quer dizer com "teste", mas fui adiante e comecei a criar meu aplicativo usando a técnica acima e parece funcionar até agora com a ressalva que adicionei à minha entrada acima. Estou fazendo isso principalmente porque estou acostumado a organizar meu código dessa maneira em outros idiomas e ficaria grato se alguém com mais conhecimento pudesse me dizer que é uma má idéia até que eu vá longe demais! :)
bWlrYWphdWhvbmVu
1
continue ... é isso que queremos ... ser capaz de ter a mesma classe de nome em dois pacotes diferentes para poder existir e referenciar adequadamente (arquivos mais importantes) e, se não funcionar, o conjunto idéia de namespace é uma idéia fracasso ...
user2727195
Algum efeito colateral usando "struct" como uma maneira de invadir namespaces?
Alex Nolasco
Não que eu tenha encontrado no que diz respeito ao desempenho. O Xcode (6.1 GM) está em alguns casos raros reclamando de tipos que não existem, mas acredito que esse pode ser o caso quando não estiver estruturando um código como esse também. Recentemente, resolvi um problema adicionando ao meu alvo de teste todos os arquivos que não fazem sentido, mas resolveram o problema. :)
bWlrYWphdWhvbmVu
12

Acredito que isso seja alcançado usando:

struct Foo
{
    class Bar
    {
    }
}

Em seguida, ele pode ser acessado usando:

var dds = Foo.Bar();
Kevin Sylvestre
fonte
1
Ainda não estou muito feliz com a maneira como os namespaces são feitos ... e se eu tiver dez classes diferentes em um namespace e, além disso, eu preferir manter as classes em seus arquivos individuais, não quero inchar um arquivo / estrutura com todas as classes, sugestões.
precisa saber é o seguinte
2
Eu me pergunto por que eles não pensaram em incluir pacotes, é disso que precisamos, não espaços para nome, quero dizer, olhe para outras linguagens de alto nível como Java, C #, ActionScript, todos eles têm pacotes, espaços para nome nesse contexto não são nada diferentes de usar o NS ou outros prefixos para suas classes de projeto
user2727195 04/04
1
Não posso deixar de pensar se o uso de estruturas como uma maneira de invadir espaços para nome pode causar problemas desconhecidos.
Alex Nolasco
1
Essa é uma boa solução para isso. Tentei usar essa abordagem, mas tive que parar imediatamente. Quando tentei nomear minhas classes relacionadas à exibição de namespace (digamos um CustomTableViewCell), o preenchimento automático no construtor de interface não o sugeriu. Eu precisaria copiar e colar manualmente o nome da classe para a exibição, se eu tivesse usado essa abordagem.
ArG
1
Geralmente você usaria um enum, não um struct, portanto não poderá instanciar um Foo.
Kevin
7

O Swift usa módulos como o python (veja aqui e aqui ) e, como @Kevin Sylvestre sugeriu, você também pode usar os tipos aninhados como namespaces.

E para estender a resposta de Daniel A. White, na WWDC, eles estavam falando sobre os módulos rapidamente.

Também aqui é explicado:

Tipos inferidos tornam o código mais limpo e menos propenso a erros, enquanto os módulos eliminam cabeçalhos e fornecem espaços para nome.

Ivan Genchev
fonte
2
Estou procurando por pacotes como construct, conforme mencionado no seu segundo link 6.4 Packages (Python), namespaces como tipos aninhados não podem levar muito longe, e se eu tiver 10 classes diferentes e em arquivos diferentes em um namespace ou, digamos, um pacote???
precisa saber é o seguinte
7
  • Os espaços para nome são úteis quando você precisa definir a classe com o mesmo nome da classe na estrutura existente.

  • Suponha que seu aplicativo tenha MyAppnome e você precise declarar seu costume UICollectionViewController.

Você não precisa prefixar e subclassar como este:

class MAUICollectionViewController: UICollectionViewController {}

Faça isso deste modo:

class UICollectionViewController {} //no error "invalid redeclaration o..."

Por quê? . Porque o que você declarou está declarado no módulo atual , que é seu destino atual . E UICollectionViewControllerde UIKité declarado no UIKitmódulo.

Como usá-lo no módulo atual?

var customController = UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit

Como distingui-los de outro módulo?

var customController = MyApp.UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit
Bartłomiej Semańczyk
fonte
3

Você pode usar extensiona structabordagem mencionada para espaçamento de nomes sem precisar recuar todo o seu código para a direita. Venho brincando um pouco com isso e não tenho certeza se iria criar espaços para nome Controllerse Viewsnomes, como no exemplo abaixo, mas ilustra o quão longe ele pode ir:

Profiles.swift :

// Define the namespaces
struct Profiles {
  struct Views {}
  struct ViewControllers {}
}

Perfis / ViewControllers / Edit.swift

// Define your new class within its namespace
extension Profiles.ViewControllers {
  class Edit: UIViewController {}
}

// Extend your new class to avoid the extra whitespace on the left
extension Profiles.ViewControllers.Edit {
  override func viewDidLoad() {
    // Do some stuff
  }
}

Perfis / Visualizações / Edit.swift

extension Profiles.Views {
  class Edit: UIView {}
}

extension Profiles.Views.Edit {
  override func drawRect(rect: CGRect) {
    // Do some stuff
  }
}

Não usei isso em um aplicativo, pois ainda não precisava desse nível de separação, mas acho que é uma ideia interessante. Isso elimina a necessidade de sufixos uniformes de classe, como o onipresente * ViewController, que é irritantemente longo.

No entanto, ele não reduz nada quando é referenciado, como em parâmetros de método como este:

class MyClass {
  func doSomethingWith(viewController: Profiles.ViewControllers.Edit) {
    // secret sauce
  }
}
Sebastien Martin
fonte
2

Caso alguém tenha curiosidade, em 10 de junho de 2014, este é um bug conhecido no Swift:

From SevenTenEleven

"Erro conhecido, desculpe! Rdar: // problem / 17127940 A qualificação de tipos Swift pelo nome do módulo não funciona."

Adam Venturella
fonte
de acordo com a publicação de @ matt abaixo, este é um bug conhecido no Swift no momento.
Adam Venturella
Isto é corrigido agora em Beta 3 (Lançamento 7 de julho de 2014)
Adam Venturella