Como emular um iOS no Linux utilizando Docker
Tags: linux, ios, virtualization, xcode
Published: 2021-12-28 21:47:12

**_English version:_** [How to emulate iOS on Linux with Docker](https://dev.to/ianito/how-to-emulate-ios-on-linux-with-docker-4gj3)

Depois de várias tentativas sem sucesso, enfim consegui virtualizar um macOS para fazer testes em um aplicativo iOS no qual eu estava trabalhando.

Mas antes de continuar, é necessário saber que essa não é uma solução estável e possue diversos problemas de desempenho, porém, para meu propósito consegui fazer o que queria.

Nós utilizaremos o QEMU para emular um macOS e dentro dele, utilizaremos o xCode para emular um iOS, só nisso você já consegue perceber que não vai ser uma coisa leve.

O repositório no github do [Docker OSX](https://github.com/sickcodes/Docker-OSX) tem uma explicação de como usar um iPhone via usb ao invés de emular, mas eu não tenho iPhone :p

## Sumário

- [O que é o Docker OSX](#o-que-%C3%A9-o-docker-osx)
- [Especificações de hardware](#minhas-especifica%C3%A7%C3%B5es-de-hardware)
- [Instalação](#instala%C3%A7%C3%A3o)
- [Rodando um app com React Native](#rodando-um-app-com-react-native)
- [Rodando um app com Cordova](#rodando-um-app-com-cordova)
- [Criando uma conexão de pastas utilizando o sshfs](#criando-uma-conex%C3%A3o-de-pastas-utilizando-o-sshfs)
- [Considerações finais](#considera%C3%A7%C3%B5es-finais)

## O que é o Docker OSX

O Docker OSX é uma docker image que utiliza por debaixo dos panos o QEMU para que possamos emular um sistema operacional.

_Leia mais: [O que é docker?](https://stack.desenvolvedor.expert/appendix/docker/oquee.html)_

## Minhas especificações de hardware

As especificações do meu computador são consideradas OK pra fazer isso, porém, ainda consegui perceber algumas engasgadas enquanto utilizava o Docker OSX + xCode + Visual Studio Code + Dev Server. (Consegui até aquecer meu quarto com esse tanto de coisa.)

- **OS:** Manjaro Linux x86_64 
- **Kernel:** 4.19.220-1-MANJARO 
- **Shell:** zsh 5.8 
- **Resolution:** 1440x900
- **DE:** GNOME 41.2 
- **WM:** Mutter 
- **WM Theme:** Orchis-orange-compact 
- **Icons:** Win11-purple-dark [GTK2/3] 
- **Terminal:** gnome-terminal 
- **CPU:** Intel i7-3770 (8) @ 3.900GHz 
- **GPU:** NVIDIA GeForce GTX 1050 Ti 
- **Memory:** 4105MiB / 15985MiB 
- **SSD:** Crucial BX500 240gb (Altamente recomendado um SSD)

## Instalação
Primeiramente, é necessário ter o docker instalado no seu computador. No meu caso eu utilizo Manjaro, então basta abrir o terminal e digitar:

**Instalação docker**
`pacman -S docker `

**Ativando os serviços do docker**
`systemctl start docker.service`

**Ativando os serviços do docker para iniciar junto com o sistema**
`systemctl enable docker.service`

**Testando o docker:**
`docker run hello-world`

![Docker run hello-world](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xgj3c3zue8x13y0tps7c.png)

Certo, agora iremos baixar a imagem e executa-la utilizando o comando abaixo:
`docker run -it --device /dev/kvm -p 50922:10022 -e DEVICE_MODEL="iMacPro1,1" -e WIDTH=1440 -e HEIGHT=900 -e RAM=8 -e INTERNAL_SSH_PORT=23     -e AUDIO_DRIVER=alsa -e CORES=2 -v /tmp/.X11-unix:/tmp/.X11-unix -e "DISPLAY=${DISPLAY:-:0.0}"  -e GENERATE_UNIQUE=true  -e MASTER_PLIST_URL=https://raw.githubusercontent.com/sickcodes/osx-serial-generator/master/config-custom.plist sickcodes/docker-osx:big-sur`

Você pode conferir o que cada flag significa olhando o repositório no [github do docker osx](https://github.com/sickcodes/Docker-OSX), mas resumidamente, especifiquei a resolução, memória ram, cores do processador, versão big-sur do macOS e etc.

Em seguida, ele irá baixar a imagem e executar.

Quando abrir o emulador, de enter na opção `macOS Base System`

![Emulador Docker OSX](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fubit7hhx6x50d4rynwe.png)

Quando carregar o sistema, clicaremos em `Disk Utility`
![Emulador Docker OSX](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f5ksa4x7piwfsj4qe291.png)
 
Agora, procuraremos a partição que está com mais espaço de armazenamento e clicaremos na opção `Erase`
![Docker OSX apagando sistema](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/irj97wizelw0hbfoamql.png)

Para formatar é preciso que as opções estejam estritamentes iguais a esta:
![Formatação opções](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/drf8gki6wway416rb85h.png)

Clique em `Erase`, aguarde e pode fechar a janela do Disk Utility. 

Em seguida, iremos em `Reinstall macOS Big Sur` aceitamos os termos, selecionamos a partição que acabamos de criar `macOS` e nisso, ele começará a instação do sistema. (Esse processo geralmente leva 30min ~ 1h). 
![Docker OSX Instalação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rd67yhek2mjer3bkm1h5.png)

Aguardado esse tempo, o sistema deve reiniciar (ou não), no meu caso, eu tive que fazer isso de forma manual pois ele não reiniciou. Nesse caso, feche a janela do QEMU.

![Qemu error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/44eandfh00uh6swzrc0k.png)

Novamente no terminal, digitaremos:

`docker ps -a`

Para saber qual o ID do nosso container, e em seguida iremos inicia-lo com:

`docker start ID`

![Docker IDS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t0ythg12xpdnf5x9zhls.png)

Selecione o `macOS Installer` e deixe que a instalação continue.
![Docker OSX Instalação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xq39lesxjerdba85w41k.png)
 
Após o processo, ele irá reiniciar automaticamente (ou não), sendo assim, feche novamente o emulador e dê start no container mais uma vez.

![Docker OSX Instalação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xe5lk5aq4xzw55j0afwj.png)

Ao iniciar, novamente selecione a opção `macOS Installer` e aguarde o processo terminar, feito isso ele irá reiniciar. (Agora é verdade).

Nosso macOS foi instalado, com isso, vamos selecionar a opção `macOS`. 
![Docker OSX Inicialização](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wkzdtw9jozj91gi66v2f.png)
 
Feito isso, irá reiniciar novamente e você selecione a mesma opção `macOS`.

Show, nossa tela de bem vindo apareceu. Essa parte é bastante lenta, mas após isso irá ficar normal.

![Tela de bem vindo macOS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/up2vajvs9p2l8g1vq39f.png)

Configure a sua maneira, mas não faça login no AppleID agora.

Após esse processo, nossa área de trabalho aparecerá e então iremos aguardar até a dock aparecer, pois após isso o sistema fica mais fluído.
  
![Tela de instalação mac OS Sem dock](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydprxzwqrnyp88tht3hf.png)

![Tela de instalação mac OS Com dock](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xoucf5ea5kl7r8cd2zr4.png)

Agora iremos utilizar o gerenciador `brew` para instalar as coisas mais rapidamente.

Abra o terminal no macOS e instale o brew com o comando:
`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`

Irá pedir sua senha definida anteriormente para instalar.

Agora iremos instalar o `xcode` na apple store.

![Apple store xcode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zrchopyo4u207fyou05a.png)
 
Agora sim, podemos fazer o login da nossa conta AppleID. 

![Login apple store](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elma6mfjd6us7pst5y0l.png)

Feito isso, aguarde a instalação. 
Em seguida, abra o xcode e aceite os termos e aguarde instalar as dependências.

Em seguida, vá em `Preferences -> Locations -> Command-Line Tools` vai estar vazio, selecione a opção com a versão do xcode.

Quando a instalação terminar, abriremos o terminal novamente e instalaremos o cocoapods. Ele serve como um gerenciador de dependências do xCode.

`brew install cocoapods`

Feito isso, nosso macOS está instalado e configurado para rodar os projetos.


## Rodando um app com React Native
Vamos rodar um hello world do React Native para ver se está tudo certo, lembrando que não irei testar o android somente o iOS.

Abra o terminal

**Instalação no node:**
`brew install node`

**Instalação do yarn (opcional):**
`npm install -g yarn`

**Criando um projeto react native:**
`npx react-native init teste`

Se pedir pra instalar o cocoapods novamente, selecione a opção com brew.

**Entrando no diretório do react native:**
`cd teste`

**Entrando no diretório do ios:**
`cd ios`

**Instalando as dependências:**
`pod install`

**Voltando para o diretório raiz:**
`cd ..`

**Listando os simuladores disponíveis:** (Opcional)
`xcrun simctl list devices`

**Rodando o projeto utilizando xcode:**
`npx react-native run-ios --simulator="iPhone 13"`
![App rodando react native](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jq34wwlfr17dubmhcy4s.png)

Para uma experiência melhor, veja a seção: [Criando uma conexão de pastas utilizando o sshfs](#criando-uma-conex%C3%A3o-de-pastas-utilizando-o-sshfs)
 
## Rodando um app com Cordova
Vamos rodar um hello world do Quasar para ver se está tudo certo, lembrando que não irei testar o android somente o iOS.

Lembrando que o Quasar usa o Cordova/Capacitor pro iOS e Android.

**Instalação no node:**
`brew install node`

**Instalação do yarn (opcional):**
`npm install -g yarn`

**Instalação do quasar:**
`yarn global add @quasar/cli`

**Instalação do cordova:**
`yarn global add cordova`

**Criando um projeto com Quasar-CLI:**
`quasar create teste`

**Entrando no diretório do projeto:**
`cd teste`

**Adicionando cordova ao projeto:**
`quasar mode add cordova`

**Entrar no diretório do cordova:**
`cd src-cordova`

**Adicionar o iOS ao projeto:**
`cordova platform add ios`

**Verificar se está tudo certo:**
`cordova requirements`

**Listar emuladores disponíveis:** (Opcional)
`cordova emulate iOS --list`

**Instalar as dependências do projeto:**
`yarn`

**Voltar ao diretório raiz:**
`cd ..`

**Instalar as dependências do projeto:**
`yarn`

**Rodar o quasar no modo desenvolvimento ioS:**
`quasar dev -m iOS -e "iPhone 8, 15.2"`
![macOS quasar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zjoyvlx1pl9u41n9jy15.png)
 
Para uma experiência melhor, veja a seção: [Criando uma conexão de pastas utilizando o sshfs](#criando-uma-conex%C3%A3o-de-pastas-utilizando-o-sshfs)

## Criando uma conexão de pastas utilizando o sshfs
Agora que fizemos tudo e nosso app já está rodando no macOS, temos um problema: Abrir nosso editor de código ou IDE dentro do macOS é uma experiência muito ruim por causa da lentidão, glitches, mapping do teclado e etc. Dessa forma, eu pesquisei uma solução para criar uma conexão de arquivos utilizando o SSH.

Ou seja, eu posso abrir o servidor de desenvolvimento dentro do macOS e criar uma conexão em que eu possa alterar os arquivos direto do meu linux ou do macOS, de forma que atualize em ambos os lados, como uma via dupla. Isso nos garante tirar o proveito de algumas coisas que existem no modo desenvolvimento, como o fast refresh.

### Conexão do Linux para o Mac

Primeiramente, precisamos permitir a conexão via ssh por login no mac. Para isso, abriremos o terminal e digitaremos:

**Comando para abrir editar o arquivo de configuração do ssh:**
`sudo nano /etc/ssh/sshd_config`

Busque por `PasswordAuthentication` e coloque a configuração como `yes` e remova o `#` no começo da linha.

![Configuração sshd](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3aqms7coe0l4xwdga0j1.png)

Salve o arquivo.
Vá para em `System Preferences -> Sharing -> Remote Login` e ative para todos os usuários:

![macOS configuração](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/77o3ld5524ufbtvue507.png)

**Comando para reiniciar o ssh:**
`sudo launchctl stop com.openssh.sshd && sudo launchctl start com.openssh.sshd`

Agora, no terminal do nosso Linux:

**Instalação do sshfs:**
`sudo pacman -S sshfs`

**Pegando o IP do nosso container**
`docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ID_CONTAINER`

**Criar uma pasta:**
`mkdir projeto`


**Comando para abrir a conexão:**
`sudo sshfs USER_MAC@IP_CONTAINER:/CAMINHO/DO/PROJETO/NO/MAC /CAMINHO/NO/LINUX -p 23`

**Exemplo:**

![Exemplo de conexão](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gwcd70m354t5hnviy9h9.png)

Pronto, agora eu posso abrir o visual studio code no meu Linux e atualizar diretamente no Mac.

Se você tiver um outro computador, pode fazer essa conexão também e deixar o seu principal apenas pra emular.

### Conexão do Mac para o Linux

Mesmo processo anteriormente, só que o pacote do ssfs no mac pode ser instalado pelo comando abaixo.

**Instalação sshfs no mac**
`brew install --cask macfuse && brew install gromgit/fuse/sshfs-mac`

No Linux:

**Comando para abrir editar o arquivo de configuração do ssh:**
`sudo nano /etc/ssh/sshd_config`

Busque por `PasswordAuthentication` e coloque a configuração como `yes` e remova o `#` no começo da linha.

![Configuração sshd](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3aqms7coe0l4xwdga0j1.png)

Salve o arquivo.

**Comando para reiniciar o SSH no Manjaro:**
`sudo systemctl restart sshd.service`

De volta ao mac, criaremos uma pasta e abriremos a conexão.

**Criar uma pasta:**
`mkdir projeto`

**Comando para abrir a conexão:**
`sudo sshfs USER_LINUX@IP_HOST:/CAMINHO/LINUX /CAMINHO/MAC -p 23`

Após rodar o comando, irá acontecer um erro:

![Erro MAC](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fgq3r1245m3s94qe5yql.png)
 
Abra as prefêrencias e clique em `Allow`

![MacOS Preferencias](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/smhcxmmfcvvxdmrf8ssg.png)

Reincie o mac.

**Agora podemos abrir nossa conexão:** (Meu SSH está com uma porta diferente, mas a padrão é 22)

![Conexão SSH OK](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wr871w7pezvkz80t1k53.png)

Feito isso, podemos atualizar de qualquer lado que também irá atualizar.
 

## Considerações finais

Muito obrigado por ler este tutorial, que aliás, é o primeiro que publico depois de anos. Qualquer dúvida ou sugestão é sempre bem vinda.

Ah, nunca atualize o mac.

:)