AVISO
Estou disponível para oportunidades como Desenvolvedor Full-stack ou Desenvolvedor Back-end. Caso conheça algum posição em aberto, sinta-se à vontade para entrar em contato comigo no LinkedIn Iaan Mesquita :)
English version: How to emulate iOS on Linux with Docker
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 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
- Especificações de hardware
- Instalação
- Rodando um app com React Native
- Rodando um app com Cordova
- Criando uma conexão de pastas utilizando o sshfs
- Considerações 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?
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
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, 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
Quando carregar o sistema, clicaremos em Disk Utility
Agora, procuraremos a partição que está com mais espaço de armazenamento e clicaremos na opção Erase
Para formatar é preciso que as opções estejam estritamentes iguais a esta:
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).
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.
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
Selecione o macOS Installer
e deixe que a instalação continue.
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.
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
.
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.
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.
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.
Agora sim, podemos fazer o login da nossa conta AppleID.
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"
Para uma experiência melhor, veja a seção: Criando uma conexão 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"
Para uma experiência melhor, veja a seção: Criando uma conexão 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.
Salve o arquivo.
Vá para em System Preferences -> Sharing -> Remote Login
e ative para todos os usuários:
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:
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.
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:
Abra as prefêrencias e clique em Allow
Reincie o mac.
Agora podemos abrir nossa conexão: (Meu SSH está com uma porta diferente, mas a padrão é 22)
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.
:)
Top comments (12)
Excelente post camarada... único porém é que minha memória de vídeo ficou bem lenta. Com 7mb apenas.
Olá, muito obrigado mano.
Então, suas config são boas? Pq não sei como funciona no Mac, se ele pega a memória ram q passamos e dedica uma parte ao vídeo. Mesmo eu usando 8gb dava uns lags bem pequenos.
A minha máquina é razoávelmente boa cara... Achei muito lento dar boot tanto quando carrega o install quanto quando carrega o processo de boot do macos ...
AMD Ryzen 9 5900X
48GB ram 3600mhz
gtx 1050 ti
ASUS TUF Gaming X570-PLUS
Corsair mp600 pro gen4 pcie x4 nvme m.2 ssd 1tb
um detalhe é que eu tentei fazer via Windows 11 (com e sem wsl2), quando estiver com paciência booto no Ubuntu e tento novamente.
Estranho mesmo, aqui comigo deu 41 segundos desde a inicialização do container até a tela de loading da apple e até a tela de login foi +4 segundos. Depois tenta lá e da o feedback pra gente. Se tua máquina é razoavelmente boa, tadinha da minha...
Acabei de bootar no Ubunto e testar... diferença gritante, muiiiiito rápido, e estou em um SSD sata 3 de 450GB
Show!!!
o problema maior é a resolução ... e também tentei com o Moterey, ai sim fica muito ruim.
Post muito bom, man
Obrigado!!
Quando desejo iniciar novamente meu macOs qual comando seria?
Oi, você vai listar os container novamente:
docker ps -a
É vai iniciá-lo com docker start ID
Feito isso, ele vai abrir o emulador e basta você selecionar o macOS novamente.
:D