呃...

其實不會有什麼精深的東西,也就只是把我學習過程記錄一下...

2015年6月3日 星期三

UIActivityViewController

呃....又荒廢了 XD

總之,是有關UIAcitvityViewController的share。

UIActivityViewController就是iOS的share extension,似乎在iOS8才有的樣子,下面這篇文章有很好的範例,包含建立自己想要額外放入share extension的功能。

http://www.albertopasca.it/whiletrue/2012/10/objective-c-custom-uiactivityviewcontroller-icons-text/


2015年2月16日 星期一

Coding for Bugs: iOS Application Directory Structure : Document, Cache and tmp

iOS sandbox下的directory tutorial,雖然沒有特別深入,但是簡單易懂且也有說明該使用的時機。

覺得對App 可以用的資料夾感到混亂的可以看看。

Coding for Bugs: iOS Application Directory Structure : Document, Cache and tmp

2015年2月5日 星期四

open file by default viewer in iOS(UIDocumentInteractionController, QuickLook Framework )


Apple Official Document:
https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/DocumentInteraction_TopicsForIOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010409-SW1

Apple 提供UIDocumentInteractionController 來work with Quick Look Framework或其它App來開啟文件 - ( UIDocumentInteractionController Class Reference)
以及僅可preview的default browser的SDK - Quick Look Framework (Quick Look Framework Reference for iOS.)

UIDocumentInteractionController為了要可以preview file,所以包含Quick Look framework的功能。(那所以不要使用Quick Look ?單純使用Quick Look有沒有優點?)

單獨使用Quick Look Framework


1. 主要Class - QLPreviewController
2. 可以使用NavigationController push view,或是使用modal方式去push view ( presentModalViewController:animated:)
3. preview的title會自動使用URL的last path component,可以override  previewItemTitleaccessor去修改
4. Quick Look support的type: iWork, Office, RTF, PDF, Image, UTI, csv
5. 使用DataSource pattern 提供檔案來源,應類似tableview的方式

使用UIDocumentInteractionController的3 step:


1. 建立UIDocumentInteractionController的instance
2. 在UI上放一個file
3. 當tap這個file時,使用UIDocumentInteractionController去present它。

presentation的時候UIDocumentInteractionController提供三種UI:


1. preview file:
call presentPreviewAnimated:,並且必須implement documentInteractionControllerViewControllerForPreview: ,如果return值為nil或沒有其他view controller,則file不會被present

2. preview file或其他App開啟的選項:
call presentOptionsMenuFromRect:inView:animated: or presentOptionsMenuFromBarButtonItem:animated:
這邊還可以另外加入copy和print options (此兩種method與UIDocumentInteractionController的delegate有關)

3. 提示使用者要用其他App開啟:
 presentOpenInMenuFromRect:inView:animated: or presentOpenInMenuFromBarButtonItem:animated:

UIDocumentInteractionController也具有file sharing的功能,詳情見(see UI State Preservation in iOS Technology Overview and the DocInteraction sample code project)

UIDocumentInteractionController包含filename, type, url, icon等properties可供使用。
UIDocumentInteractionController另外有annotation property可提供客製化information給其他App。
UIDocumentInteractionController會自動handle view的dismiss,但也有另外提供兩種方式來進行手動關閉view。dismissMenuAnimated: or dismissPreviewAnimated:

Registering the File Types Your App Supports


如果在Info.plist有註明自己的App有support特定格式檔案的話,則其他App若需要開啟此格式檔案則可以呼叫本身的App。若要讓自己的App可以support其他App去開啟檔案,則需要在Info.plist中使用 CFBundleDocumentTypes。
CFBundleDocumentTypes是dictionary型態,有四個key:
1. CFBundleTypeName - 文件型態的名稱
2. CFBundleTypeIconFiles - 是array型態,用來放文件icon的image
3. LSItemContentTypes - contains an array of strings with the UTI types that represent the supported file types in this group.
4. LSHandlerRank - 說明App是文件的owner或是只能打開檔案而已

Apple官方文件中的XML例子如下:
<dict>
   <key>CFBundleTypeName</key>
   <string>My File Format</string>
   <key>CFBundleTypeIconFiles</key>
       <array>
           <string>MySmallIcon.png</string>
           <string>MyLargeIcon.png</string>
       </array>
   <key>LSItemContentTypes</key>
       <array>
           <string>com.example.myformat</string>
       </array>
   <key>LSHandlerRank</key>
   <string>Owner</string>
</dict>

當其他App 使用UIDocumentInteractionController  call自己的App時,其他App會丟information到  application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions:,若有自行定義的document type,則需override method去處理收到的information並初始化自己的App。
丟來的資訊存放在上述方法中dictionary型態的options,有幾個key:
1. UIApplicationLaunchOptionsURLKey  - 檔案的URL
3.UIApplicationLaunchOptionsSourceApplicationKey - 想開檔案的其他App的bundle identifier
3.UIApplicationLaunchOptionsURLKey - 其他App傳來的plist file

以下是非常簡單的Quick Look Framework 範例


#import "ViewController.h"
#import <QuickLook/QuickLook.h>

@interface ViewController ()<QLPreviewControllerDataSource,QLPreviewControllerDataSource>{
    NSURL *url;
}
@property (nonatomic, strong) QLPreviewController *qlController;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *strURL=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    strURL=[strURL stringByAppendingPathComponent:@"簡介Doxygen.doc"];
    url=[NSURL fileURLWithPath:strURL];

    // Do any additional setup after loading the view, typically from a nib.
    self.qlController=[[QLPreviewController alloc]init];
    self.qlController.dataSource=self;
}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    [[self.qlController navigationItem] setRightBarButtonItem:nil];
    [self presentViewController:self.qlController animated:YES completion:nil];
}

-(NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller{
    return 1;
}
-(id<qlpreviewitem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index{
    return url;
}
@end

再來是UIDocumentInteractionController 的範例

#import "ViewController.h"

@interface ViewController ()<UIDocumentInteractionControllerDelegate>{
    NSURL *url;
}
@property(nonatomic,strong) UIDocumentInteractionController *docController;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *strURL=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    strURL=[strURL stringByAppendingPathComponent:@"簡介Doxygen.doc"];
    url=[NSURL fileURLWithPath:strURL];
    self.docController=[UIDocumentInteractionController interactionControllerWithURL:url];
    self.docController.delegate=self;
    
    // Do any additional setup after loading the view, typically from a nib.
}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    [self.docController presentPreviewAnimated:YES];
}

-(UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller{
    return self.navigationController;
}
-(void)documentInteractionControllerWillBeginPreview:(UIDocumentInteractionController *)controller{

}
@end

以上兩個範例都很簡單,官方文件也有其他範例可以看。 比較要注意的兩點:
1. 使用QLViewController時,若有使用NavigationController,可以用navigation的push
2. UIDocumentInteractionController 在delegation method的return要return navigationController,若是不用navigation則直接return self就可以。

最後重申一次,QLViewController只能preview,而UIDocumentInteractionController 則是可以support其他App或做比較細節一點的設定,像是可以設定action sheet讓 user選擇要preview、使用其他App開啟或是view dismiss要進行什麼動作之類的。
若只是要preview,QLViewController就可以滿足,其他狀況 UIDocumentInteractionController 還是比較方便。

2015年2月4日 星期三

Suggested App at lockscreen

在FB上的討論串看到的,記錄一下。

http://stackoverflow.com/questions/25897219/can-i-get-my-ios-app-to-appear-on-the-lower-left-corner-of-the-lock-screen
http://stackoverflow.com/questions/26082414/make-app-appear-as-ios-8-suggested-app-at-lockscreen

第一篇是說明目前在lock screen上有兩種情況可以收到message:
1. 裝有App的情況下,由beacon去broadcast message。
2. 未裝有App的情況下,由Apple去broadcast message。

針對上述第二點則有人回答在第二篇,如何可以透過Apple去做這件事:
要透過Apple的report bug頁面去申請,但必須要符合一些限制,App必須直接與環境/地理資訊有相關聯才行。
以下節錄自第二個連結內文,是其他developer去問然後Apple回應的原文:

========
Here is the answer from Apple Tech Support - read it and weep!
Location relevant Apps are recommended on the lock screen and in the App Store based on a number of factors, including their popularity at the location, and there is no mechanism or process by which an App developer can add their App to the list of recommended Apps. However, if your iOS App is directly related to the location or venue (such as a mall guide iOS App which is published by the mall owner or operator), and you believe your iOS App’s relevance warrants it being included the list of recommended Apps, then you are encouraged to submit an enhancement request via http://bugreport.apple.com/, and request that Apple consider whether your iOS App should be included in the list of recommended Apps for that location. You should provide as much detail in the ’enhancement request' about the App and venue/location as possible, such as the App ID, App Name, URL to the App on the App Store, venue name, venue location (address & lat/lon), the relevance (if any) of your iOS App to the venue/location, and relationship (if any) between you as the developer and the owner/operator of the venue/location. Submission of an enhancement request does not guarantee that a particular iOS App will be added to the list of recommended Apps for a venue or location, and the sole feedback from Apple on the request will come through the http://bugreport.apple.com/bug report system.


2015年1月25日 星期日

Objectvie-C review (features, Delegation, protocol, category, KVC)

也是很久前的draft 囧 都是一些老生常談講到爛到不能再爛的東西,但是每次都會忘掉,就單純做個紀錄,因此此篇非常雜亂!
===================================

Obj-C introduction
dynamic language - dynamic type (id) and binding, a little more effort than C++, but has more flexible
http://rintarou.dyndns.org/2010/12/09/objective-c-淺談/
GC - suspend on iOS & OS X 10.8 (variety of algorithm)
property - access method(auto synthesize), encpulation, dot syntax, attribute configuration, KVO
hint:internal access - directly get and using setter to set instance variables

cocoa vs. cocoa touch
http://www.cnblogs.com/kkun/archive/2012/04/25/2470166.html

protocol
即類似java interface,主要是由於OC本身沒有多重繼承機制,所以protocol
可讓Class之間互相溝通與使用對方的method,protocol最頻繁被使用於
delegate 與 data source DP
Delegation
如我的blog所說,是一種DP讓Class之間可以互動並可委託其他Class執行
自己不希望執行的功能。其class互動與溝通有其他很多方法可以替代,但
是此方法可以充分發揮MVC精神,例如View不應該處理的MC,可透過此 
DP完成這種概念。
由於protocol具@optional與@require(default)兩種method的設定,因
此在委託方中implement方法的使用時,要記得當使用@optional的方法時
,要多利用introspection機制確定方法有沒有被conform。
在使用introspection機制時也要再另外注意一件事,就是當method被頻繁
的call or callback時,應該想辦法將introspection機制與頻繁被執行的部分
分隔開來,避免浪費多餘的效能。(performance optimal)
優化機制作法:
override delegate setter,在當中先進行introspection機制並將結果
cache,未來直接使用cache的結果來判斷是否可以執行method即可。

category
在其他language中會發生繼承下來的方法不夠用或是沒找到自己想用的,這時
多數作法是在subclass增加方法,或是被經常用到的方法就新增到superclass裏
,這種情況下若是不複雜的且是自己implement的superclass可能不會造成太大
困擾,但若是接手他人的code或是superclass已經發展到很複雜並且太多subclass
在使用,若不想動到原本的code並且不會讓每個subclass都要繼承新增的method
,category所實現的額外加入method就非常的好用。
若沒有取名的category稱為類別延伸,通常用於隱藏不想public的instance variable
。需注意累目的方法名稱要加上prefix,避免override原始method
其存在主要是實現clean code 想法,透過category可對method分門別類,而不會
導致某個class既肥大又雜散


Observer Desgin Pattern:
KVC - KVO
http://rintarou.dyndns.org/2011/07/22/objective-c-programming-筆記-2/

KVC - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/Overview.html#//apple_ref/doc/uid/20001838-SW1

透過String格式的identifier間接access object properties/instance variable,KVC與@property一樣,會自動使用accessor。
若是instance variable,則會自動建立accessor去存取variable,但因為KVC的判斷方法是 找外內部的accessor ->找變數 ->找不到
,因此一般都會為instance variable套上property,降低效率成本。
符合KVC格式 - 
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/Compliant.html#//apple_ref/doc/uid/20002172-BAJEAIEE

Attribut, to-one and Many format

無論Attribute或to-one,找不到key會引用valueForUndefinedKey:回傳一個exception,可自行override這個函數。

attribute :

Simple value,譬如String, bool, int, float, NSNumber等,key path可直接指定variable name,當attribute非object時(scalar type or struct),需implement setNilValueForKey:。
若是scalar type時,setValue需使用NSNumber wrap,可使用literal syntax,另外要注意的是當傳入數字字串時是可以使用的,會自動call NSString的method幫忙轉成NSNumber,但須注意應避免此種狀況,減少效率成本。
若是struct則一定需要implement setter和getter,且setValue時需使用NSValue wrap C struct再傳入,傳入的值會自動unwrap,且在obsevser被call時的change parameter一樣要使用NSValue的getValue去unwrap。
(scalar type or struct) - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/DataTypes.html#//apple_ref/doc/uid/20002171-BAJEAIEE

to-one : 

Object。使用方法 ex: [Object setValue:@“” forKeyPath:@“firstName”];

to-many : (NSArray, NSSet, NSDictionary)

1. collection不能使用nil當值,所以value不可直接傳nil,但是可以使用NSNull,會建立一個nil的instance放入。
2. 回傳的非單一值,是同identifier的allvalue
3. 分為indexed (Array )and unorder(set)

KVC implement detail: 
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/SearchImplementation.html#//apple_ref/doc/uid/20000955-CJBBBFFA
原則:
1 .先找是否有@property auto-synthesize的 method(可以被override)或是否有自己根據rule而implement method
2. 若設定可以直接取值,則直接找是否有定義值並取值
3. 都沒有,auto invokes 找不到key的method

Encrypt & Decrypt context in iOS by CommonCryptor and communicate to PHP mcrypt (blowfish, AES, DES, 3DES...)

這是一篇好幾個月前的draft,久到我只記得怎麼做,卻不知道為什麼會這樣做的文章 XD

==============
繼上次使用3rd-party的blowfish加密library,這次換使用iOS public source 中的CommonCryptor library。用好好的幹嘛換?原因是chilkat的試用期限到了 囧,這裡想必會有一個疑問:如果有試用期,明知有試用期,為什麼還使用?因為被誤入歧途,說什麼那個限制是假的......Orz。總之,也是多了解一種3rd-party的library,多學到一些東西囉。

在學習CommonCryptor的過程中,其實還有不少內容並不是非常了解,不過至少可以work了 XD
==============
(以下是好幾個月後補的,所以Blowfish的algorithm細節已經記不得了,看到寫死的部份若有疑惑,請查Blowfish的algorithm XD)

這個method是加密的前置作業,在這當中可以看到我對傳入的String轉成Data並加大Data的長度,這是有關於encrypt的input長度,由於PHP以及iOS兩邊實作的Blowfish algorithm本身雖然一樣,但在input以及output可能多少有些不同,詳情要自行到wiki去查查blowfish的algorithm.. XD 但總之長度必須要符合兩邊Blowfish的要求,才不會導致encrypt或是未來decrypt出問題。
另外我對實際做encrypt或decrypt的部分另外寫成一個method,稍後會提到,總之若沒意外,計算完data length後丟入encrypt會return data出來,最後將data convert成String丟回去即可。噢,最後一樣可以看到我對加密後的data長度減8,這也是因為Blowfish的加密algorithm的制式output,詳情一樣請wiki blowfish XD

-(id) encode:(NSString *)str{
    NSMutableData *dataStr=[[NSMutableData alloc] initWithData:[str dataUsingEncoding:NSUTF8StringEncoding]];
    if (([dataStr length]%8)!=0) {
        [dataStr increaseLengthBy:(8-[dataStr length]%8)];
    }
    NSData *encryptData=[self crypt:kCCEncrypt cinpherText:dataStr];
    NSString *returnStr=[BitHelper bytesToHex:(unsigned char*)[encryptData bytes] length:(int)[encryptData length]-8];
    return returnStr;
}

這個method則是decrypt的前置作業,當這個method被呼叫的時候,會傳入一串16進位的字串,但此字串資料實際上是byte,所以需convert回去byte型態。同樣的由於PHP以及iOS兩邊Blowfish的input與output不同,所以output的長度我也必須確認,但是相比iOS這邊encrypt後丟給PHP需顧慮PHP看不看得懂,PHP進行encrypt後的output反而只需要確認是否有額外增加長度而填入dirty data即可。因此除了0或1的情況,其餘只要確認收到資料格式最後是否有dirty data並捨棄即可。

-(id) decode:(NSString *)str{
    NSMutableString *decodeStr=[[NSMutableString alloc]initWithData:[self crypt:kCCDecrypt cinpherText:[BitHelper hexToBytes:str]] encoding:NSUTF8StringEncoding];
    //dirty padding in data, for example:[{"m_id":"44"}]@@@, after "]" character exist some bytes and print can't see that, so need to clear they.
    if ([decodeStr length]!=8) {
        NSRange range=[decodeStr rangeOfString:@"]" options:NSBackwardsSearch];
        NSUInteger datalength=range.location+range.length;
        NSMutableData *returnData=[[NSMutableData alloc]initWithData:[self crypt:kCCDecrypt cinpherText:[BitHelper hexToBytes:str]]];
        [returnData setLength:datalength];
            return returnData;
    }
    return [self crypt:kCCDecrypt cinpherText:[BitHelper hexToBytes:str]];

}

重頭戲來了,在這method中可以看到使用了CCCryptorStatus的variable以及CCCrypt的C function,這是由iOS的CommonCryptor library所提供的,CCCryptorStatus是用來判斷加解密的status是成功或失敗,最重要的是在CCCrypt,當把所有參數丟入CCCrypt並成功加解密後就會取得Bytes型態的資料,回傳後再交給NSData處理即可,這樣就完成了iOS的加解密功能了!

P.S.:
1.
由於是因為和PHP串接,所以會需要自己確認data length以及有沒有dirty data,若是encrypt以及decrypt的input與output都是來自iOS的CommonCryptor的話,就不需要這些步驟了,因為iOS的CommonCryptor會看得懂自己做的input與output。

2.
CCCrypt必須放入很多個參數,我有的已經有點忘了,我就其中記得的說明一下:
operation - 在encode或decode裏可看到,也就是告知CCCrypt我現在要做decrypt或encrypt
kCCAlgorithmBlowfish - trace這個常數就可以知道,這是告知CCCrypt要用什麼algorithm去做,即使是要使用別的加解密演算法,像是AES, DES或3DES這些加密演算法,其實這個function都已經做好,是一個非常方便的library!
kCCOptionPKCS7Padding - 就是data的padding
[KeyAscii bytes] - Blowfish用來加解密的key,由於是C function,所以必須丟入bytes型態。
[ivAscii bytes] -  Blowfish另一把key,印象中是在上一把key與data XOR後,再XOR這把key做二度加密 (不一定正確,細節忘了 XD)
[data bytes] - 愈加解密的資料
[datalength]- 資料的長度
buffer - 資料加解密後會被放在這個buffer中,回傳後記得free掉自行malloc的buffer。
bufferSize - 告知buffer有多大
&numBytesCrypted - 實際收到的buffer的長度

-(NSData *)crypt:(uint32_t) operation cinpherText:(NSData *) data{
    NSData *keyAscii = [@"5399602753996027" dataUsingEncoding:NSUTF8StringEncoding];
    NSData *ivAscii = [@"53996027" dataUsingEncoding:NSUTF8StringEncoding];
    size_t bufferSize = 2048;
    size_t datalength=[data length]+8;
    if (operation==kCCEncrypt)
        datalength=[data length];
    void *buffer = malloc(bufferSize);
    size_t numBytesCrypted = 0;
    CCCryptorStatus destatus = CCCrypt(operation, kCCAlgorithmBlowfish,
                                       kCCOptionPKCS7Padding,
                                       [keyAscii bytes], [keyAscii length],
                                       [ivAscii bytes],
                                       [data bytes], datalength, /* input */
                                       buffer, bufferSize, /* output */
                                       &numBytesCrypted);
    
//    NSLog(@"get %lu %lu %p %d", bufferSize, numBytesDecrypted, data, destatus);
    if (destatus == kCCSuccess && operation==kCCDecrypt) {
        //the returned NSData takes ownership of buffer and will free it on dealloc
        return [NSMutableData dataWithBytesNoCopy:buffer length:numBytesCrypted];
    }else if(destatus == kCCSuccess && operation==kCCEncrypt)
        return [NSMutableData dataWithBytesNoCopy:buffer length:numBytesCrypted];
    
    free(buffer); //free the buffer;
    return nil;
}

================
老實說這篇其實有點慚愧,現在寫出來只是為了消化一些draft,我原本想說可以完整的解釋每個細節以及blowfish algorithm再寫出來,但最後還是沒有完成,可能是後來的工作在加解密這塊沒什麼需求,所以我就懶了吧.. XD 希望自己未來有一天可以補上這邊的knowledge Orz

2015年1月23日 星期五

Design Pattern in iOS (Singleton, notification, delegation, KVC-KVO, class cluster)

嗚哇~好幾個月沒發表啦 囧 其實是一直寫在draft而且整理不完,哭哭 Orz
先分享最近工作上的presentation slide好了,不過是簡單屬於Developer都知道
的東西就是 XD

http://www.slideshare.net/YiShouChen/design-patterns-in-ios

就加減看一下啦 XD