Personal portfolio - Mauricio Aznar

Javascript electron

 

Introduction

Native node modules

Recommended to use electron-build (e.g. bycrypt)

Native node modules


Debugging

Debugging electron link

mainWindow.webContents.openDevTools()


App object

app

Prevent app from quitting

app.on('before-quit', (e) => {
  e.preventDefault()
})
JavaScript

Browser window

browser window

setup

use loadFile instead of loadURL

const { app, BrowserWindow } = require('content/snippets/javascript-electron')

let mainWindow

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { ndoeIntegration: true }
  })

  mainWindow.loadFile('index.html')
}

app.on('ready', createWindow)
JavaScript

Load content

window.loadURL('https://myseite.com/index.html')
window.loadFile('index.html')
window.loadURL('file://Users/me/app/index.html')
JavaScript

Get user data (information of the user logged in)

app.getPath('userData')


Showing window gracefully

let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { ndoeIntegration: true },
    show: false,
  })

  mainWindow.once('ready-to-show', mainWindow.show) 
}
JavaScript

Or...

let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { ndoeIntegration: true },
    backgroundColor: '#2b2b3b'
  })
}
JavaScript

parent & child

parent & child

let mainW

function createWindow () {
  mainW = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { ndoeIntegration: true }
  })

  secondaryW = new BrowserWindow({
    width: 600, height: 300,
    webPreferences: { ndoeIntegration: true },
    parent: mainW,
    modal: true,
    show: false,
  })

  mainW.loadFile('index.html')
  secondaryW.loadFile('secondary.html')


  setTimeout(() => {
    secondaryW.show()
    setTimeout(() => {
      secondaryW.close()
      secondaryW = null
    }, 3000)
  }, 2000)

  mainW.on('closed', () => {
    mainW = null
  }) 
}
JavaScript

frameless window

frameless

let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { ndoeIntegration: true },
    frame: false
  })

  mainWindow.loadFile('index.html') 
}
JavaScript

browser window properties and methods (options)

browser windows optios

State management on windows

electron-window-state package

npm install --save-dev electron-window-state
Shell

const windowStateKeeper = require('electron-window-state')

let mainWindow

function createWindow () {
  let winState = windowStateKeeper({
    defaultWidth: 1000,
    defaultHeight: 800
  })
  
  mainWindow = new BrowserWindow({
    width: winState.width,
    height: winState.height,
    x: winState.x,
    y: winState.y,
    webPreferences: { ndoeIntegration: true },
  })

  mainWindow.loadFile('index.html')
  
  winState.manage(mainWindow)
}
JavaScript

Web contents (broweser window)

Web contents

Instance vs static

const { app, BrowserWindow, webContents } = require('content/snippets/javascript-electron')

let mainWindow

function createWindow() {

  mainWindow = new BrowserWindow({
    width: 1000,
    height: 1000,
    x: 100,
    y: 100,
    webPreferences: { ndoeIntegration: true },
  })

  mainWindow.loadFile('index.html')

  // mainWindow.webContents.openDevTools()

  // web content instance
  let wc = mainWindow.webContents

  wc.on('did-finish-load', () => {
    console.log('All content (images or others content included)')
  })

  wc.on('dom-ready', () => {
    console.log('Dom loaded (all tags)')
  })

  // static method (from all web content instatnces)
  webContents.getAllWebContents()

  mainWindow.on('closed', () => {
    mainWindow = null
  })
}
JavaScript

Prevent window creation

wc.on('new-window', (e, url) => {
  e.preventDefault()
  console.log(`Preventing new window for: ${url}`)
})
JavaScript

Before event

wc.on('before-input-event', (e, input) => {
  e.preventDefault()
  console.log(`Preventing new window for: ${input}`)
})
JavaScript

Login

wc.on('login', (e, request, authInfo, callback) => {
  console.log('Logging in: ')
  callback('user', 'password')
})

wc.on('did-navigate', (e, url, statusCode, message) => {
  console.log(`Navigated to: ${url}`)
  console.log(statusCode)
})
JavaScript

Did navigate

wc.on('did-navigate', (e, url, statusCode, message) => {
  console.log(`Navigated to: ${url}`)
  console.log(statusCode)
})
JavaScript

Context menu

wc.on('context-menu', (e, params) => {
  console.log(`Context menu opened on: ${params.mediaType} at x:${params.x}, y:${params.y}`)
})

wc.on('context-menu', (e, params) => {
  console.log(`User selected text: ${params.selectionText}`)
  console.log(`Selection can be copied: ${params.editFlags.canCopy}`)
})

JavaScript

Execute javascript

wc.executeJavascript()


Session

Session

  • Session share sessions
let ses = mainWindow.webContents.session
JavaScript

Default session is also used

let defaultsSes = session.defaultSession
JavaScript

Custom session

const { session } = require('content/snippets/javascript-electron')

let customSession = session.fromPartition('part1')

let mainWindow
let secondWindow
let thirdWindow

const mainWindow = new BrowserWindow({
  width: 1000,
  height: 1000,
  x: 100,
  y: 100,
  webPreferences: {
    ndoeIntegration: true,
    session: customSession
  },
})


let persistedCustomSession = session.fromPartition('persist:part2')

const secondWindow = new BrowserWindow({
  width: 1000,
  height: 1000,
  x: 100,
  y: 100,
  webPreferences: {
    ndoeIntegration: true,
    session: persistedCustomSession
  },
})


// asiggned inside of browser window

const thirdWindow = new BrowserWindow({
  width: 1000,
  height: 1000,
  x: 100,
  y: 100,
  webPreferences: {
    ndoeIntegration: true,
    partition: 'persist:part3'
  },
})

JavaScript

clear session

clear on instance

ses.clearStorageData()


Cookies

Cookies

const { session } = require('content/snippets/javascript-electron')

let mainWindow

let customSession = session.fromPartition('part1')

function createWindow() {

  let ses = session.defaultSession

  // read cookies
  ses.cookies.get({})
    .then(cookies => {

    })
    .catch(e => {
    })

  let cookie = {
    url: "https://myappdomain.com",
    name: 'cookie1',
    value: 'electron',
    expirationDate: 1622818789
  }

  // set cookie
  ses.cookies.set(cookie)
    .then(() => {
      console.log('Cookie 1 set')
    })

  mainWindow = new BrowserWindow({
    width: 1000,
    height: 1000,
    x: 100,
    y: 100,
    webPreferences: { ndoeIntegration: true },
  })

  // get by name
  set.cookies.get({
    name: 'cookie1'
  })

  // remove cookie (url and name of cookie)
  ses.cookies.remove("https://myappdomain.com", 'cookie1')
    .then(() => {
    })


}


JavaScript

Download item

Download item

Use html attribute property download on a a tag

const { session } = require('content/snippets/javascript-electron')

let mainWindow

let customSession = session.fromPartition('part1')

function createWindow() {

  let ses = session.defaultSession

  // read cookies
  ses.cookies.get({})
    .then(cookies => {

    })
    .catch(e => {
    })

  mainWindow = new BrowserWindow({
    width: 1000,
    height: 1000,
    x: 100,
    y: 100,
    webPreferences: { ndoeIntegration: true },
  })

  // download item without any prompt (small file)
  ses.on(`will-download`, (e, downloadItem, webContents) => {
    console.log('starting download')
    let filename = downloadItem.getFilename()
    let filesize = downloadItem.getTotalBytes()

    // save to desktop
    downloadItem.setSavePath(app.getPath('desktop') + `/${filename}`)

    downloadItem.on('updated', (e, state) => {

      let received = downloadItem.getReceivedBytes()

      if (state === 'progressing' && received) {
        let progress = Math.Round((received / filesize) * 100)
        webContents.executeJavascript(`window.progress.value = ${progress}`)
      }

    })
  })


}


JavaScript

Dialog

Dialog

const { Dialog } = require('content/snippets/javascript-electron')

let mainWindow


function createWindow() {

  mainWindow = new BrowserWindow({
    width: 1000,
    height: 1000,
    x: 100,
    y: 100,
    webPreferences: { ndoeIntegration: true },
  })

  mainWindow.loadFile('index.html')


  // file explorer dialog

  mainWindow.webContents.on('did-finish-load', () => {

    Dialog.showOpenDialog(mainWindow, {
      buttonLabel: 'Select a photo',
      defaultPath: app.getPath('home'),
      properties: [
        'multiSelections',
        'createDirectory',
        'openFile',
        'openDirectory'
      ]
    })
      .then(result => {

      })


    Dialog.showSaveDialog({})
      .then(result => {

      })

    Dialog.showMessageBox({
      title: 'title',
      message: 'Please select an option',
      detail: 'Message details',
      buttons: ['yes', 'no', 'maybe']
    })
      .then(result => {
        console.log('button selected index ' + result.response)
      })
  })
}


JavaScript

accelerators & global shortcuts

const { globalShortcut } = require('content/snippets/javascript-electron')

globalShortcut.register('G', () => {
  console.log('user pressed g')
})

globalShortcut.register('CommandOrControl+G', () => {
  console.log('user pressed g with a combination key')
})

// once
globalShortcut.register('CommandOrControl+G', () => {
  console.log('user pressed g with a combination key')
  globalShortcut.unregister('CommandOrControl+G')
})
JavaScript

  • menu is like a system bar
  • could be exported to a separate file
  • roles (already integrated menu actions)
  • shortcuts are only triggered when app is in focus
const { app, BrowserWindow, Menu, MenuItem } = require('content/snippets/javascript-electron')

let mainWindow

let mainMenu = new Menu()

// in mac the first menu item is the name of the app
let menuItem1 = new MenuItem({
  label: 'Electron',
  submenu: [
    {
      label: 'Item 1',
      click: () => {
        console.log('item clicked')
      },
      accelerator: 'Shift + Alt + g'
    },
    {
      label: 'Item 2',
      submenu: {
        label: "Sub item 1"
      }
    },
    {
      role: 'toggleFullScreen'
    },
    {
      role: 'undo'
    },
    {
      role: 'redo'
    },
    {
      role: 'copy'
    },
    {
      role: 'paste'
    },
    {
      label: 'Item 3',
      enabled: false
    }
  ]
})

mainMenu.append(menuItem1)


let mainMenu2 = Menu.buildFromTemplate([
  {
    label: 'Electron',
    submenu: [
      { label: 'Item 1' },
      {
        label: 'Item 2',
        submenu: {
          label: "Sub item 1"
        }
      },
      { label: 'Item 3' }
    ]
  },
  {
    label: 'Action 2'
  },
  {
    label: 'Action 3'
  }
])


function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { ndoeIntegration: true }
  })

  mainWindow.loadFile('index.html')

  Menu.setApplicationMenu(mainMenu)
}

app.on('ready', createWindow)

JavaScript

Context menu

const { app, BrowserWindow, Menu, MenuItem } = require('content/snippets/javascript-electron')

let mainWindow

let contextMenu = new Menu.buildFromTemplate([
  {
    label: 'item 1'
  },
  {
    label: 'item 2'
  }
])

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { ndoeIntegration: true }
  })

  mainWindow.loadFile('index.html')

  mainWindow.webContents.on('context-menu', () => {
    contextMenu.popup()
  })
}

app.on('ready', createWindow)

JavaScript

Tray

Tray Native images

const { app, BrowserWindow, Menu, MenuItem, Tray } = require('content/snippets/javascript-electron')

let tray
let mainWindow


let trayMenu = Meny.buildFromTemplate([
  { label: 'Item 1' },
  { role: 'quit' }
])

function createTray() {
  tray = new Tray('trayTemplate@2x.png')
  tray.setTooltip('Tray details')

  tray.on('click', (e) => {

    if (e.shiftKey) {
      app.quit()
    } else {
      mainWindow.isVisible ? mainWindow.hide() : mainWindow.show()
    }
  })

  tray.setContextMeny(trayMenu)
}

let contextMenu = new Menu.buildFromTemplate([
  {
    label: 'item 1'
  },
  {
    label: 'item 2'
  }
])

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { ndoeIntegration: true }
  })

  mainWindow.loadFile('index.html')

  mainWindow.webContents.on('context-menu', () => {
    contextMenu.popup()
  })
}

app.on('ready', createWindow)

JavaScript

Power Monitor

Power monitor

const electron = require('content/snippets/javascript-electron')
const { app, BrowserWindow, Menu, MenuItem, Tray } = electron

let tray
let mainWindow

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000, height: 800,
    webPreferences: { ndoeIntegration: true }
  })

  mainWindow.loadFile('index.html')


  electron.powerMonitor.on('resume', (e) => {
    if (!mainWindow) {
      createWindow()
    }
  })

  electron.powerMonitor.on('suspend', (e) => {
    console.log('Saving some data')
  })
}

app.on('ready', createWindow)

JavaScript

Screen

  • used only when app is ready

Screen

const electron = require('content/snippets/javascript-electron')
const { app, BrowserWindow, screen } = electron

let primaryDisplay = screen.getPrimaryDisplay()

let tray
let mainWindow

function createWindow() {
  mainWindow = new BrowserWindow({
    width: primaryDisplay.size.width / 2,
    height: primaryDisplay.size.height,
    x: primaryDisplay.bounds.x,
    y: primaryDisplay.bounds.y,
    webPreferences: { ndoeIntegration: true }
  })

  mainWindow.loadFile('index.html')

}

app.on('ready', createWindow)

JavaScript

Renderer process

Browser window proxy

Browser window proxy

  • we can control window properties
let win
const newWin = () => {
  win = window.open('https://developer.mozilla.org')  
}

const closeWin = () => {
  win.close()
}

<br />

JavaScript

Web frame

  • zoom
  • spelling and grammar
  • resources

Web frame

const electron = require('content/snippets/javascript-electron')
const { webFrame } = electron

webFrame.getZoomFactor() // 1 === 100%, 2 === 200%
JavaScript

Desktop capturer

const electron = require('content/snippets/javascript-electron')
const { desktopCapturer } = electron


desktopCapturer, getSources({
  type
})

JavaScript

IPC communication

IPC main IPC renderer

  • inter process communication

main.js

const electron = require('content/snippets/javascript-electron')
const { app, BrowserWindow, ipcMain } = electron

let primaryDisplay = screen.getPrimaryDisplay()

let tray
let mainWindow

function createWindow() {
  mainWindow = new BrowserWindow({
    width: primaryDisplay.size.width / 2,
    height: primaryDisplay.size.height,
    x: primaryDisplay.bounds.x,
    y: primaryDisplay.bounds.y,
    webPreferences: { ndoeIntegration: true }
  })

  mainWindow.loadFile('index.html')


  mainWindow.webContents.on('did-finish-load', (e) => {
    mainWindow.webContents.send('mailbox', 'you have mail')
  })

}

ipcMain.on('channel1', (e, args) => {
  e.sender.send('channel1-response', 'Message received on channeld 1')
})

cMain.on('sync-message', (e, args) => {
  e.returnValue = 'return value'
})


app.on('ready', createWindow)

JavaScript

renderer.js

const electron = require('content/snippets/javascript-electron')
const { ipcRenderer } = electron

ipcRenderer.send('channel1', 'Message')

const response = ipcRenderer.sendSync('sync-message', 'Message')

ipcRenderer.on('channel1-response', (e, args) => {

})

ipcRenderer.on('mailbox', (e, args) => {
  console.log(args)
})

app.on('ready', createWindow)
JavaScript

Remote module

Remote

  • risky exposing node js to users
  • performance penalty
  • accessing electron node modules in renderer process

main.js

const electron = require('content/snippets/javascript-electron')
const { app, BrowserWindow, ipcMain } = electron

let primaryDisplay = screen.getPrimaryDisplay()

let tray
let mainWindow

function createWindow() {
  mainWindow = new BrowserWindow({
    width: primaryDisplay.size.width / 2,
    height: primaryDisplay.size.height,
    x: primaryDisplay.bounds.x,
    y: primaryDisplay.bounds.y,
    webPreferences: {
      ndoeIntegration: true,
      enableRemoteModule: true
    }
  })

  mainWindow.loadFile('index.html')


  mainWindow.webContents.on('did-finish-load', (e) => {
    mainWindow.webContents.send('mailbox', 'you have mail')
  })

}


app.on('ready', createWindow)

JavaScript

renderer.js

const electron = require('content/snippets/javascript-electron')
const { remote } = electron
const { dialog, BrowserWindow } = remote

setTimeout(() => {
  dialog.showMessageBox({
    message: 'Dialog from renderer',
    buttons: ['One', 'Two']
  }).renderer(res => {
    console.log(res)
  })

  let win = new BrowserWindow({
    x: 50,
    y: 50,
    width: 300,
    height: 250
  })

  const app = remote.app

  let mainWindow = remote.getCurrentWindow()

  mainWindow.maximize()
}, 2000)

JavaScript

Disabled remote and calling electron modules from renderer

main.js

const electron = require('content/snippets/javascript-electron')
const { app, BrowserWindow, ipcMain, dialog } = electron

let primaryDisplay = screen.getPrimaryDisplay()

let tray
let mainWindow


ipcMain.on('ask-fruit', (e) => {
  callDialog().then(answer => {
    e.reply('answer-fruit', answer)
  })
})

ipcMain.handle('ask-fruit2', (e) => {
  return callDialog()
})

async function callDialog() {
  const buttons = ['One', 'Two']

  const index = await dialog.showMessageBox({
    message: 'Dialog from renderer',
    buttons: buttons
  }).renderer(res => {
    console.log(res)
  })

  return buttons[index]
}

function createWindow() {
  mainWindow = new BrowserWindow({
    width: primaryDisplay.size.width / 2,
    height: primaryDisplay.size.height,
    x: primaryDisplay.bounds.x,
    y: primaryDisplay.bounds.y,
    webPreferences: {
      ndoeIntegration: true,
      enableRemoteModule: false
    }
  })

  mainWindow.loadFile('index.html')


  mainWindow.webContents.on('did-finish-load', (e) => {
    mainWindow.webContents.send('mailbox', 'you have mail')
  })

}


app.on('ready', createWindow)

JavaScript

renderer.js

const electron = require('content/snippets/javascript-electron')
const { ipcRenderer } = electro

ipcRenderer.send('ask-fruit')

ipcRenderer.on('answer-fruit', (e, arg) => {
  console.log(answer)
})

ipcRenderer.invoke('ask-fruit2').then(answer => {
  console.log(answer)
})


JavaScript

Shared api

Process

electron js process

  • available at main process
  • available at renderer process when node integration is true
  • available process methods
    • hang()
    • crash()

Shell

shell

use default applications for resource usage

  • openExternal
  • openPath
  • showItemInFolder
  • moveItemToTrash
const { shell } = require('content/snippets/javascript-electron')
JavaScript

Native images

native images

main.js

const { nativeImage, ipcMain } = require('content/snippets/javascript-electron')

let mainWindow


ipcMain.handle('app-path', () => {
  return app.getPath('desktop')
})

JavaScript

renderer.js

const { nativeImage, ipcRenderer } = require('content/snippets/javascript-electron')
const fs = require('fs')
const splash = nativeImage.createFromPath(`${__dirname}/splash.png`)
console.log(splash.getSize())
const toPng = e => {
  let pngSplash = splash.toPNG()
}
const toJpg = e => {
  let pngSplash = splash.toJPEG(100)
}
const toTag = e => {

  let size = splash.getSize()

  const resizedImage = splash.resize({ width: Math.round(size.width / 4), height: Math.round(size.height / 4) })

  let splashUrl = resizedImage.getDataUrl()
  document.getElementById('preview').src = splashUrl

}
const saveToDesktop = async (data, extension) => {
  let desktopPath = await ipcRenderer.invoke('app-path')
  fs.writeFile(desktopPath + '/' + extension, data, console.log)
}

JavaScript

Clipboard

Clipboard

const { clipboard } = require('content/snippets/javascript-electron')

console.log(clipboard.readText())

const contentOfClipboard = clipboard.readText()

clipboard.writeText('some text')

const image = clipboard.readImage()

image.toDataUrl()
JavaScript

Features & techniques

offscreen rendering

  • The "paint" event will fire each time a section of the webContents is rendered to a BrowserWindow. This happens regardless of the webContents being visible or not and is useful for handling off-screen content rendering.

offscreen rendering

main.js

const electron = require('content/snippets/javascript-electron')
const { app, BrowserWindow } = electron
const fs = require('fs')


// dont use gpu, use cpu instead
app.disableHardwareAcceleration()

let primaryDisplay = screen.getPrimaryDisplay()

let mainWindow

function createWindow() {
  mainWindow = new BrowserWindow({
    width: primaryDisplay.size.width / 2,
    height: primaryDisplay.size.height,
    x: primaryDisplay.bounds.x,
    y: primaryDisplay.bounds.y,
    show: false,
    webPreferences: {
      ndoeIntegration: true,
      offscreen: true
    }
  })

  mainWindow.loadUrl('https://electronjs.org')


  let i = 1
  mainWindow.webContents.on('paint', (e, dirty, image) => {
    let screenshot = image.toPNG()
    fs.writeFile(app.getPath('desktop') + `/screenshot_${i}.png`, screenshot, console.log)
    i++
  })


  mainWindow.webContents.on('did-finish-load', () => {
    console.log(mainWindow.getTitle())
    mainWindow.close()
    mainWindow = null
  })

}


app.on('ready', createWindow)

JavaScript

Network detection

Online & offline

  • exclusive to the renderer process
  • there are events that report if app is online or offline
const isOnline = navigator.onLine ? 'online' : 'offline'

JavaScript

Notifications

Notifications

  • clicking the notification focuses the app
const { remote } = require('content/snippets/javascript-electron')
const self = remote.getCurrentWindow()

let notification = new Notification('Electron app', {
  body: 'Some notification info'
})

notification.onclick = (e) => {
  if (!self.isVisible()) {
    self.show()
  }
}
JavaScript

Preload scripts

security

preload.js

const fs = require('fs') 

window.versions = {
  electron_v: process.versions.electron,
  writeContent: function (text) {
    fs.write(__dirname, '/a_file.txt', text, console.log)
  }
}
JavaScript

main.js

const electron = require('content/snippets/javascript-electron')
const { app, BrowserWindow } = electron
const fs = require('fs')

let mainWindow

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000,
    height: 1000,
    show: false,
    webPreferences: {
      ndoeIntegration: false,
      preload: __dirname + 'preload.js'
    }
  })

  mainWindow.loadUrl('https://electronjs.org')

}


app.on('ready', createWindow)
JavaScript

Progress bar

  • loading bar for an app depending on the operating system
  • mainWindow.setProgressBar(-1) remove the progress bar

Progress bar

const electron = require('content/snippets/javascript-electron')
const { app, BrowserWindow } = electron
const fs = require('fs')

let mainWindow

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1000,
    height: 1000,
    show: false,
    webPreferences: {
      ndoeIntegration: false,
      preload: __dirname + 'preload.js'
    }
  })

  mainWindow.setProgressBar(0.25)

  mainWindow.loadUrl('https://electronjs.org')

}


app.on('ready', createWindow)
JavaScript

Application distribution

Electron builder

electron builder

CloudConvert is an online file converter. We support nearly all audio, video, document, ebook, archive, image, spreadsheet, and presentation formats. To get started, use the button below and select files to convert from your computer. Cloud convert


Code signing

Code Signing Certificates allow you to add digital signatures to your executables, enable software developers to include information about themselves and the integrity of their code with their software. The end users that download digitally signed 32-bit or 64-bit executable files (.exe, .ocx, .dll, .cab, and more) can be confident that the code really comes from you and has not been altered or corrupted since it was signed. Comodo ssl store Apple Developer


Publishing releases

Electron builder: A complete solution to package and build a ready for distribution Electron app for macOS, Windows and Linux with “auto update” support out of the box.

Auto update

Semantic versioning


AutoUpdater module

Auto update Electron log


MacOs

Apple id

Notarize

Electron notarize


Hardened runtime

The Hardened Runtime, along with System Integrity Protection (SIP), protects the runtime integrity of your software by preventing certain classes of exploits, like code injection, dynamically linked library (DLL) hijacking, and process memory space tampering. To enable the Hardened Runtime for your app, navigate in Xcode to your target’s Signing & Capabilities information and click the + button. In the window that appears, choose Hardened Runtime.

Hardened runtime


Touch bar

Apple touch bar Touch bar simulator