Usando o Swift se permitir com o operador lógico AND &&

93

Sabemos que podemos usar uma if letinstrução como um atalho para verificar se há um nil opcional e depois desembrulhar.

No entanto, quero combinar isso com outra expressão usando o operador lógico AND &&.

Então, por exemplo, aqui eu faço o encadeamento opcional para desempacotar e opcionalmente fazer o downcast do meu rootViewController para tabBarController. Mas, em vez de aninhar as instruções if, gostaria de combiná-las.

if let tabBarController = window!.rootViewController as? UITabBarController {
    if tabBarController.viewControllers.count > 0 {
        println("do stuff")
     }
 }

Doação combinada:

if let tabBarController = window!.rootViewController as? UITabBarController &&
    tabBarController.viewControllers.count > 0 {
        println("do stuff")
     }
}

O acima dá o erro de compilação Uso do identificador não resolvido 'tabBarController'

Simplificando:

if let tabBarController = window!.rootViewController as? UITabBarController && true {
   println("do stuff")
}

Isso dá um erro de compilação. O valor de limite em uma associação condicional deve ser do tipo opcional . Tendo tentado várias variações sintáticas, cada uma fornece um erro de compilador diferente. Ainda estou para encontrar a combinação vencedora de ordem e parênteses.

Portanto, a questão é: é possível e, em caso afirmativo, qual é a sintaxe correta?

Observe que desejo fazer isso com uma ifdeclaração, não com uma switchdeclaração ou com um ?operador ternário .

Max MacLeod
fonte
É sempre uma boa ideia fornecer as mensagens de erro na pergunta.
zaph
Declaração mais simples para pessoas que gostam de fazer experiências com isso:var foo : Int? = 10; if let bar = foo { if bar == 10 { println("Great success!") }}
DarkDust
A mensagem de erro ao usar if let bar = foo && bar == 10é Use of unresolved identifier "bar"(no segundo bar, é claro).
DarkDust
apenas uma nota que fui capaz de encurtar o exemplo acima usando firstObject do Objective C da seguinte maneira: if let navigationController = tabBarController.viewControllers.bridgeToObjectiveC (). firstObject as? UINavigationController {
Max MacLeod
1
1. bridgeToObjectiveCdesapareceu no beta 5 (e muito possivelmente nunca foi planejado para uso geral). 2. De qualquer maneira, há um firstmétodo no Arraytipo integrado do Swift .
rickster

Respostas:

143

A partir do Swift 1.2, isso agora é possível . As notas de lançamento do Swift 1.2 e Xcode 6.3 beta afirmam:

Desdobramento opcional mais poderoso com if let - A construção if let agora pode desembrulhar vários opcionais de uma vez, bem como incluir condições booleanas intermediárias. Isso permite expressar o fluxo de controle condicional sem aninhamento desnecessário.

Com a declaração acima, a sintaxe seria:

if let tabBarController = window!.rootViewController as? UITabBarController where tabBarController.viewControllers.count > 0 {
        println("do stuff")
}

Isso usa a wherecláusula.

Outro exemplo, desta vez lançando AnyObjectpara Int, desembrulhando o opcional e verificando se o opcional desembrulhado atende à condição:

if let w = width as? Int where w < 500
{
    println("success!")
}

Para aqueles que agora usam o Swift 3, "onde" foi substituído por uma vírgula. O equivalente seria, portanto:

if let w = width as? Int, w < 500
{
    println("success!")
}
Max MacLeod
fonte
2
Esta deve ser a resposta escolhida.
Francis.Beauchamp
sim, pois agora mudou. A resposta de Bryan estava correta naquele momento
Max MacLeod
58

No exemplo do Swift 3 Max MacLeod seria assim:

if let tabBarController = window!.rootViewController as? UITabBarController, tabBarController.viewControllers.count > 0 {
    println("do stuff")
}

O wherefoi substituído por,

ph1lb4
fonte
10
Então, é para && para que serve || ?
jeet.chanchawat
9
@ jeet.chanchawat OU lógico não faz sentido neste contexto. if letsó pode prosseguir se o opcional desembrulhar com sucesso e for atribuído ao novo nome de variável. Se você dissesse OR <something else>, poderia facilmente ignorar todo o propósito do if let. Para OR lógico, basta fazer um normal ife dentro do corpo lidar com o fato de que o primeiro objeto ainda é opcional naquele ponto (não desembrulhado).
jhabbott
37

A resposta de Max está correta e é uma maneira de fazer isso. Observe, porém, que quando escrito desta forma:

if let a = someOptional where someBool { }

A someOptionalexpressão será resolvida primeiro. Se falhar, a someBoolexpressão não será avaliada (avaliação de curto-circuito, como você esperaria).

Se você quiser escrever ao contrário, pode ser feito assim:

if someBool, let a = someOptional { }

Nesse caso, someBoolé avaliada primeiro, e somente se for avaliada como verdadeira a someOptionalexpressão é avaliada.

par
fonte
5

Swift 4 , vou usar,

let i = navigationController?.viewControllers.index(of: self)
if let index = i, index > 0, let parent = navigationController?.viewControllers[index-1] {
    // access parent
}
Sazzad Hissain Khan
fonte
4

Não é possível.

Da gramática Swift

GRAMÁTICA DE UMA DECLARAÇÃO DE IF

if-instrução → if -condição bloco de código else-clauseopt

condição-se → expressão | declaração

cláusula elseelse bloco de código | else if-statement

O valor de qualquer condição em uma instrução if deve ter um tipo que esteja em conformidade com o protocolo BooleanType. A condição também pode ser uma declaração de vinculação opcional, conforme discutido em Vinculação opcional

if-condição deve ser expressão ou declaração. Você não pode ter expressão e declaração.

let foo = baré uma declaração, não avalia para um valor em conformidade com BooleanType. Ele declara uma constante / variável foo.

Sua solução original é boa o suficiente, é muito mais legível do que combinar as condições.

Bryan Chen
fonte
mas certamente "if let" - que é uma sintaxe válida do Swift - é uma expressão e uma declaração?
Max MacLeod
@MaxMacLeod let foo = baré declaração, não expressão. você não pode fazerlet boolValue = (let foo = bar)
Bryan Chen
ok, mas para citar o guia Swift, página 11, o exemplo é: if let name = optionalName. optionalName deve ser uma expressão em que se optionalName não for opcional, ele será avaliado como verdadeiro. A segunda parte da instrução, então, entra em ação, em que o opcional desembrulhado é atribuído ao nome. Portanto, a questão é, se "optionalName não é um opcional" é uma expressão, ela pode ser expandida com um AND lógico. A propósito, acho que você provavelmente está correto em que isso não pode ser feito.
Max MacLeod
-1

Acho que sua proposta original não é tão ruim. Uma alternativa (mais confusa) seria:

if ((window!.rootViewController as? UITabBarController)?.viewControllers.count ?? 0) > 0 {
    println("do stuff")
}
jtbandes
fonte
1
mas você precisa codificar para lançar tabBarControllernovamente
Bryan Chen