[Electron] How to codesign your Mac app

Image Credit

codesign

Introduction

Recently, Electron desktop application gets hot and popular among developers. It’s easy for developers to quickly mockup a desktop application and distribute it to three different platform with zero efforts ! (This is really a lie after I made another popular open source project called Kaku with the same tech.)

Ok back to topic, for companies who want to distribute their desktop apps to the world, they must codesign the application first ! The reason why this is important is because if you don’t do so, if the user enable gatekeeper by default, the application will be blocked due to unidentified developer and users will not be able to open it !

unidentified_developers

security_preferences_options

So in this article, I’ll try to share some my experiences about how to do the codesign for Mac app and what’s going on behind the problems.

TL;DR

Thanks to Marco Pracucci who wrote some useful details in his post, you can just follow these simple steps to do codesign :

  1. Get a Developer ID certificate from Apple and install it into your Mac’s Keychain
  2. Sign your application bundle codesign --deep --force --verbose --sign "<identity>" Application.app
  3. Verify the signature codesign --verify -vvvv Application.app and spctl -a -vvvv Application.app

Note: The identify here is the id wrapped inside the parentheses that you can get from keychain like the screenshot below:

keychain

But … the world sucks

The first problem I have encountered is version problem.

After codesign the app on my laptop (running 10.11.4) and share this app to my team members to test with, for those who are using 10.10.x, they will not be able to use the app and the gatekeeper’ll keep complaining something like the image below : 

vectr_damaged

But for the others who are using 10.11.4, the app is working well on their laptops (WTF???)

After googling a while, I finally found a discussion thread here on GitHub talking about this ! If you codesign your application on 10.11.4 , you’ll successfully get it codesigned and usable BUT for users who are using 10.10.x (to be specific those who use laptops <10.11.3) will have problems when opening that app.

From the discussion thread, we can learn that there are something changed from 10.11.3 to 10.11.4. If you want to hack that around by yourself, you’ll need to follow the steps here. But for me, I use electron-packager, so if you are using that too, you can just upgrade to 6.0.0+ because they already handle that for you on that version with zero efforts !

How to verify your app

  • codesign
    • This tool is not only for doing the codesign, but also you can use it to verify whether it’s well signed or not. After running it, you can get things like this :

https://gist.github.com/EragonJ/cc16613280384b0bf4bda36f1e761171

  • spctl
    • For this tool, this is mainly used to see whether your codesigned application will be blocked by gatekeeper or not, if you didn’t get anything wording like accepted, your application will always be blocked or treated as damaged by gatekeeper, so it’s really important to use this tool to do the check every time when you create the new application.
    • Note: If you are using Apple Developer ID certificate to build you application and be distributed by yourself, Remember to change your [System] > [Privacy] to “Mac App Store and Identified developers. Otherwise, you will keep getting rejected information.

https://gist.github.com/EragonJ/36f31b2b1f0659e95ef4fdab6b2e9a02

How to test

You can check details here from Apple’s documentations, but I’ll still copy some here.

  1. To disable Gatekeeper using the spctl command
    • $ sudo spctl --master-disable
  2. To confirm that Gatekeeper is enabled using the spctl command
    • $ spctl --status
      • If enabled, you will get assessments enabled
      • If disabled, you will get assessments disabled
  3. To test your Developer ID-signed app
    1. Make sure your gatekeeper is enabled by follow above ways.
    2. Email your Developer ID–signed app to yourself and use the copy that Mail downloads to trigger the dialog.
    3. Host your Developer ID–signed app on your own local or remote server and use the copy that Safari downloads to trigger the dialog.
    4. By doing so, your gatekeeper will be triggered correctly !

Troubleshooting

A) My certificate on keychain keeps showing expired

This problem is critical and annoys developers who are not familiar with Apple certificate things (like me !) When working on the codesign feature, I noticed that no matter how hard I re-install the certificate that I download from Apple, it will keep showing expired with no reason !

After trying and discussing this with one of my iOS friend, we finally realized it’s related to this notice ! For someone (not sure who, but including me), the Apple Worldwide Developer Relations Certification Intermediate Certificate is going to expire on Feb 14, 2016 which will cause the problem to make your certificate keeps expired.

What you need to take action here is to renew the this core certificate and try to re-install (or re-create) your developer certificate again and things will be solved … WTF !!

B) Where to find my certificate ?

For most of people, if you are writing desktop application by Electron, normally you’ll distribute your application by yourself (like us). But, I noticed that it’s really frustrating to figure out how to get that Developer ID Certificate !!

I have tried several times from https://developer.apple.com, but there is nothing related to Developer ID Certificate (or maybe it’s mainly because I am using different role, so it’s not showing up ?) ! After reading tons of articles, I finally realized that it’s inside Xcode !! You can check the screenshot below :

issue_certificate

But you can notice that the buttons are grey and is not clickable. That’s mainly because the role is wrong !! Even if you are admin role, you still can’t generate that ! Only Team Agent can do that ! So if you are developer or admin, Go ask your leader for that and tell him/her to press the button above to create the certificate for you.

After getting the certificate (it will be name like this xxxxxxx.p12), you can just double-click the file and install into your keychain. Remember, always keep them private 🙂

Note: Only the first one who made the certificate and uploaded to Apple Developer can have this .p12 file. This will only exist on his own keychain and you can’t even get it from Apple Developer. So remember to ask him to export that p12 file and share with the whole team ! Remember, if you lost this .p12 file, no one can save you and you need to regenerate another certificate again.

Last few words

It’s really a hard time to fight with codesign stuffs especially there are less resources talking about this. But whatever, I wrote them down already !! If this article does help you, please feel free to share !

And also, if there is anything wrong or missing in the article, feel free to tell me and I’ll update it ! See you guys next time 🙂

Reference

  1. https://pracucci.com/atom-electron-signing-mac-app.html
  2. https://github.com/electron/electron/issues/4899
  3. https://goo.gl/ZgKXr1
  4. https://developer.apple.com/support/certificates/expiration/

[Javascript] TwZip.js

twzip

Github Link

最近因為 Hax4 有 project 需要用到台灣郵遞區號,所以就花了一點時間做了一個簡單的 js (jQuery) plugin – TwZip.js。不過身為一個前端工程師當然想把事情都留在前端處理就好,因此當初在設計這個 plugin 的時候就打算把所有的資料都放在前端,然後以 lazyload 的方式把資料載進來而不需要後端的支援(超懶)。

而說到資料,TwZip.js 的資料是從中華郵政全球資訊網下載來的,然後簡單寫了一個 makefile 搭配 node.js 自動產生數百個鄉鎮市區的 metadata 供前端使用。

這邊要提一個我一直被卡住的白痴問題(OS 老師當初應該把我當掉),因為原本我的設計是三層式架構(像是 台北市/信義區/信義路),但是這會造成資料過度碎片化的問題。之所以會這樣是因為在 file system 裡面最小單位的 block or Cluster 有一個最小容量,像我電腦就是 4 KB,所以如果資料小於 4KB 就還是以 4KB 計,因此這個三層式設計最後就產生出 16X MB 的資料(因為產生出太多 metadata 了)… 整個比原本的專案大上好幾十倍呀 …

為了解決這個問題,最後就改成兩層式架構(像是 台北市/信義區,也因為層數變少的關係所以最後就變成約 4 MB 的資料了,整個就是皆大歡喜。

還記得以前的前輩曾經教過我在寫程式的時候要先把 API 想好,確定使用者使用的介面再開始寫細節,透過這樣的決定來讓整個專案照著預期的方向前進才是比較好的,這樣才可以提前知道這個設計上的缺陷及問題,算是又多了一點體悟吧!

儘管如此,目前這個 plugin 還只能算是一個 prototype ,未來應該會再把 zipcode 的資訊透過 callback 傳回去。其他的功能就再說吧。

DogFooding ++

[FxOs] Inter App Communication

之前投了一篇稿在 Mozilla TW 的謀智台客,主要是在探討 FxOS 如何透過 IAC 在 App 間溝通資料,廢話不多說,請看:

iac

在開發 App 的時候,我們有時候需要在App之間傳遞資訊,而今天我們要介紹的主角就是新引入的 WebAPI: IAC (Inter App Communication),有了這個好東西,我們就可以輕易的在 App 間傳遞訊息,以下就用筆者遇到的例子來說明吧:

User story

當使用者拿到 Firefox OS 手機,第一次開機的時候,會自動先開啓 FTU (First Time Use) App 作導覽。當導覽進行到最後一步的時候,會看到這個畫面,可以按下「Home」鍵回到主畫面。

ftu-mobile-627x1024

而在 Firefox OS 平板上,最後一步的導覽畫面會是這樣,但是改透過由螢幕下方往上滑動的手勢回到主畫面:

ftu-tablet-1024x640

大家可能還不是很明白為什麼這個 User Story 下面要使用 IAC,讓我解釋一下。因為在手機剛啟動的時候,我們的 FTU 是一個獨立的 App,而這個 App 是被 System App 所呼叫起來的,兩個 App 之間如果需要溝通,就需要透過 IAC 來傳遞資料。

之所以 FTU App 需要跟 System App 做溝通,是因為「使用者在 FTU 導覽過程中,是無法按 Home 鍵離開的」,但很不巧的這個需求在 Firefox OS 平台是 Tablet 的時候反而變成是一種限制,怎麼說呢,因為對 Tablet 來說,我們並沒有實體的 Home 鍵,因此「Swipe 向上的手勢」就被我們定義成「使用者按 Home 鍵」,但是在先前 Mobile 的需求下,使用者在 FTU 是不允許按 Home 鍵的!!因此這就產生了一個問題,我要怎麼判別使用者現在的情境是:

  1. Tablet 使用者
  2. FTU 已經導覽到最後一步
  3. 使用者可以透過螢幕下方往上滑動的手勢(等同按 Home 鍵)離開 FTU App

這個時候…我們就需要 IAC 的幫助!

IAC , we need your help

這邊要解釋一下 FTU App、System App 及 IAC 的角色。

FTU App 的功用主要是在教導使用者如何初始設定 FxOS 及透過 step by step 的教學來了解如何使用 FxOS 的裝置,所以他可以知道使用者目前的 step 到哪個階段了。

System App 在這裡的功用是在處理使用者按下 Home 鍵時要處理的事情,他只知道目前使用者是不是按下 Home 鍵(在 Tablet 就是 Swipe 向上的手勢),然後依照各個平台做相對應的事情。

而 IAC 的角色就是 FTU App 及 System App 溝通的橋樑,因為 System App 不知道目前 FTU App 是不是到了最後一步,所以自然就無法解除阻擋 Home 鍵這件事情,所以在這個 User Story 下,當使用者來到最後一個 Step 的時候(而且是在 Tablet 的平台),FTU App 就會透過 IAC 和 System App 溝通,然後請求 System App 可以讓我們以透過 Swipe 向上的手勢(等同按 Home 鍵)離開 FTU App。

在 Gaia 我是這麼做的:

In FTU App :

https://gist.github.com/EragonJ/7986909.js

從程式碼中可以看到我們透過 app 實體去連接一個 keyword(在這個例子中就是 ftucomms),FTU App 跟 System App 之間就是透過這個 keyword 來當做識別並透過 port 來溝通,我們只需要把東西通通塞進 port.postMessage 做雞精就對了!

聰明的你可能會問,所以 App 只需要隨便用個 keyword 就可以連結並傳東西嗎?當然不行,接收訊息的 App (傳送訊息的不需要)還需要去它的 manifest.webapp 設定如下:

https://gist.github.com/EragonJ/7986929.js

這樣子我們 System App 就可以接收來自 FTU App 的訊息了!在 System App 只要我們確定傳送進來的 message 是我們當初預期的 ‘done’ 就可以放行讓使用者可以透過 Swipe 向上的手勢離開 FTU App 了!

不過你可能又會問我要怎麼讓 System App 來接這個訊息呢,這是個好問題。如果你需要透過 IAC 來做資料傳遞的時候,你需要使用 mozSetMessageHandler 來掛載相對應的 Handler。

下面是 IAC 的基本使用方法:

https://gist.github.com/EragonJ/7986936.js

但是要注意的是因為這個 API 只會執行最後一個掛上去的 Handler,所以當你一個 App 要做多組 IAC 資訊傳遞的時候就需要自己做額外的處理。

但在寫程式的時候沒注意到件事情,所以當時 Music App 的另一個 contributor – Jim Porter 同時也自己在 System App 建立了一個 Handler,而我的 Handler 又是在他建立之前建立的,因此所有我 FTU 有關的 Handler 不會執行到,因為通通都被它的 Handler 蓋過去了。

所以為了讓後面的人不要發生這個問題,之後就和 Jim Porter 討論出一個 IACHanlder,每個 App 只要引入這段 javascript ,我們就會自動幫他建好一個共用的 Handler,所有的資料都是這個 Handler 統一管理,然後以一個固定的 naming 用 Custom Event 的方式傳播出去,讓有需要的人可以自己接 Event。(有興趣了解實作的人可以看這裡

所以最後 System App 只要用這樣簡單一行程式碼就可以接 Event 然後做解除限制的動作了:

https://gist.github.com/EragonJ/7986944.js

是不是很簡單呢!希望這篇能夠幫助到也需要使用 IAC 的開發者朋友哦 🙂

參考資料: Inter App Communication wiki 文件

[FxOS] Deep into Template.js

Image Credit

fxos

因為最近開始接觸很多 FxOS 核心的程式碼,所以發現了很多精鍊的程式碼,其中有一個很有趣的是 Template.js。

市面上有一大堆 Template engine,基本的功能該有的都有,幾乎都大同小異。但是 FxOS 自己的 Template 卻相當吸引我的注意,因為他才短短的一百多行就實作了一個 Template engine 該做的所有事情(這邊只討論 Search then Replace,不考慮 inline logic )而且有一個很關鍵的事情,就是他巧妙的解決了 Template 存在性的問題。

通常一般 Front-end 的 Template engine 都有一個問題,那就是要把 Template 存在哪裡?

Way 1 – by myself

愛用 jQuery 的我通常都是這樣做的:一開始先透過 selector 把 template 載入(一般都會 cache 住,只是我這個範例沒有特別寫這一段),而這個 template 本身會搭配一個 hide 的 class 避免一開始頁面在 render 的時候會被使用者看到,最後利用 sub-selector 把相對應的地方換成新值,最後再拿掉 hide class 並塞到 DOM tree 內。

https://gist.github.com/EragonJ/7300164.js

恩,整體看起來好像還不賴,整個 code 簡單好懂,也做了 template cache(我沒特別寫,但是通常都會被我 cache 到某個 Controller 下),後續維護也只需要加上相對應的 selector 然後替換值就好了。

Way 2 – by Handlebars.js

雖然 Way 1 已經很好了,但是有時候需要 toggle hide class 其實還蠻麻煩的,能不能不要把 CSS 和 Template 扯在一起?有了這個想法後,我就開始到處尋找可能的做法,最後找到了 Handlebars.js,他巧妙的利用 Script tag 做到這件事情:

https://gist.github.com/EragonJ/7300364.js

它的作法是去改變 script tag 的 type,你可以看 w3c 上面的說明,只要 browser 看不懂 script type 的話,它一致都會把它 render 成 text,又因為 script tag 本身就不會被 render 出來,所以就達到把 template 藏起來的效果,最後只要搭配 selector 去把它拉出來然後替換值就可以了。

Way 3 – by Template.js in FxOS

雖然 Way2 感覺起來好像不錯,但是利用 script type 這種 hack 的方式感覺有點不是這麼優雅,因為 Template 的東西本來就不該存活在script tag 內,到底還有沒有別的辦法來做到這件事情呢?

有,我在 Template.js 找到了。

http://gist-it.appspot.com/https://github.com/mozilla-b2g/gaia/blob/0a0dcb5a12d70ccd07edfd7c1392cdbf24f6febc/shared/js/template.js?slice=41:53

從程式碼我們可以發現他會一直去尋找所有的 sibling 直到找到整個註解,在 HTML 裡面,註解就是 <!– –>,所以一開始很巧妙的,我們的 template 就是被註解起來的,也就不會被使用者看到內容,也因為一開始就看不見,所以我們就根本不需要去做 CSS 的視覺欺騙,整個就是把 nodeType 利用到極致,使用先天的特性漂亮的解決了這個問題,也就產生了這個 Template.js 。

很有趣吧,有興趣的人可以去看 Template.js 作者 Rick 的 Github,其他的就讓我們留到下次再來分享吧 😛

[Note] Useful tools for F2E

這篇用來整理一下與 F2E 相關的 tool,以供日後參考用(持續更新):

Javscript | CSS Compressor(壓縮器)

  • YUI compressor
  • 好用的 YUI compressor ,支援 JS | CSS 的壓縮,使用說明載點

Obfuscator(混淆器)

Test(測試)

  • Qunit
  • Javascript 單元測試的 Framework,適用於 jQuery plugin,使用說明載點

[Notes] Interesting animation in jQuery

剛剛意外再次看到tzangms的部落格,沒想到已經過了幾年的時間了!因為這段日子對javascript開始有了一些研究,所以對它的Banner scrolling 的特效很感興趣,就順手研究了一下它的 js code,沒想到這麼簡單就能做到,不知道該說 jQuery 太厲害了,還是想到這種實作方法的人太聰明呢:P

上面這張是他網站的截圖,光是版面的配置以及使用的自製圖案來看,其實就看得出小巧精美的一面,不過今天的重點是那個Banner,我對於他可以做到捲動的效果感到很好奇,而實際做法其實也就是想像中這麼容易,只是他利用了一個CSS background-position的特性,原來當div的background設定成無限延展的時候(預設),就可以利用把position拉到無限遠的地方並搭配 Timer(jQuery可以用animate)設定好一個時間差,然後讓頁面在這個時間內一直偏移position的值來完成捲動的特效。

最後附上測試用程式碼,而圖檔的部分則是以tzangms的banner為例。
https://gist.github.com/757436.js?file=gistfile1.html

[JS] jquery.zoomImg.js 1.0

原本我一直不認為Javascript是一個很特別的Script Language,但是直到最近因為工作的因素所以逼不得以要去維護相關的Javascript的程式,結果沒想到Javascript竟然是如此的博大精深,甚至目前的Desktop application或是 Mobile application都開始使用Javascript來做開發。

直到最近剛結束的Coscup,我想效法一些朋友對自由軟體的態度來激勵自己,一來可以為這個世界盡一份心力,二來也可以訓練自己的能力,就當打怪練功吧:P

這個jquery.zoomImg.js 1.0是因為想要先寫好放著,等之後要做Blog的Image特效時使用的XD,不過既然有這個想法,那就打鐵趁熱,先把基本的功能刻出來,以後再做整理並加強功能吧。

這個plugin是在jquery-1.4.2.min.js的環境下開發的,不過我並沒有用到1.4才特有的一些寫法,所以應該在1.3也還能夠使用。jquery.zoomImg.js In Github

[JS] jQuery.css() problem when using Firefox

最近在公司開發JS的程式的時候,很多時候都會利用到快速又方便的jQuery Library,而這次就很剛好的遇到了Bug,花了很多時間才解出來,以下為測試環境(FF 3.59 , 3.6 ):

假設我現在有一個DOM的元素,是一個img,當我設定其CSS為「display:none;」時,在FF上就會沒辦法利用
「jQuery.css(‘left’) or jQuery.css(‘top’)」來把這個值取出。就算你已經設定好left或是top的值,都只會抓到0px。

範例程式碼:
http://gist.github.com/518809.js?file=gistfile1.html

所以為了要解決在元素被hidden時不能取值的問題,就只能用比較髒的手段來抓到這個值,就是利用「visibility」。

什麼是visibility?它和display不同的地方在於說,display:none會直接不顯示這個元素並不佔用空間;而visibilty:hidden則是不顯示這個元素但是卻仍存在並佔用空間。

所以這個很Tricky的方法可以參考下面:
http://gist.github.com/518819.js?file=gistfile1.html

這樣就可以成功的取到left的值(top亦同)而不會影響UI的部分了,算是這次遇到最無言的Bug吧。

下次見囉。

δ 2011/01/20: 又開始繼續維護這個專案,其實對 FF 3.6.8 來說(不確定其他版本是否也會有相同問題),就算是最新版本的 jQuery 1.4.* 都無法從 display:none 的元素中利用 .css() 來取得特定的屬性值,因此我利用 jsfiddle 寫了一組完整測試,包含 display:none 及 visibility:hidden 元素的各種可能,請自己用你的瀏覽器來測試一下吧。

[JS] Image preload

The way a browser normally works, images are loaded only after an HTTP request is sent for them, either passively via an tag or actively through a method call.

所以這就是為什麼圖片都會有Delay抓不到的情況…因為他通常是被觸發時才會自動去抓src的圖。可是如果真的是這樣的話,那就要想個辦法來避免這個問題。不過該怎麼辦咧?

The simplest way to preload an image is to instantiate a new Image() object in JavaScript and pass it the URL of the image you want preloaded , and load it simultaneously to the page with the onLoad() event handler:

哦酷哦,直覺上的想法就是要讓頁面load完時就先onload 一個function,這樣就可以把執行時間提前,因此只要寫個function包起來讓onload去讀就可以了。

DEMO CODE:
http://gist.github.com/251380.js?file=gistfile1.js