2015年9月2日 星期三

iOS Push/Local Notification之原理

這次來介紹一下如何使用iOS上的Push Notification,舉凡Facebook上的訊息、Gmail的郵件通知都是此類。而首先就是要知道Notification有分成Local和Push兩種,前者像是行事曆的定時通知,後者就是前面講的。Local的通知好處理,可以指定日期和時間來通知:
 NSCalendar * calendar = [NSCalendar autoupdatingCurrentCalendar];

NSDateComponents * dateComponent = [NSDateComponents alloc] init];

[dateComponent setDay: item.day];  // item: self defined object

[dateComponent setMonth: item.month];

[dateComponent setYear: item.year];

[dateComponent setHour: item.hour];

[dateComponent setMinute: item.minute];

NSDate * date = [calendar dateFromComponents: dateComponent];



 UILocalNotification *localNotif = [[UILocalNotification alloc] init];

 if (localNotif == nil)

    return;

localNotif.fireDate =

[date
  dateByAddingTimeIntervalInterval:-(minutesBefore*60)];

 localNotif.timeZone = [NSTimeZone defaultTimeZone];

 localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in
  %i minutes.", nil),
           item.eventName, minutesBefore];



localNotif.alertAction = NSLocalizedString(@"View Details", nil);

localNotif.alerttitle = NSLocalizedString(@"Item Due", nil);

localNotif.soundName = UILocalNotificationDefaultSoundName;
      localNotif.applicationIconBadgeNumber = 1;

NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName
  forKey:ToDoItemKey];

localNotif.userInfo = infoDict;

[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];

那麼要設定Push Notification要怎麼做?首先要用Apple的Push Notification機制(簡稱APN),自己的Server發出通知後會傳到Apple的Server,之後再從Apple傳給使用者的App:


我覺得這樣做的原因是要保持通知的完整性和安全性,假如說你傳送有惡意訊息的封包給使用者豈不是不好?而建立整個連線流程如下圖所示,裝置先和APN建立TLS(Transport Layer Security)連線,之後取得憑證後TLS就算建立完成:

而Provider也是同樣要建立TLS連線和取得憑證:

而當憑證建立完成後App和APN, Provider的互動就是靠Token來完成,App若要使用Push Notificaition必須先行註冊至APN(從使用者那邊獲取通知許可),然後APN再回傳加密過的Token給Provider,之後該Token再從Device回傳到Provider,如下圖所示:
而在傳送Push Notification時會連同Token,並藉由裝置ID來回傳加密的通知內容給裝置:

到這邊整個Push Notification就算完成,接下來就是在AppDelegate.m加上程式碼:

// Remote notifications
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
    NSLog(@"Receive deviceToken: %@", deviceToken);
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
    NSLog(@"Remote notification error: %@", error.localizedDescription);
}
另外當收到了Notification後要做什麼事就先靠didReceiveRemoteNotification來完成,而handleActionWithIdentifier則是當使用者對Notification做出回應後你要採取什麼動作時,可用completionHandler的block來完成:

// When app is waked up, this method will be called
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
    // with push notification from remote server
    // When app is running in the foreground, this method will be called
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler{
    // Test for identifier with a sample indentifier
    if ([identifier isEqualToString:@"ACCEPT_IDENTIFIER"]) {
        [self handleAcceptActionWithNotification:userInfo];
    }
}
以上就是整個流程,至於說要註冊憑證的方法可參考這篇:popcorny的碎碎念

2015年8月31日 星期一

NSOperation及NSOperationQueue與GCD之比較

之前上Stanford iOS 7的課程時有介紹到GCD(Grand Central Dispatch),是一種比較低階的函數,讓code可以在不同的thread中跑。可是假如說我今天要檢查該thread的運作情況,好比說他的優先順序、是否還在運行等,這時候GCD能提供的設定就比較少,於是就需要NSOperation和NSOperationQueue啦~

在Mac OS X 10.6之前,NSOperation跟GCD是採用不同的運作模式,10.6之後NSOperation就改成運作在GCD之上了,換言之他是比較高階的API,但是同時也保有GCD的效能。使用NSOperation時有以下步驟:

  1. 繼承自NSOperation
  2. 覆寫main()
  3. 在main中建立autorelease pool(自動釋放池)
  4. 把code放到autorelease pool中
  5. 針對需求,在外部檢查isCancelled等屬性
至於NSOperation中有以下method:
  1. start:呼叫該函數來執行該operation,但如果沒有特別指定去哪個queue時將會在main queue執行,要特別注意
  2. dependency:可以設定兩個operation的相依性來設定先後順序。舉例來說我今天下載了一段音訊,要等下載完成後才能進行裁剪,GCD中的dispatch_barrier_async()有提供類似的功能
  3. priority:這點是我覺得比較有趣的地方,可針對特定的operation來設定setQueuePriority:,像是NSOperationQueuePriorityVeryLow
  4. completion block:當operation完成後可以在setCompletionBlock:^{}去做其他事情,好比說存檔等等
另外NSOperationQueue相較於NSOperation就更為簡單,僅需要alloc並init即可,之後可調用以下方法:
  1. concurrent operations:這點比較複雜,簡單來說一個queue可以有多個thread,而每個operation會分配到一個thread來執行。舉例來說我有一個queue,然後加入2個operations,這樣建立兩個thread對應到每個operation,所以我這個queue就會有兩個thread
  2. maximum number of concurrent operations:設定每個queue中最多只能有幾個operations來執行
  3. add operation:增加operation到queue,如果要釋放的用使用release方法
  4. pending operation:查詢在queue中有哪些operations。只有「待執行」和「正在執行」的operation才會在queue中。這部分可以用NSArray來保存operations,或是使用NSDictionary來查詢相關的operation
  5. pause(suspend)queue:就是停止queue
  6. cancel operation:如果說該operation尚未執行,呼叫cancel會把operation從queue中移除;如果是正在執行的operation,就要看該operation會不會去檢查isCancelled
  7. addOperationWithBlock:如果當你不想要有NSOperation的子類別卻又想要來queue中執行,就可在該方法中的block插入程式碼。這有點像GCD的block。不過要注意的是若要參考block外的物件,就必須傳入weak屬性的物件
最後綜合比較NSOperation和GCD,我覺得GCD比較簡單、直覺,但是可以操作的範圍不大(因為就是函數),可是其他物件可以繼承NSOperation,獲得了使用多線程的能力。所以我想當要操作物件時就用NSOperation,然後某些片段(下載等需求)就可以用GCD來解決。總之在iOS中多線程是個很重要的觀念,有這麼好用的功能就要好好用它啊~~

Protocol v.s. Delegate

最近剛好寫了一些跟NSOperation的東西,裡面用到許多的Protocol和Delegate,之前學到我們可以宣告一個Protocol,然後讓class去遵循它,可是那為何我們還需要有Delegate? Delegte不也是讓我們去遵循其中的方法來實作嗎?就像是我們在寫UITableView時都要有UITableViewDataSource和UITableViewDelegate來對資料和cell進行設定(因為View無法擁有自己的資料)。那麼兩個這麼像,Protocol和Delegate又有什麼差別呢?

答案是一定有差別的,不然你就不會一直看到這兩個名詞。先複習一下什麼事Protocol,假設當我宣告MyProtocol中有個aMethodInMyProtocol時,任何一個遵循MyProtocol就可調用該方法;另外也可讓實體變數遵循Protocol,譬如我的MyClass中有以下變數:

@property (nonatomic, strong) id  <myprotocol>  instanceThatImplementMyProtocol;

此時該instanceThatImplementMyProtocol就可調用MyProtocol的方法。此外宣告Protocol時也可再遵循其他Protocol,例如:

@protocol Foo
...
@end

只有當你實作了anotherProtocol後,然後你再實作Foo後你才可以說已經實作好該Foo Protocol。那麼在Delegate又是怎麼樣呢?

Delegate說起來是一種設計方法,全名叫做Delegation Design Pattern,Delegate要求遵循該delegation的class去實作Protocol的方法(可以說是一種「方法」),使得該class可以在特定時候使用該Protocol的method。對我來說,Protocol提供一個介面來讓大家共享裡面的方法,然後Delegate會讓用它的class去實現該方法,使用Delegate的好處是可以減弱耦合,達到共享的目的。

另外取自StackoverFlow的解答,我覺得看英文比較能懂:)
The class that uses the delegate knows that its delegate coforms to the protocol, so it knows that it can call the implemented methods at given times. 

2015年5月10日 星期日

The Pragmatic Programmer : from journeyman to master 書摘


我們,採集的只是石頭,卻必須時時刻刻展望未來的大教堂。
                                                                                                                —採石工人的信條

  1. 第一章:注重時效的哲學
    • 負責你的代碼,不要找蹩腳的藉口
    • 不要容忍破窗戶:有問題就及時修改,別累積技術債
    • 做團隊裡「做變化的催化劑」:提供大家改變的契機,讓大家追隨你而改變
    • 永遠記住大目標,要見樹並見林
    • 使質量成為需求問題:讓你的用戶參與權衡
    • 定期為你的知識資產做投資
    • 批判地分析你讀到的和聽到的
    • 你說什麼和你怎麼說同樣重要:交流、知道你要說什麼、瞭解聽眾、選擇文件的風格和美觀
  2. 第二章:注重實效的途徑
    • 不要重複你自己:和重用(Reuse)差別在,重複(Repeat)在於你做了同樣的事情,你應該只做一次卻做了很多次,造成時間和成本浪費;而重用則是再次利用優秀的程式碼來達到效率上的精進。
    • 讓重用變得容易(Make It Easy to Reuse)
    • 消除無關事物之間的影響:提升系統各組件的正交性(即降低耦合性)
      • 有正交性的好處:提高生產率、降低風險、項目團隊、設計
      • 編碼:讓代碼解藕、避免使用全域變數、避免相似的函數
    • 可撤銷性:不要存在最終決策,你的產品隨時隨地都有可能砍掉重練
    • 使用曳光彈來幫助你找到目標:和原型製作的差異在於曳光彈代碼是完整的,之後會構成最終系統的骨架的一部分
      • 用戶能及早看到能工作的東西
      • 開發者建構了一個他們能在其中工作的結構
      • 有了集成的平台
    • 為了學習而製作原型:原型所具備的元素有
      • 正確性
      • 完整性
      • 健壯性
      • 風格
    • 靠近問題領域編寫程式(Program Close to the Problem domain):選擇特定語言或是編寫特別定義過的程式來解決問題
    • 估算以免發生意外
      • 理解提問內容
      • 建立系統的模型
      • 把模型分解為組件
      • 給參數指定值
      • 估算項目進度
  3. 第三章:基本工具
    • 用純文本保存知識(Keep Knowledge in Plain Text)
    • 利用命列Shell的威力(Use the Power of Command Shells)
    • 用好一款編輯器:好的編輯器應該具有以下特性
      • 可配置:可高度客製化
      • 可擴展性
      • 可編程性
    • 總是使用原始碼控制工具
    • 要修正問題,而不是發出指責
    • 不要恐慌
      • 將你的數據視覺化
      • 跟蹤
      • 使用橡皮鴨:向別人解釋你的程式碼,讓別人像鴨子一樣一直點頭,直到問題出現
      • 消除過程
    • Select沒有問題
    • 不要假定,要證明
    • 學習一種文本操縱語言
    • 撰寫能撰寫程式碼的工具
  4. 第四章:注意時效的偏執
    • 你不可能寫出完美的軟體
    • 使用DBC按照合約設計:
      • 前條件
      • 後條件
      • 類別不變項
(未完.....)

2015年5月2日 星期六

數學思考(Think Mathematically) 筆記&閱讀心得

大二下過了這麼久,終於有時間好好讀一本書了。

這次借來的看的「數學思考」是由建中第49屆314班全體同學合譯後出版。其實會讀到這本書真的是因為大學的專業課程所需,像是統計、作業研究、電腦網路等,大部份都需要縝密的分析和邏輯能力,逐一抽絲剝繭後才能找到解決方案。既然想說自己不是讀書的料,那就只好強化數學能力,畢竟數學是所有學科的基礎嘛。

⟪筆記部分⟫

這本書主要提供了一套可依循的步驟來看待數學問題。作者引入了「要點思考」(Rubric Writing)這方法,主要步驟有:

  1. Stuck
  2. Aha!
  3. Check
  4. Reflect
第一個Stuck是指遇到困難,作者提到「遇到困難是件榮譽的事」,因為你可以從中發現到思維上的障礙和不足點,好比說我是不是「沒有搜集足夠的已知條件」、「我不確定我的所求」或是「所使用的工具不對」等等。另外在這階段時要多多使用「特殊化」和「一般化」的技巧,特殊化是指找出某個符合題目的例子,包含要找到特例,來輔以自己的想法;而一般化是問說「為什麼這樣做才是正確的」,就是確認自己的方法是否正確,之後能不能套用到類似性質的問題上。

第二個Aha!是指在解題時想到好點子就記錄下來,不管是靈光一閃或是經過縝密推論而得到結論,讓你很開心、愉悅,覺得離解答又更進一步的想法,都把它記錄下來,在往後的計算也可能再次用到。

第三個Check顧名思義就是檢查任何的計算或推理,你也可以用幾個例子檢查你所發現的法則(特殊化),最後再看看你的解法是否能解決原命題。

最後一個的Reflect是指想一下發生了什麼事。可以寫下重要的想法或是記憶中引人注目的時刻,然後確實地仔細思考在這一題中學到了什麼。

經過以上步驟就完成了要點寫作,再加上作者所提供的「進入」、「攻擊」、「回顧」,這張圖在解決數學問題上就顯得十分有用。

p.129 數學思考整體步驟

⟪心得⟫

這本書閱讀起來十分輕鬆,一個晚上就讀完了。看完後我第一個想法是:「假如我高中時代有看過這本書就好了。」從小時所接受的數學教育一直以來都是大量算題目、介紹很多工具和方法、熟練不同的解題技巧,到最後只要題目再有新的變化還是招架不住。我記得我高中有個老師每堂課就要我們背題目,尤其是矩陣那個章節更是要求。所以從教育來看,有兩個點很值得檢討:

1. 思考、辯駁、討論的訓練


台灣的數學教育一直很填鴨,課堂上我們總是希望老師教得愈多愈好,教更多解題技巧,學生做愈多題目愈好,可是卻常常缺乏討論的機會。近日才看到關於猶太人的教育相關文章,他們對於孩子放學後會問說:「你今天有沒有問了好問題?」而不是像台灣的家長問說:「今天考得如何?」「有學到什麼?」。愛因斯坦曾說想像力比知識重要,我把它解讀為「提出好問題比獲得知識更重要」因為提出問題時你要對該領域有一定了解,而且也代表你願意學習。

在我們的中小學教育中其實還蠻有這種練習的,讓學生發言、問問題,都是很好的機會。可是到了國高中,面臨升學考試壓力,這種討論的模式大量減少,問問題也傾向於很簡單的回答,非常可惜。

此外台灣人也很奇怪,就是會有菁英主義。菁英不是不好,可是大家總會希望說有個唸書高手出現,他總是自己唸書,然後硬是比別人高分,造就班上大家都自己念自己的,你要跟我討論課業門都沒有。可是假如你今天沒有外在幫助,缺乏討論和問問題,又怎能期待大家都變好呢?

2. 分析問題、深入思考的能力

從高中進入到大學後,教授開始用原文書上課。一方面是讓我們練英文整我們,另一方面是讓我們完整接觸洋人的想法和思考方式。可是台灣學生在這塊十分欠缺(對我就是其中一位),他們對於一個問題都可以有很完整的思考脈絡,就跟在數學思考裡面闡述的雷同,從已知慢慢推到未知,並且不注重於標準答案,而是自己的答案能不能說服他人。

在台灣考試考久了我們都希望出現那種十秒鐘就能解的題目,我們要求標準、快速的答案,所以才會有人覺得台灣都被標準答案綁死,導致我們長大後無法洞悉問題、提出有效且長遠的解決方案。

與其做大量訓練,不如給我們下一代有用的魚竿,教孩子如何思考,尤其是在數學這塊。



2015年4月6日 星期一

為了開發iOS App,要如何先學好Objective C? How to learn Objective-C well in order to develop iOS App?

這個寒假以來大概是我腦力消耗最多的時候了。自從學校的網頁設計課程教授網頁手機版設計後,我就打算改成iOS版,然後開始想買哪種Mac,後來在室友的幫助下買到一台二手但是狀況極佳的Macbook Pro,就這樣開始了學習Objective C的旅程。

而剛好買玩電腦前後,學校有了個做App的比賽,剛好就藉這機會報名參加。那時候剛好是期末考,要在期考時完成作品說明書也是很累人。他有分初審和決賽,結果初審只有格式不符的沒通過,對於自己的辛苦準備實在有點過意不去啊。

實際上也是到1/25之後才有時間學習,剛開始被MVC的觀念卡住,還有一些瑣碎的物件導向知識又會出來反咬我。所幸後來就慢慢克服。我使用的教材以Stanford CS193p Fall 2013-2014為主,查資料時使用"Programming in Objective-C Fifth Edition",對我來說這線上課程真的就有如在史丹佛上課一般,老師講得緊湊又有趣,非常推薦。

回歸正傳,要開發iOS App還是蠻建議從Objective-C開始,理由不外乎有:

  1. Objective-C現有的程式碼多,Stackoverflow上一堆相關問題回答
  2. Objective-C已經存在許久,穩定度經得起考驗
  3. 學會Objective-C轉換到Swift根本無痛轉移,只是有些微語法差異
學了一陣子後發現重要的還是基礎觀念,每次有Bug大概有一半以上都是基礎觀念在咬我。很多想說我這功能不知道怎麼寫、不知道有什麼函數可用,其實網路上很快就可以查到。StackoverflowApp Coda上就有很多可用的程式碼。可是你如果不知道函數要吃的參數型態、這邊該不該alloc、NSDictionary要怎麼用、MVC架構如何導入,那很快還是會卡住。

我個人蠻推薦高見龍大大在部落格上的說明,像是protocol、instancetype都介紹得很詳細。另外雖然說語法常常會讓人卡關,可是相對應的電腦科學背景也頗需要。像是我最近剛讓iOS去跟SQLite做query,一些資料庫背景知識有時候會跑出來咬我。

不過我相信只要有毅力,真的就會慢慢地把App做出來。加油!

2015年3月11日 星期三

跳脫舒適圈一直都是好事?

有人說,跳脫舒適圈,可以讓人成長,挑戰自己的極限,還能擴展視野,看到不一樣的東西。

現代產業強調跨出舒適圈,投入其他領域,找到不同的商機,甚至異業結盟都有可能,讓自己的專業無限擴展。換言之,窩在自己的舒適圈,一樣可以活得很好,但是可能就看不到外面更美好的世界了。

不過,大二後我對這個有了不同看法。第一,一個人如果突然跑到自己完全不熟悉的領域,而且完全都沒準備,這樣用「救火」來說是否比較貼切呢?一位法律背景突然要去開刀也不太可能。因此,應該說是要找個跟自己有「一定」關聯的產業去做,才不至於出現極大的反感。好比法律系的可以去打醫療官司,可以去對付專利蟑螂。縱使之前不知道這是什麼,一做下去仍是很有意義的。

反之,如果新領域僅是要你幫忙、替他人賣勞力、絲毫不給你發揮空間,而且又跟自己背景極大的無相關。此時,就要審慎評估了。當然也有不乏救火的人才,柯P就是很典型的例子,受到刺激之下出來競選成為台北市長。他在手術房的經驗給政治人物新的看法,做事快狠準,乾淨俐落。

而這當然建立在他願意去做,他也覺得很有意義。於是乎,我試著探討「跳脫舒適圈」是否要什麼前提?倘若我覺得沒意義還要去做嗎?萬一我覺得有意義,但是做了之後發現不是自己想要的呢?這都見仁見智。重點在於,至少要有意願去做,也要去模擬看看做了之後會發生什麼事情。

其實當自己做了全盤考量,就等於跳出了舒適圈了。

因此,我會覺得,要跳去的地方最好要跟自己領域有關聯,或是符合自己某些特質。這樣做起來才會有意義,縱使可能會很累,但是已經做過全盤考量,才有本事走下一步,也才讓「跳脫舒適圈」的意義完整體現出來。