15 days ago

新建一個來兼看 crontab 是否順利執行的文件

touch /tmp/crontab_log.txt

設定定時任務

crontab -e

0/5 * * * * /bin/date "+%Y-%m-%d %H:%M:%S" >> /tmp/crontab_log.txt

:wq 存檔後退出,會看到

crontab: installing new crontab

接著我們來看每分鐘是否會將當前時間記錄到該文件裡

tail -f /tmp/crontab_log.txt

如果有出現就代表成功,可以在 mac 上執行定時任務。

如果沒有的話參考 在MAC OS X上如何启用crontab?

如果對於 cron 的時間設定不熟悉也可以參考 crontab.guru

 
5 months ago

這篇實作主要是練習如何使用 Swift 去實現

  1. Get JSON from URL
  2. Parse JSON

因為我平常有 Ruby 開發相關區塊鏈接 API 的經驗,所以這邊我挑了 CoinMarketCap 來串接,主要是因為他有直接兌換美元的結果,不需要再去計算,像是其他交易所如 Poloniex 只有 BTC 對 USDT ,畢竟 USDT 並不是真正的美元匯率(只是錨定接近而已)

一開始的 Outlet 跟簡單的 Interaction 就不多說了,就是拉一下 UI 畫面,分別設定好 Label 怎麼顯示、Button 會觸發什麼事件。

在這邊我意外發現一個專門講解 Swift 開發技巧的 Youtube 頻道,講的內容都很新,就以我現在 2017/12/02 來看,可以在影片看到上傳日期是 2017/07/01 ,講解的是最新 Swift 4 如何利用 Decodable 去 Parse JSON

Parsing JSON Just Became Super Easy in Swift 4 with Decodable

直接上程式碼

先宣告一個 Decodable 的 struct, 自訂你需要的型別

struct CryptoCurrency: Decodable {
    let name: String
    let price_usd: String
}
...

解析後並設定 label text 的 function

func updateBitcoinPrice() {
    self.cryptoCurrencyLabel.setText("Fetching...")
    
    // Coinmarketcap

    guard let url = URL(string: "https://api.coinmarketcap.com/v1/ticker/?limit=10") else { return }
    
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data else { return }
        
        do {
            let crypto_currency = try JSONDecoder().decode([CryptoCurrency].self, from: data)
            
            let btc_price = crypto_currency.first?.price_usd ?? "0.00"
            
            self.cryptoCurrencyLabel.setText("\(btc_price) USD")
            
        } catch let jsonErr {
            print("Error serializing json:", jsonErr)
        }
    }.resume()

這邊我比較懶的部分是直接拉 Array 的第一筆資料去顯示比特幣價格,因為 CoinMarketPrice 的價格顯示是依照交易量排名,也因此我認為比特幣交易量應該都是在第一名,所以在部份比較偷雞XD,不過還要在學習改進的部分是如何針對一長串的 Array 去找到指定符合的數值(今天只學拿到 data 然後解析完輸出)。

小結

不過稍微研究了一下 Swift 4 新的 Decodable + JSONDecoder 用法除了可以達到更簡單的語法撰寫,同時也能夠支援自訂型別,在以往的解決辦法常常要寫很冗長的程式碼或是使用第三方套件到了 Swift 4 都可以通通丟掉了。

實際作品在我的 Github watchOS-BitcoinPrice

參考資料

 
5 months ago

三個點的符號 ..., 英文的正式名稱是 Ellipsis,中文譯是指省略的意思,不過一般在學習這類運算符,下 google 的關鍵字應該多半都會是 three dots 或是 dot-dot-dot

展開運算符號 (Spread Operator)

這種運算符是一種速寫的語法,一開始可能不好理解,不過用上癮後就會熟能生巧了

const boys = ["Nic", "Kurt", "Jimmy"]
const girls = ["Doris", "Flower"]

console.log([...boys]) // ["Nic", "Kurt", "Jimmy"]


//  兩個 Array 組合

console.log([...girls, ...boys]) // ["Doris", "Flower", "Nic", "Kurt", "Jimmy"]

原本需要用 concat 完成的組合 Array 動作只要用展開運算符拼起來就可以了,而且不受 concat 只能放在陣列後方

替代原本 Array 的 apply 方法

由於擴展運算符可以展開 array, 所以在 ES6 之後不需要用 apply 方法,將 array 轉為函數的參數

// ES5

function sum(x, y, z) {
  return x + y + z;
}

var args = [1, 2, 3]
sum.apply(undefined, args); // 6


// ES6

function sum(x, y, z) {
  return x + y + z;
}

const args = [1, 2, 3]
sum(...args) // 6

展開運算符還有一個特別的功能,會把可迭代(iterable)或與陣列相似(Array-like)的Object 轉變為 Array

在 JavaScript 中內建的可迭代(iterable) Object 有

  • String
  • Array
  • TypedArray
  • Map
  • Set
const myName = "Nic"
const chars = [...myName] // ["N", "i", "c"]

參考資源

 
5 months ago

垂直下拉關閉鍵盤

通常用到 text view 讓人可以鍵盤輸入資訊的時候,其實可以貼心的加入垂直下拉就可以關閉鍵盤的功能,因為很多 APP 打完字是沒辦法關鍵盤的,這其實滿惱人的。

而這個功能也不需要額外寫 function , text view 元件中只要在 Scroll View 裡面將 Bounce Vertically 打勾就可以了。

開啟自動彈出鍵盤

如果你的這個頁面是一載入時,就是需要用戶填寫資訊或筆記的地方,像是內建的備忘錄,其實可以自動彈出鍵盤,不需要等用戶點擊螢幕之後才跑出鍵盤,這是一個比較貼心的方式。

只要在 Text view 元件中的 keyboard 設定為 Dismiss interactively 就可以了

 
5 months ago

必要時,需要依照風格或是設計師不同的風格來配合,所以我們會需要更改 Status Bar
的顏色,至於改的方式有兩種,可以透過設定檔或是直接從 code 裡面動態修改(比方說切換樣式等等)

以下圖來說,是更改後的樣式,如果沒更改就會跟背景一樣是全黑的,完全看不到 Status bar

前置動作

在專案下找到 info.plist 後將 View controller-based status bar appearance 這個設定關閉,如果找不到就自己新建一個然後設定為關閉就可以了。若設定沒關閉就無法改 Status Bar 的顏色,所以這個前置動作一定要記得做。

從設定檔改

在專案目錄下找到 Targets -> General -> Status Bar Style -> Light

從程式碼中改

UIApplication.shared.statusBarStyle = UIStatusBarStyle.lightContent
// 設定 status bar 為白色


UIApplication.shared.statusBarStyle = UIStatusBarStyle.default
// 設定為預設值

參考資源

 
5 months ago

Keyboard does not show up in simulator

Xcode 9.1

因為 Xcode 的預設是把你的電腦 keyboard 當作在模擬器裡面用的主選項,所以需要進去設定模擬器的 hard ware 鍵盤打開,如果沒有打開這個選項就會變成你寫了自動彈出 keyboard 卻在模擬器上沒有彈出來。

具體可以操作 Xcode HardWare -> KeyBoard -> Toggle Software Keyboard

也可以直接按下(⌘ + K),這樣一來 simulator 就會跟實機一樣有鍵盤。

以下是在使用者觸碰螢幕之前,也就是打開某功能或某頁面能夠自動加載鍵盤的方法

在 view did load 指定讓某 outlets 後加入 becomeFirstResponder

Usage:

class ViewController: UIViewController {

    // MARK: - View Did Load

    override func viewDidLoad() {
        super.viewDidLoad()
        subtotalTextfield.becomeFirstResponder()
    }
    
    // MARK: - Outlets

    @IBOutlet weak var subtotalTextfield: UITextField!
}

參考資源

 
5 months ago

ES6 之後可以使用 Set

var uniqArray = new Set([1, 2, 3])

uniqArray
// Set(3) {1, 2, 3}


uniqArray.add(3)
// Set(3) {1, 2, 3}


uniqArray.add(4)
// Set(4) {1, 2, 3, 4}

Javascript: Remove Duplicates From Array Of Objects

如果 Array 裡面是放 Object 就不適用了,網路上查到比較多的答案是有人寫了一個專門去除重複元素的 function

function removeDuplicates(originalArray, prop) {
     var newArray = [];
     var lookupObject  = {};

     for(var i in originalArray) {
        lookupObject[originalArray[i][prop]] = originalArray[i];
     }

     for(i in lookupObject) {
         newArray.push(lookupObject[i]);
     }
      return newArray;
 }

Usage:

removeDuplicates(arrayWithDuplicates, 'size');

Returns:

[
    {
        "color": "red",
        "size": "small"
    },
    {
        "color": "blue",
        "size": "medium"
    },
    {
        "color": "red",
        "size": "large"
    }
]

And:

removeDuplicates(arrayWithDuplicates, 'color');

Returns:

[
    {
        "color": "red",
        "size": "small"
    },
    {
        "color": "green",
        "size": "small"
    },
    {
        "color": "blue",
        "size": "medium"
    }
]

參考來源

 
5 months ago

目標:密碼設置必須要含英文及數字的組合

app/models/user.rb
  validate :password_complexity
  
  def password_complexity
    if password.present?
       if !password.match(/^(?=.*[a-zA-Z])(?=.*[0-9])) 
         errors.add :password, "Password complexity requirement not met"
       end
    end
  end

拿掉位數限制,這部份 devise 本身就有 validation , 甚至必要時可以直接在 devise config 設定 password length

/^(?=.*[a-zA-Z])(?=.*[0-9])/
   |             |          
   |             |          
   |             |          
   |             |
   |             任意字符串后必须要有「数字」
   |                        
   任意字符串后必须要有「英文」

參考資源

 
5 months ago

何謂 race condition ?

競爭危害(race hazard)又名競態條件、竞争条件(race condition),它旨在描述一個系統或者進程的輸出依赖于不受控制的事件出现顺序或者出现时机。 此词源自於兩個訊號試著彼此競爭,來影響誰先輸出。 -- wiki - 競爭危害

AASM 中的 race condition

開發 rails project 很常會使用 AASM 來進行狀態管理,好處是你可以不必寫一堆 if else 來判斷狀態該往何處走。

那麼有一種情況是,當兩個用戶同時更新一筆數據的狀況下,造成 race condition 的情況出現

舉例以交易訂單生成來說

  • order_placed: 是當訂單成立後的狀態
  • notify_paid: 是買家通知付款
  • done: 是完成訂單

那麼有種情況是,買家還沒通知付款,我這邊已經收到帳款,所以可以直接將訂單完成。

但有沒有一種可能是,賣家和買家在同一個時間點點擊狀態變更的按鈕

  • 賣家按下訂單完成
  • 買家通知已經付款

雖然在 AASM 很聰明的幫我們限制了能變更狀態的條件,但是卻會出現在被 object 暫存記憶體的時候,產生 race condition。

class Order < ActiveRecord::Base
  include AASM

  aasm do
    state :order_placed, :initial => true
    state :notify_paid
    state :done
  end
  
  event :notify do
    transitions :from => :order_placed, :to => :notify_paid
  end
  
  event :deliver do
    transitions :from => [:order_placed, :notify_paid], :to => :done
  end
end

這時候我們測試

# 這時候 Order 狀態應該是初始化狀態 order_placed

order_1 = Order.last
order_2 = Order.last

order_1.deliver! # true, 狀態變更為 done
order_2.notify! # true, 狀態變更為 notify_paid

order_2 不是應該被 rollback 拒絕存取才對嗎? order 應該已經變成 done, 沒理由又可以變成 notify_paid 才對呀

但在這個案例中的 object 裡面

order_1, order_2 這兩個 object 已經透過暫存記憶體記住訂單的狀態是 order_placed,所以他們合理的可以轉換狀態,但這樣就不符合我們的邏輯了,難不成在每次 order 執行的時候都要 reload 嗎?

其實不必,我們可以用 AASM 中的 Pessimistic Locking (悲觀鎖定) 來解決。

只需要加上 requires_lock: true,就可以避免 race confidtion

class Job < ActiveRecord::Base
  include AASM

  aasm requires_lock: true do
    ...
  end

  ...
end

讓我們在測試一次

order_1 = Order.last
order_2 = Order.last

order_1.deliver! # true, 狀態變更為 done
order_2.notify! # ROLLBACK, AASM::InvalidTransition: Event 'notify' cannot transition from 'done'.

完美了,可以避免同時間操作產生的 race condition。

小結

不過 Pessimistic Locking(悲觀鎖定)要看情況使用,因為他會影響一些 performance , 他的執行原理是如其名,悲觀的認定每一筆資料存取時,其他的客戶端也會存取同一筆資料,因此對相關的資料進行鎖定,直到自己操作完成才解除鎖定。

這樣的好處是

  • 提高隔離層級
  • 避免 race condition

壞處是

  • 存取效能變差,因為每次都要鎖定

如果你的 project 不會有同時改變狀態的可能發生,可以不用強制一定要在 aasm 上加上 Pessimistic Locking,只需要在適當的時機在用就可以了。

參考資源

悲觀鎖定(Pessimistic Locking)
競爭危害

 
6 months ago

If you can not restore backup on high version (like iOS 11.2 beta), maybe you can try my solution.

Situation

currently, I have two Iphone devise

  • Iphone 7 Plus, iOS 11.2 beta2
  • Iphone X, iOS 11.1

I want to restore backup from a newer version of iOS 11.2 beta2 to a IphoneX devise using an older version of iOS 11.1, but iOS and iTunes does not support this.

If you try this, you may see the following error messages:

  • You cannot see or select the most recent backup from the list of available backups in iTunes or iCloud.
  • iTunes displays the alert “The backup …. cannot be restored to this …. because of the software on the …. is too old.”
  • iTunes displays the warning “No backups available.”
  • Apple 備份時出現備份已損毀或不相容

Solution

You can update older devise version to latest ios version, but latest ios beta version is not support Iphone X.

So, you can wait it, or try my solution and following this:

  1. backup on your iTunes now.
  2. Use Decipher Backup Repair tool
  3. Fix it and restore to your newer devise.

notice: 29.99 USD can repair 2 backup.

Now, I restore my newer version of iOS to older version of iOS successful now.