Do you feel clumsy when using Jupyter Lab / Notebook with lots of opening websites? Do you want to get rid of the tab bar / address bar / bookmarks and focus on your notebook?
Entering full screen is an option, but it has draw backs say you can’t put multiple pages for reference on the same screen, and switching between apps is cumbersome… etc
Isn’t it great to have your Jupyter running as a native app?
Go download Fluid App, it can turn any URL into native macOS app.
Enter the URL, app name, provide an app icon, and you are done!
Jupyter as Native App: Tabs / address bar / bookmarks are gone
Now you can place your Jupyter app on the dock, or alt-tab to switch between other apps.
Note that its better to launch Jupyter with--no-browser option in terminal, then set a password for the Jupyter server. Then you just need to put your password next time when launching the native Jupyter app.
P.S.
Don’t have the app icon? I’ve prepared one for you below 🙂
Run Jupyter Notebook Server as a background service on your mac.
Generate a Login Password
The first thing you need to do is to generate a password for your server, such that all your codes / resources are being protected.
Open terminal, type ipython to enter interactive console, then enter the following:
from notebook.auth import passwd
password()
Generate password hash in iPython console
The password you entered here will be the password for logging in your Jupyter notebook server.
The output of the passwd() function is the hash of your password, you will need to paste the hash into your Jupyter configuration file later.
Configure the Jupyter Notebook
You first need a configuration file to configure your notebook server. Generate the config file with the follow command in terminal:
jupyter notebook --generate-config
A jupyter_notebook_config.py will be saved in your ~/.jupyter directory, open it and edit the follow entries:
You may now start your notebook server by typing jupyter notebook in terminal, and you can be able to access it from any computer in your network, with your server IP address and the default port 8888.
Server setup successful if you see the above login page
Type in the password (not the hash) and you are good to go.
Start the Server as a Background Service
In stead of staring the server with jupyter notebook in terminal, we can use the following command to start the server in background, then close the terminal.
jupyter notebook &> /dev/null &
What we did above is to put all the text output generated by the server to void (&> /dev/null), then run the server process in background (the last &).
You can also check the server process with the following command:
ps aux | grep jupyter
You will get the process ID from the above command, and you can stop the server by:
kill -9 <PID>
Optional Setup
Above is the minimal setup for runing a Jupyter Notebook server in background on mac. If you would like to enhance the server security and usability, here listed some extra things you can do.
Using HTTPS for Secure Server Access
If you would like to connect the server through a secure connection, such that your password is encrypted during network transmittion, you can enable SSL of your server.
If you refer to the Jupyter Notebook documentation, they suggest us to generate self-signed certificate and private key for SSL connection. It does work in desktop computers and Android devices. However, it doesn’t work in iOS devices, no matter using Safari, Chrome, or native Jupyter client apps.
A better solution is to be the Certificate Authority (CA) yourself, and generate a CA cert to sign your own SSL cert. The details of generate self-signed CA cert, SSL cert and private key could be found in Juno’s documentation. The doc also demonstrated how to install the CA cert into iOS devices.
After generating the SSL cert and key, you need to specify them in the Jupyter configuration file. Open your Jupyter_notebook_config.py again and edit the following entries:
After restarting the Jupyter Notebook server, you may access your server with https protocol.
As your cert is self-signed, your browser will warn you and you have to click show details and visit this website to confirm that you trust the cert and the site. You also need to type in your mac login password for accepting the cert for your computer.
Special Notes for iOS devices: at the time of writing, most functionalities of Jupyter Notebook work well in web browsers, however some keyboard shortcuts, say escape / control-m / tap auto-completion are not functioning in both Safari and Chrome… using native app like Juno seems a better choice at the moment.
Start the Server in Background During Login
There are serveral ways to start the notebook server during login, such as setting up a launchd deamon. However, I prefer setting Login Items in System Preferences → Users & Groups as its simplier to implement.
We first create an Login Item using Automator App
launch Automator app, select “Application” for document type
drag Library → Utilities → Run shell script to the workflow area
paste the following script and save it as JupyterAutoStart.app
The script did pretty much the same as Start the Server as a Background Service above. Here we also save the server process ID into a text file, such that we can kill the old server instance and start a new one if we login again.
After adding the Login Item, re-login to check if your notebook server is runing at the background or not.
Router Setting for Servers in LAN
You have to do port mapping on your router if you want to access a server in LAN from the internet. Here I use Apple Airport Extreme as an example, but all routers should work similarly.
Open AirPort Utitility → Network → Port Settings, add a new entry and map the public TCP port to your private(your local server) TCP port.
Last but not least, you may want to apply for a dynamic DNS service such that you don’t have to change your server URL everytime your IP changed.
Harnessing The Power of Both Languages in Your Apps
WebKit allows us to use javascript along side with the native swift code. On one hand, we could call the javascript statements in swift. On the other hand, javascript from web view could be able to trigger a delegate method defined in swift code. This gives us a two way communication between the native swift code and javascript used by a web view.
Basic Setup
Here we have a window with a WKWebView showing some text. It has 2 buttons inside. One for showing the text and triggering a swift handler, the other for hiding the text. Also, we have a native button that triggers a javascript function to hide the text.
Triggering Javascript Functions from Swift
The IB outlet and action of the app as follow. Note that we simply use evaluateJavaScript(_:completionHandler:) to trigger some javascript in the web view, hideText() is a custom method we defined inside javascript.
Receiving Javascript Messages
To receive message from the javascript, we need to provide a message name for javascript to call upon. We just name it “jsHandler”. After that we load the html and javascript into the web view.
We also need to adopt the WKScriptMessageHandler protocol in our view controller, and implement the userContentController(_:didReceive:) method. For simplicity, we just print out the message received from the javascript.
The HTML and javascript we loaded as follow. Note that in the showText() method, the window.webkit.messageHandlers.jsHandler.postMessage() is the method we use to trigger the delegate method implemented in our view controller.
To create an app that can be auto launched after login (i.e. adding login item) is far more complicated then expected…
For sandboxed app, the recommended approach is using the Service Management Framework. (Adding login items using a Shared File List is another approach for non-sandboxed app, which will not be discussed here.)
The basic concept is to create an “Helper Application” that registered to the system, which responsible for launching your main app while user login.
The Helper App
In your project, create a new Cocoa Application target.
As the helper app should have no visual element, remove the view controller and window scene from the storyboard, as well as the related swift files.
Remove the ViewController.swift
Remove the View Controller Scene in the storyboard
Remove the Window Controller Scene (if you want visual on the helper app to see if its successfully launched by the main app or not, as well as launched by the system service or not after logging in, you may keep it for a while and delete it afterwards)
As the helper app will have the same default AppDelegate.swift which conflict with the main app. You need to rename it and adjust the class name of App Delegate in the Application Scene accordingly.
Then make the helper app as a background service. In Info.plist, set Application is background only to Yes.
In the app delegate, check if the main app is already running or not. If not, launch it.
The tricky part is to delete the last four path components of the helper app bundle path. It is because the helper app is actually embedded inside the main app bundle, under the subdirectory Contents/Library/LoginItems. So including the helper app name there will be a total of 4 path components to be deleted.
Another tricky part is to set skip install = YES in the build setting.
You then turn on App Sandbox in Capabilities of the helper target, and the helper app part is done.
Main App
You first import ServiceManagement, then check if the helper app is already running or not, launch and register it with SMLoginItemSetEnabled(_:_:) if necessary.
In my sample app I created an checkbox to toggle the helper app, as well as showing if its already launched or not.
We then copy the helper app into the main app bundle. In the Build Phases of the main app, create a new Copy Files phase.
with the following settings:
Destination: Wrapper
Subpath: Contents/Library/LoginItems
Add the helper app file
The last step is to enable App Sandbox and Development Signing of both main and helper app, then your are good to go.
Testing
You can test the auto launch feature as follow:
Build the main app — NOT RUN (if you build and run the main app in Xcode, the helper app may not function after quitting Xcode)
Right click the product of your main app → Show in Finder
Double click the main app in Finder to launch it, and check the auto launch button
Quit everything and log out
Log in again
The main app should be auto launched by the helper, with the auto launch button checked.
Menu bar apps refer to apps that sit on the menu bar (a.k.a. status bar) of macOS. It provide instant access of the key functionalities, as well as being accessible all time during a login session.
Creating the Status Bar Item
We use NSStatusBar.system().statusItem(withLength: -1) to request a status bar item from the system. Note that it may be failed (return nil) if the status bar is already full packed with many other status bar items.
Upon successfully got an status bar item, you got a NSButton as the UI element to put on the menu bar. You need to configure it by providing an image, as well as setting the target and action of the button.
All these should be done during the app launching period, so in the app delegate, we have:
Note that a strong reference must be used to retain the status bar item object returned by the system. Otherwise it will be released afterwards, and nothing will be shown on the menu bar.
Displaying the Menu
To display the menu, you need to provide a reference point for placing the menu object, in this case, the status bar button. We then create a mouse event, and use popUpContextMenu(_:with:for:) to bring up the menu.
Info.plist Setting
If your app is menu bar ONLY, similar to the Dropbox macOS client, you need to set Application is agent = YES in your Info.plist in order to omit the Dock’s app icon and the app menu on the upper left corner.