既然講到抓圖神器, 就先幫友人Wisely宣傳一下: https://play.google.com/store/apps/details?id=com.wisely.imagedownloader

不過在這並不是要宣傳抓圖神器的, 之前在找題目練習Swift時, 就想到要寫一個解析HTML的工具, 然後可以像是jsoup, 或是goquery一樣方便, 至少用來解析網頁內的連結或圖片連結方便的, 比如說像是下面的code:

let $ = SwSelect(html)
let imgUrls = $(”img”).attrs(”src”)
for url in imgUrls {
    print(url)
}

這個例子就這幾行就可以把網頁裡面圖片的Url給抓出來了, 又或, 如果我們只想抓某特定class的tag內包含的圖片(如:”<div class=“a1“><img src=“aa.jpg”/></div><div class=“a2″><img src=“a2.jpg”/></div>” 我們只想要包含在a1裡面卻不想要a2裡面的就可以用:

let $ = SwSelect(html)
let imgUrls = $(”.a1″).find(”img”).attrs(”src”)

本來我是想學goquery一樣, 複刻出一套jQuery出來的, 順便複習一下jQuery, 上面的範例其實也像是jQuery的API(不得不說這API還有selector設計真的是神來一筆), 因此選擇要把goquery從golang po到swift來, 其實是蠻可行的, 因為這兩個語言相似度還蠻高的, 但做到一半發現, 要複製整個jQuery其實蠻龐大的, 而且我只需要解析HTML的部分並不需要修改HTML的部分, 因此中途急轉彎, 把寫到一半的code, 重新包裝成這個SwSelect, 剛好也足夠達到上面兩個例子想要的啦

由於原本就是想從goquery po過來,  因此包裝了libxml2, 也把goquery用的selector parser的核心 - Cascadia給移植到Swift來, 整個SwSelect可以說是基於Cascadia, 這次也順便練習了Swift 2的一些新特色, 不過在整合libxml2上面也吃了些苦頭, 這以後有機會再寫, 目前其實應該可以使用了, 說明還沒補, Cathage的支援也還沒試過, 不過一些用法可以看一下Unit test來當範例:

https://github.com/julianshen/SwSelect/blob/master/SwSelectTests/SwSelectTests.swift

這週應該會找時間補上說明

如果有需要的話歡迎拿去使用:

https://github.com/julianshen/SwSelect

在Swift裡, Protocol和Protocol extension是非常強悍的東西, 比起OO世界的Class, 其實是更加犀利, 在今年(2015)的WWDC有一場個人很推薦的Protocol-Oriented Programming in Swift , 這場講的蠻精闢的, 熱門到後來加開了第二場, 今年我去WWDC, 我自己就兩場都去了, 回來又再回為了一次, 相當建議聽看看的:

https://developer.apple.com/videos/play/wwdc2015-408/

好, 這篇重點不是推薦這場演講….而是怎在Swift中去建立Array extension? 這好像沒什麼難的, 不就像下面這樣就可以了?

extension Array {
   func concat() -> String {
       var str = “”
       for i in self {
           str += String(i)
       }
       return str
   }
}

沒錯, 這是最簡單的方式, 但其實有個問題, Array內的元件(element)是泛型, 也就可能是任意的型態, 以這個例子來說, 也有可能是無法轉成String的型態, 在這邊就有可能出問題, 因為這寫法會被套用到所有的Array型態去, 不管是String array, In array或其他, 但大部分現實的例子(也就是我手上在寫的東西剛好碰到的例子), 我們只希望延伸出的東西只被套用到某些特定的型態, 這時候就得加上”where”來解決這問題:


以上面這例子, 雖然字串陣列跟整數陣列都有find這函式, 參數型態也一樣(Self.Generator.Element), 但這兩者的find的行為是不同的, 以字串陣列來說是找含有這一字串的, 而以整數陣列來說則是找一樣的, 這邊就是靠extension裡的where

這邊有個不一樣的地方是, Array被換成CollectionType了, 這是因為where裡面要判斷Element是不是我們所要的型態, 而Array是一個struct, CollectionType才是一個Protocol, 所以這邊我們用CollectionType來達到這目的

實在有點詭異的問題, 解決後又覺得實在有點蠢, 結果這問題足足花了我一個半小時才得到答案

問題是這樣的, 我拿了這一個字串 “‘x\\r\nx’” 來做解析, 一個個Character去檢查是不是”\r”, 如:

let x=“'x\\r\nx’”
for s in x.characters {
    if s == “\r” {
         print(”got you!”)
    }
}

邏輯上看起來沒啥問題, 但執行結果一直不如預期(沒印出”got you”), 把第四個字元substring出來它也不等於字串”\r”

百思不得其解, 結果最後在\r\n中間加一個空白後, 就得到預期的結果了, 原來….它是把”\r\n”整個當成一個字元!!!

在寫我的週末習作時, 為了抓取bitmap上所有的pixels時, 碰到這問題, 找了整個晚上才找到答案

首先, 要抓取bitmap上所有的pixel必須要使用到CoreGraphics裡的CGBitmapContextGetData, 想當然爾, 這是老舊的API, C API, 那用Swift可以去取用嗎? 參照Interacting with C APIs 這篇裡面有相關的Swift與C之間的對照

回到取pixels這目的, 我希望取到的值是一個整數陣列, 精確來說是[UInt32]的陣列, 但CGBitmapContextGetData回傳的是什麼呢? 查文件可知是UnsafeMutablePointer<Void> ,依照上面的文件來說, 對照到C則是"void *“, 文件裡面有提到:

When a function is declared as taking an UnsafeMutablePointer<Void> argument, it can accept the same operands as UnsafeMutablePointer<Type> for any type Type.

意思就是, 它也可以是整數陣列指標, 但, 怎麼轉回去呢? 這就是讓我整個晚上超崩潰的, 幹, 這文件沒寫!

搞半天, 才終於讓我找到答案:

let data = CGBitmapContextGetData(context)
    
let intData = UnsafeMutablePointer<UInt32>(data)
let intArray = Array(UnsafeBufferPointer(start: intData, count: bitmapByteCount))

原來必須先把它轉型成UnsafeMutablePointer<UInt32>, 然後再用UnsafeBufferPointer轉存到實際的陣列中(因為UnsafeBufferPointer是SequenceType, 才可以轉成Array), 當然也要給它陣列大小

但實際上這樣做對不對, 安不安全…還不知道, 找到答案寫下來先

主要是看Medium iOS版的搜尋畫面, 覺得他在UITextField裡顯示載入動畫還蠻不錯的, 不會有礙整個畫面的觀瞻, 所以就想做成像這樣:

其實這也不難, 靠的是UITextField的rightView(有rightView和leftView可用), 只要把rightView指定到一個UIActivityIndicatorView, 把rightViewMode設成.Always, 然後呼叫startAnimation就可以了:

https://github.com/julianshen/RAC-GitHubUserSearch/blob/aa3754787f2eec9e779493dffc65a65aa918c50a/GithubUserSearch/ViewController.swift#L39-L41

另外在loading結束後把rightViewMode設成.Never

https://github.com/julianshen/RAC-GitHubUserSearch/blob/master/GithubUserSearch/ViewController.swift#L52-L54

那怎判斷loading開始了呢? 那就要借重另一個東西, 在呼叫GitHubAPI前呼叫observe:

https://github.com/julianshen/RAC-GitHubUserSearch/blob/aa3754787f2eec9e779493dffc65a65aa918c50a/GithubUserSearch/ViewController.swift#L35-L44