US Visa初めおのiPhoneアプリ

なぜ、実際に



MacずiPhoneをお持ちで、モバむルアプリを䜜成しようずしないのですか どういうわけか間違っおいたす。 幞いなこずに、ここで問題が明らかになりたした。この問題はトピックに完党に適合し、非垞に有甚であるず同時に実装するこずもそれほど難しくありたせん。 そこで、Objective-CずCocoaに没頭したした。



免責事項



iOS向けの最初のアプリケヌションだけでなく、Objective-Cの最初のアプリケヌションも原則ずしお芚えおおいおください。 実装の品質や有効性のふりをするこずはありたせんが、Objective-CずiOSの䞀般的な開発のアむデアを提䟛する非垞に党䜓的な簡単な䟋を埗たず蚀いたいです。 特にこの蚀語をたったく知らない人のために。



免責事項2



この投皿はもずもず、英語- 米囜ビザMy First iPhone Appの The Pragmatic Bookshelf Magazineの蚘事ずしお公開されたした。 ここで発行されたロシア語版は、雑誌版の正確な翻蚳ではありたせん。少し埌で別のテキストずしお曞かれたためです。



ヒュヌストン 問題がありたす」



過去1幎間、ロンドンの倧䜿通でアメリカのビザを䜕床か申請しなければなりたせんでした。 特に私の堎合、「管理凊理」が必芁だず蚀われるたびに。 曞類はあなたから受け入れられたすが、ビザの代わりにバッチ番号を䞎え、圌らは定期的に自分のりェブサむトを芋るず蚀いたす。など。 公匏PDFのリンクをクリックしお、ファむルを開き、CTRL-Fを抌しお、番号バッチ番号を入力しおから進みたす。



自動化のアむデアがありたした-iPhone甚のアプリケヌションを䜜成しお、アプリケヌション番号を䞀床入力するず、ボタンを1回クリックするだけでビザ凊理ステヌタスを取埗できたす。 アプリケヌションは、PDFファむルをダりンロヌドしお解析し、アプリケヌションのデヌタを抜出できる必芁がありたす。



Windowsがある堎合はどうなりたすか



すべおが倱われるわけではありたせん。 Objective-Cは、CygwinたたはMinGWを介しおWindowsで実行できたす。 さらに、 GNUstepプロゞェクトは、Objective-Cを䜿甚しおWindowsでグラフィカルプログラムを䜜成するためにAppKitおよびFoundationラむブラリを䜿甚するこずを可胜にしたす。 残念ながら、この蚘事ではあたり深く掘り䞋げたせん。 コマンドラむンアプリケヌションのみを䜜成したす。 PDFをダりンロヌドしお解析できるようになりたす。 WindowsずMacの䞡方でアプリケヌションをビルドできたす。 その埌、このアプリケヌションのモゞュヌルをほずんど倉曎せずに䜿甚しお、iOS甚の本栌的なプログラムを䜜成したす。 しかし、悲しいかな、これはMac所有者のみが察象です。 もちろん、XcodeでiPhoneシミュレヌタヌの仮想マシンにHackintoshをむンストヌルしお実行するこずはできたすが、実際のiPhoneにHackintoshをアップロヌドするこずは、実際のMacなしでは成功したせん。



WindowsにGNUstepをむンストヌルする



次の2぀の玠晎らしい投皿を芋぀けたした。



Objective-CずiOS APIに粟通しおいただければ幞いです



私はObjective-Cに぀いおは䜕も知りたせんでしたが、メモリ管理に察する圌の珍しいアプロヌチに぀いおのうわさを陀いお、次の本をスクロヌルしなければなりたせんでした。



è­Šå‘Š  以䞋のリンクには、Amazonの個人アフィリ゚むトプログラム番号が含たれおいたす。 これらのリンクをクリックした埌に行われた可胜な賌入から、私はわずかな割合を埗るこずができたす。 これがあなたに合わない堎合は、リンクをクリックしないでください。たたは、カットアンドペヌストで手動でURLを「クリヌン」にしおください。 ご理解いただきありがずうございたす。



1. iOSプログラミングThe Big Nerd Ranch Guide、3 / eBig Nerd Ranch Guides







2. Objective-CプログラミングBig Nerd RanchガむドBig Nerd Ranchガむド







3. Objective-Cでのプログラミング第4版開発者甚ラむブラリ







たた、魔法のような無料のドキュメントが1぀ありたす-C ++からObjective-Cたで 。



そのため、タスクは3぀の䞻芁な郚分に分かれおいたす。



Objective-Cに慣れた埌、CたたはC ++の経隓の少ない開発者、特にUIの開発経隓がある堎合以前はDelphi / C ++ Builderをいじくり回しおいた、Objective- CずCocoaは簡単です。 メモリ管理の非垞に珍しい半手動モデル特にC ++のRAIIずJavaのガベヌゞコレクタヌに焊点を圓おるだけで十分です。 Objective-Cはメモリ自䜓を管理したすが、正しいリリヌスのためにオブゞェクトぞの参照の蚈算を制埡するのはあなた次第です。 原則を理解する必芁がありたす。そうしないず、メモリリヌクが避けられたせん。 それはたさに私が最初に持っおいたものでした。 幞いなこずに、Xcodeの優れたプロファむリングツヌルを䜿甚するず、䞻な問題をほが即座に特定できたす。



以䞋に、Objective-CずCocoaの初心者ずしおの個人的な䞻芳的な印象を瀺したす。 既に経隓があれば面癜いずは思えたせんが、そうでない堎合は面癜いず思いたす。



たず、クラスのメンバヌ関数の名前がObjective-Cでどのように圢成されるかを芋るのは興味深いです。 それはほずんど人間の蚀語に䌌おいたす。 英語で蚀うず、「デヌタの䞀郚で針を芋぀けお、可倉配列ずしお実装されたリストに結果を远加しおください」ず蚀うず、Objective-Cでは次のようになりたす。



+ (bool)findInPortion:(NSMutableData *)someData needle:(NSString*)aNeedle andAddTo:(NSMutableArray*)aList { ... }
      
      





このコヌドを巊から右に䞊から䞋に読むず、ほが完党な文が埗られたす。 正匏には、このメ゜ッドのフルネヌムはfindInPortion:needle:andAddTo:



匕数には名前が付けられ、その名前はメ゜ッドのフルネヌムの䞀郚です。 倉数匕数の名前 someData



、 aNeedle



、およびaList



をaNeedle



ず、実際に英語で曞くこずができたす。 もちろん、これはどちらかずいえば「冗長」なアプロヌチですが、コヌドを入力する際のXcodeの玠晎らしい予枬システムにより、これらすべおのタヌンをすばやく簡単に立ち䞊げるこずができたす。 たた、長い行を分割するずきの埓来の配眮は、パラメヌタの正匏名ずそれを衚す倉数を区切るコロンで発生するこずに泚意しおください。



Objective-Cには、メ゜ッドを呌び出すための型砎りな構文がありたす。 たずえば、次の代わりに



 NSMutableArray* list = NSMutableArray.alloc.init;
      
      





それは曞かれおいたす



 NSMutableArray* list = [[NSMutableArray alloc] init];
      
      





奇劙に芋えたすが、それは習慣の問題です。 繰り返したすが、入力するコヌド予枬システムを䜿甚するず、角括匧を物理的に詰め蟌むこずなく入力できたす。



Objective-CおよびCocoaは、習埗する必芁があるプログラミングパタヌンを積極的に䜿甚したす。 たずえば、デリゲヌト。 Cocoaのどこにでもありたす。 デリゲヌトは、コヌルバックを含むクラスです。 䞀緒に、個々の関数たたはメ゜ッドのバンドルを枡すず、必芁なすべおのコヌルバックを実装するオブゞェクトが1぀だけ枡されたす。 たずえば、暙準のNSURLConnectionクラスを䜿甚しおPDFをダりンロヌドしたした。 このクラスにはNSURLConnectionDelegateデリゲヌトのプロビゞョニングが必芁です。デリゲヌトのメ゜ッドは、ダりンロヌドプロセス䞭にさたざたなむベントが発生するず呌び出されたす。



それで、数週間の倜の本の埌ろの培倜で、最初のアプリケヌションのスケルトンをスケッチしたした。 しかし、それはMarlezonバレ゚の最初の郚分にすぎたせんでした。 次に、PDF圢匏を扱う必芁がありたした。



パヌサヌpdf



すでに述べたように、倧䜿通からの情報を含むファむルはPDF圢匏です。 この圢匏の説明は、Adobe Webサむトで入手できたす。 私はドキュメント「 PDF Reference 3版、バヌゞョン1.4 」を䜿甚したした。



PDF解析は、私の非垞にコンドミニアムに実装されおいたす。 デヌタは郚分的なものであるため、ドキュメントを郚分的に分析したす。 新しいデヌタをバッファに远加し、その䞭のPDF圢匏を解析しようずしたす。 たず、 stream



endstream



ずendstream



stream



endstream



囲たれたフラグメントを探したす。 そのような各ブロックの内容は、 zlib/inflate



介しおzlib/inflate



です。 その埌、これはすでに玔粋なテキストであり、もちろんPDFマヌクアップ蚀語を考慮しお、バッチ番号を探しおいたす。 番号が芋぀かったら、それを印刷しお次のブロックに進みたす。



パヌサヌの䞻な手順

  1. stream\r\n



    およびendstream\r\n



    タグによっお制限された、珟時点で受信したデヌタにブロックがある堎合、バッファヌから切り取り、 zlib/inflate



    介しお「展開」したす。
  2. 最初のステップで開かれおいないブロックはテキストです。 タグBT\r\n



    テキストの開始およびET\r\n



    テキストの終了で囲たれたフラグメントを芋぀ける必芁がありたす。 そのようなブロックをすべお芋぀け、それらを組み合わせお文字列のリストにしたす。
  3. 手順2で芋぀かった各行内で、かっこで囲たれおいない郚分文字列を削陀したす。 括匧で囲たれおいるのはサヌビス情報だけであり、必芁ありたせん。
  4. そのため、PDFからプレヌンテキストを抜出したした。 論理的には、このファむルの情報は、アプリケヌション番号バッチ番号、ステヌタス、日付の3぀の列を持぀テヌブルに線成されおいたす。 悲しいかな、その䞭でも、ペヌゞヘッダヌはただ出くわしおいたす。 それらを取り陀くために、珟圚の行がバッチ番号11桁のように芋える堎合、ステヌタス行ず日付行が埌に続くように芋えたす。 それらを受け取り、再び新しいバッチ番号を埅っおいたす。




私が蚀ったように、分析は特定のファむルに合わせお調敎されおおり、倧䜿通がそれを倉曎するず、すべおが壊れたす。 正芏衚珟を䜿甚する堎合でも、はるかに柔軟になりたすが、読者が自分で解決できるようにしおおきたす。



远加 。 この蚘事に取り組む過皋で、特別なWebサヌビスを䜜成し、簡単なURLを䜿甚しおアプリケヌションデヌタを受信できるアクセスし、PDFを解析するための「キッチン」党䜓が「クラりド」で行われるずいうアむデアが生たれたした。 ドブ博士は最近、このアプロヌチを説明する蚘事「Google App EngineによるGoのRESTful Webサヌビス 」を公開したした。 興味のある方は、このWebサヌビスを介しお動䜜するようにアプリケヌションを「仕䞊げ」たす。 䞀般的にはトリッキヌです。最初に、Webサヌビスにアクセスし、それから回答がある堎合はそこで終了し、そうでない堎合は、PDFをダりンロヌドしお解析する手順を自分で開始したす。



コマンドラむンアプリケヌション



そのため、PDFをダりンロヌドしおアプリケヌションから情報を抜出するアプリケヌションを䜜成するほずんどすべおのこずを知っおいたす。 アプリケヌションはコマンドラむンから動䜜したす。 GNUstepおよびClangを介しおMacおよびWindowsから構築できたす。 さらに、このアプリケヌションの゜ヌスファむルは、iOSのバヌゞョンを倉曎せずに䜿甚されたす。



ファむル



BatchPDFParser.h



このファむルには、アプリケヌションのステヌタスの曎新に関する情報を含むBatch



クラスの宣蚀ず、 findInPortion:needle:andAddTo:



メ゜ッドを実装するfindInPortion:needle:andAddTo:



クラスが含たれおいたすちなみに、これは静的クラスメ゜ッドです。行+



先頭を参照しおください。



 @interface Batch: NSObject { NSString *batchNumber, *status, *date; } @property (atomic, copy) NSString* batchNumber, *status, *date; @end @interface BatchPDFParser: NSObject + (bool)findInPortion:(NSMutableData *)data needle:(NSString* const)needle andAddTo:(NSMutableArray*)list; @end
      
      





BatchPDFParser.m



このファむルはパヌサヌPDFを実装したす。



 #import <Foundation/Foundation.h> #import "BatchPDFParser.h" #import "zlib.h" @implementation Batch @synthesize batchNumber, status, date; - (void) dealloc { [batchNumber release]; [status release]; [date release]; [super dealloc]; } @end @implementation BatchPDFParser
      
      





findInData:fromOffset:needle:



メ゜ッドは、 strstr()



型のstrstr()



特定のデヌタブロック内のサブストリングを怜玢したす。 怜玢はプリミティブであり、たずえばILCアルゎリズムを実装するこずで高速化できたす。



 + (int) findInData:(NSMutableData *)data fromOffset:(size_t)offset needle:(char const * const)needle { int const needleSize = strlen(needle); char const* const bytes = [data mutableBytes]; int const bytesLength = [data length] - needleSize; for (int i = 0; i < bytesLength;) { char const* const current = memchr(bytes + i, needle[0], bytesLength - i); if (current == NULL) return -1; if (memcmp(current, needle, needleSize) == 0) return current - bytes; i = current - bytes + 1; } return -1; }
      
      





isBatchNumber:number:



メ゜ッドは、文字列がバッチ番号かどうかを確認したす



 + (bool) isBatchNumber:(NSString*)number { long long const value = [number longLongValue]; return value >= 20000000000L && value < 29000000000L; }
      
      





findBatchNumberInChunk:needle:andAddTo:



は、 BT



およびET



タグで囲たれたフラグメントを怜玢したす。 カッコ内のテキストはハむラむトされ、芋぀かったものの䞭で特にアプリケヌション番号、ステヌタス文字列、日付文字列がハむラむトされたす。



 + (bool) findBatchNumberInChunk:(char const*)chunk needle:(NSString*)needle andAddTo:(NSMutableArray*)list { enum { waitBT, waitText, insideText } state = waitBT; enum { waitBatchNumber, waitStatus, waitDate } batchParserState = waitBatchNumber; NSMutableString* line = [[NSMutableString alloc] init]; Batch* batch = nil; bool found = NO; while (*chunk) { if (state == waitBT) { if (chunk[0] == 'B' && chunk[1] == 'T') { state = waitText; [line deleteCharactersInRange:NSMakeRange(0, [line length])]; } } else if (state == waitText) { if (chunk[0] == '(') { state = insideText; } else if (chunk[0] == 'E' && chunk[1] == 'T') { if (batchParserState == waitBatchNumber) { if ([self isBatchNumber:line]) { [batch autorelease]; batch = [[Batch alloc] init]; batch.batchNumber = line; batchParserState = waitStatus; } } else if (batchParserState == waitStatus) { batch.status = line; batchParserState = waitDate; } else if (batchParserState == waitDate) { batch.date = line; batchParserState = waitBatchNumber; if ([batch.batchNumber isEqualToString:needle]) { NSString* pair = [NSString stringWithFormat:@"%@\n%@", batch.status, batch.date]; [list addObject:pair]; NSLog(@"Found match: '%@' '%@' '%@'", batch.batchNumber, batch.status, batch.date); found = YES; } } [line autorelease]; line = [[NSMutableString alloc] init]; state = waitBT; } } else if (state == insideText) { if (chunk[0] == ')') { state = waitText; } else { char const c[2] = { chunk[0], 0 }; [line appendString:[NSString stringWithUTF8String:&c[0]]]; } } chunk += 1; } [line release]; [batch release]; return found; }
      
      





珟圚、䞻なメ゜ッドはfindInPortion:needle:andAddTo:



ここでは、 stream\r\n



およびendstream\r\n



タグで囲たれたフラグメントが匷調衚瀺され、内容がzlib/inflate



を介しお展開され、分析のためにfindBatchNumberInChunk:needle:andAddTo:



にfindBatchNumberInChunk:needle:andAddTo:



れたす。



 + (bool)findInPortion:(NSMutableData *)portion needle:(NSString*)needle andAddTo:(NSMutableArray*)list { static char const* const streamStartMarker = "stream\x0d\x0a"; static char const* const streamStopMarker = "endstream\x0d\x0a"; bool found = false; while (true) { int const beginPosition = [self findInData:portion fromOffset:0 needle:streamStartMarker]; if (beginPosition == -1) break; int const endPosition = [self findInData:portion fromOffset:beginPosition needle:streamStopMarker]; if (endPosition == -1) break; int const blockLength = endPosition + strlen(streamStopMarker) - beginPosition; char const* const zipped = [portion mutableBytes] + beginPosition + strlen(streamStartMarker); z_stream zstream; memset(&zstream, 0, sizeof(zstream)); int const zippedLength = blockLength - strlen(streamStartMarker) - strlen(streamStopMarker); zstream.avail_in = zippedLength; zstream.avail_out = zstream.avail_in * 10; zstream.next_in = (Bytef*)zipped; char* const unzipped = malloc(zstream.avail_out); zstream.next_out = (Bytef*)unzipped; int const zstatus = inflateInit(&zstream); if (zstatus == Z_OK) { int const inflateStatus = inflate(&zstream, Z_FINISH); if (inflateStatus >= 0) { found = found || [BatchPDFParser findBatchNumberInChunk:unzipped needle:needle andAddTo:list]; } else { NSLog(@"inflate() failed, error %d", inflateStatus); } } else { NSLog(@"Unable to initialize zlib, error %d", zstatus); } free(unzipped); inflateEnd(&zstream); int const cutLength = endPosition + strlen(streamStopMarker); [portion replaceBytesInRange:NSMakeRange(0, cutLength) withBytes:NULL length:0]; } return found; } @end
      
      





DirectDownloadViewDelegate.h



NSURLConnectionDelegate



デリゲヌトヘッダヌ



 @protocol DirectDownloadViewDelegate<NSObject> - (void)setProgress: (float)progress; - (void)appendStatus: (NSString*)status; - (void)setCompleteDate: (NSString*)date; @end
      
      





DirectDownloadDelegate.h



実際、デリゲヌト自䜓はNSURLConnectionDelegate



。



 #import "DirectDownloadViewDelegate.h" @interface DirectDownloadDelegate : NSObject { NSError *error; BOOL done; BOOL found; NSMutableData *receivedData; float expectedBytes, receivedBytes; id<DirectDownloadViewDelegate> viewDelegate; NSString* needle; } - (id) initWithNeedle:(NSString*)aNeedle andViewDelegate:(id<DirectDownloadViewDelegate>)aViewDelegate; @property (atomic, readonly, getter=isDone) BOOL done; @property (atomic, readonly, getter=isFound) BOOL found; @property (atomic, readonly) NSError *error; @end
      
      





DirectDownloadDelegate.m



そしおその実装



 #import <Foundation/Foundation.h> #import "DirectDownloadDelegate.h" #import "BatchPDFParser.h" @implementation DirectDownloadDelegate @synthesize error, done, found;
      
      





コンストラクタヌinitWithNeedle:andViewDelegate:



は、デリゲヌトを䜜成し、画面曎新タスクに䜿甚される別のデリゲヌトDirectDownloadViewDelegate



でパラメヌタヌ化したす。 ここで、ずころで、最初にデストラクタ(void) dealloc:







 - (id) initWithNeedle:(NSString*)aNeedle andViewDelegate:(id<DirectDownloadViewDelegate>)aViewDelegate { viewDelegate = aViewDelegate; [viewDelegate retain]; needle = [[NSString alloc] initWithString:aNeedle]; receivedData = [[NSMutableData alloc] init]; expectedBytes = receivedBytes = 0.0; found = NO; return self; } - (void) dealloc { [error release]; [receivedData release]; [needle release]; [viewDelegate release]; [super dealloc]; }
      
      





connectionDidFinishLoading:



メ゜ッドは、接続が完了するず呌び出されたす。



 - (void) connectionDidFinishLoading:(NSURLConnection *)connection { done = YES; NSLog(@"Connection finished"); }
      
      





connection:didFailWithError:



メ゜ッドは、ファむルのダりンロヌド時に゚ラヌをスロヌしたす。



 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)anError { error = [anError retain]; [self connectionDidFinishLoading:connection]; }
      
      





connection:didReceiveData:



、チャネルから新しいデヌタが受信されるず呌び出されたす。 このような各郚分をバッファヌに远加し、ダりンロヌドの進行状況むンゞケヌタヌを曎新しお別のデリゲヌトであるviewDelegate



を介しお、デヌタフラグメントをPDF圢匏で分離し、最埌に芋぀かったものを印刷したす。



 - (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)someData { receivedBytes += [someData length]; [viewDelegate setProgress:(receivedBytes / expectedBytes)]; [receivedData appendData:someData]; NSMutableArray* list = [[NSMutableArray alloc] init]; bool foundInCurrentPortion = [BatchPDFParser findInPortion:receivedData needle:needle andAddTo:list]; for (id batch in list) { NSLog(@"[%@]", [batch stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]); [viewDelegate appendStatus:batch]; } [list release]; found = found || foundInCurrentPortion; }
      
      





䜿甚する最埌のNSURLConnectionDelegate



デリゲヌトNSURLConnectionDelegate



は、 connection:didReceiveResponse:



ず呌ばれconnection:didReceiveResponse:



ヘッダヌを含むHTTP応答が受信されたずきに呌び出されたす。 「Content-Length」ヘッダヌから将来のファむルの長さを取埗しお、埌でダりンロヌドむンゞケヌタヌを曎新できるようにしたす。



 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)someResponse { NSDictionary *headers = [someResponse allHeaderFields]; NSLog(@"[didReceiveResponse] response headers: %@", headers); if (headers) { if ([headers objectForKey: @"Content-Length"]) { NSLog(@"Content-Length: %@", [headers objectForKey: @"Content-Length"]); expectedBytes = [[headers objectForKey: @"Content-Length"] floatValue]; } else { NSLog(@"No Content-Length header found"); } } }
      
      





NSURLConnectionDirectDownload.h



このファむルには、 NSURLConnection



クラスに远加するdonwloadAtURL:searching:viewingOn:



が含たれおいたす。 ここで興味深いのは、Objective-Cのカテゎリの抂念を通じお、新しいメ゜ッドを既存のクラスず「ミックス」できるこずです。 ここでは、 DirectDownload



カテゎリを远加したす。



 @interface NSURLConnection (DirectDownload) + (BOOL) downloadAtURL:(NSURL *)url searching:(NSString*)batchNumber viewingOn:(id)viewDelegate; @end
      
      





NSURLConnectionDirectDownload.m



さお、PDFダりンロヌドの最埌の郚分です。 donwloadAtURL:searching:viewingOn:



は接続を䜜成し、ダりンロヌドを開始したす。 次に、ダりンロヌドが完了するたでNSRunLoop



ルヌプで埅機したす。 このルヌプにより、アプリケヌションはダりンロヌドプロセス䞭のむベントに応答できたす。 このダりンロヌドはただGUIに関連付けられおいないこずに泚意しおください。 viewDelegate



デリゲヌトを䜿甚しお、アプリケヌションの「顔」ず通信したす。



 #import <Foundation/Foundation.h> #import "DirectDownloadDelegate.h" @implementation NSURLConnection (DirectDownload) + (BOOL) downloadAtURL:(NSURL *)url searching:(NSString*)batchNumber viewingOn:(id)viewDelegate { NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; DirectDownloadDelegate *delegate = [[[DirectDownloadDelegate alloc] initWithNeedle:batchNumber andViewDelegate:viewDelegate] autorelease]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate]; [request release]; while ([delegate isDone] == NO) { [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; } if ([delegate isFound] != YES) { [viewDelegate appendStatus:@"This batch number is not found."]; NSLog(@"This batch number is not found."); } NSLog(@"PDF is processed"); [connection release]; NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"yyyy/MM/dd HH:mm:ss"; NSString* lastUpdateDate = [dateFormatter stringFromDate:[NSDate date]]; NSLog(@"Last update at: %@", lastUpdateDate); [viewDelegate setCompleteDate:lastUpdateDate]; [dateFormatter release]; NSError *error = [delegate error]; if (error != nil) { NSLog(@"Download error: %@", error); return NO; } return YES; } @end
      
      





ViewController.m



既に述べたように、コマンドラむンアプリケヌションでは、コントロヌラヌにはスタブのみが含たれたす。これは、プログラムのフルバヌゞョンで埌で実装したす。



 #import <Foundation/Foundation.h> #import "DirectDownloadViewDelegate.h" #define IBAction void
      
      





空のViewController



スタブViewController



。



 @interface ViewController : NSObject <DirectDownloadViewDelegate> @end #import "NSURLConnectionDirectDownload.h"
      
      





ファむルをダりンロヌドするアドレス。



 static char const* const pdf = "http://photos.state.gov/libraries/unitedkingdom/164203/cons-visa/admin_processing_dates.pdf";
      
      





そしお、コントロヌラヌクラスのモック実装。



 @implementation ViewController
      
      





appendStatus:



テストコヌルバックappendStatus:



次の芁求の曎新が怜出されたずきに呌び出されたす。 ここでログむンするだけで、アプリケヌション党䜓で画面フォヌムを曎新したす。



 - (void) appendStatus:(NSString*)status { NSLog(@"appendStatus(): '%@'", [status stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]); // Some code is skipped here because not required for the command line mode. }
      
      





setProgress:



テストコヌルバックsetProgress:



デヌタの次のバッチを受け入れた埌にダりンロヌドむンゞケヌタヌを曎新する必芁があるずきに呌び出されたす。



 - (void) setProgress:(float)progress { // Some code is skipped here because not required for the command line mode. }
      
      





setCompleteDate:



テストコヌルバックsetCompleteDate:



PDF分析が完了するずsetCompleteDate:



たす。 ここでも、ログむンするだけです。



 - (void) setCompleteDate:(NSString*)date { NSLog(@"setCompleteDate(): '%@'", date); // Some code is skipped here because not required for the command line mode. }
      
      





さお、すべおを実行する最埌のメ゜ッドはupdateBatchStatus:



完党なプログラムでは、フォヌム䞊のボタンをクリックするず呌び出されたす。 ここでは、 main()



から呌び出されたす。



 - (bool) updateBatchStatus:(NSString*)batchNumber { NSURL *url = [[[NSURL alloc] initWithString:[NSString stringWithCString:pdf encoding:NSASCIIStringEncoding]] autorelease]; return [NSURLConnection downloadAtURL:url searching:batchNumber viewingOn:self]; } @end
      
      





main-cli.m



コマンドラむンから実行したす。



 #import <Foundation/Foundation.h> #import "DirectDownloadDelegate.h" @interface ViewController : NSObject <DirectDownloadViewDelegate> - (bool) updateBatchStatus:(NSString*)batchNumber; @end int main(int argc, char *argv[]) { @autoreleasepool { ViewController* viewController = [ViewController alloc]; [viewController updateBatchStatus:[NSString stringWithCString:argv[1] encoding:NSASCIIStringEncoding]]; [viewController release]; } return 0; }
      
      





これをすべお収集しお実行しおみたしょう。



Mac甚のMakefile







 files = \ ViewController.m \ BatchPDFParser.m \ NSURLConnectionDirectDownload.m \ DirectDownloadDelegate.m main-cli.m all: build run build: clang -o USVisaTest -DTESTING -framework Foundation -lz $(files) run: ./USVisaTest 20121456171
      
      





GNUstepのGNUmakefile



makefile



 include $(GNUSTEP_MAKEFILES)/common.make TOOL_NAME = USVisa USVisa_OBJC_FILES = \ ../ViewController.m \ ../BatchPDFParser.m \ ../NSURLConnectionDirectDownload.m \ ../DirectDownloadDelegate.m \ ../main-cli.m USVisa_TOOL_LIBS = -lz ADDITIONAL_OBJCFLAGS = -DTESTING CC = clang include $(GNUSTEP_MAKEFILES)/tool.make run: ./obj/USVisa 20121456171
      
      





make



ず入力しmake



。 Windows



 This is gnustep-make 2.6.2. Type 'mmake print-gnustep-make-help' for help. Making all for tool USVisa... Creating obj/USVisa.obj/../... Compiling file ViewController.m ... Compiling file BatchPDFParser.m ... Compiling file NSURLConnectionDirectDownload.m ... Compiling file DirectDownloadDelegate.m ... Compiling file main-cli.m ... Linking tool USVisa ...
      
      





実行しお、実際のアプリケヌションを確認できたす。



 make run
      
      





私は次のものを手に入れたした



 This is gnustep-make 2.6.2. Type 'mmake print-gnustep-make-help' for help. ./obj/USVisa 20121456171 2012-06-19 17:27:11.472 USVisa[3420] [didReceiveResponse] response headers: {"Accept-Ranges" = bytes; "Cache-Control" = "max-age=600"; Connection = "keep-alive"; "Content-Length" = 2237242; "Content-Type" = "application/pdf"; Date = "Tue, 19 Jun 2012 16:27:11 GMT"; ETag = "\"4b2ca3e41de5ba4ae45670e776edfc3b:1339778351\""; "Last-Modified" = "Fri, 15 Jun 2012 16:06:15 GMT"; Server = Apache; } 2012-06-19 17:27:11.604 USVisa[3420] Content-Length: 2237242 2012-06-19 17:27:12.093 USVisa[3420] Found match: '20121456171' 'send passport & new travel itinerary' '14-Jun-12' 2012-06-19 17:27:12.104 USVisa[3420] [send passport & new travel itinerary\n14-Jun-12] 2012-06-19 17:27:12.111 USVisa[3420] appendStatus(): 'send passport & new travel itinerary\n14-Jun-12' 2012-06-19 17:27:13.769 USVisa[3420] Connection finished 2012-06-19 17:27:13.774 USVisa[3420] PDF is processed 2012-06-19 17:27:13.961 USVisa[3420] Last update at: 2012/06/19 16:27:13 2012-06-19 17:27:13.972 USVisa[3420] setCompleteDate(): '2012/06/19 16:27:13'
      
      





したがっお、すべおが機胜したすPDFのダりンロヌドずパヌサヌ。 それでは、iOSバヌゞョンに取り掛かりたしょう。 悲しいかな、Macナヌザヌのみ。



画面レむアりト



アプリケヌションを非垞にシンプルにしたした。入力フィヌルド、ボタン、および曎新を衚瀺する堎所を持぀1぀のフォヌムです。







ダりンロヌドむンゞケヌタず回転スラむダヌが䞀時的に衚瀺されたす。



ViewController.h



これで、コントロヌラヌが完党に実装されたした。 TESTING



マクロを通じお、簡易版ず完党版を区別したした。



 #import <Foundation/Foundation.h> #import "DirectDownloadViewDelegate.h" #ifdef TESTING #define IBAction void @interface ViewController : NSObject <DirectDownloadViewDelegate> @end #else #import "ViewController.h" #endif #import "NSURLConnectionDirectDownload.h" static char const* const pdf = "http://photos.state.gov/libraries/unitedkingdom/164203/cons-visa/admin_processing_dates.pdf"; @implementation ViewController #ifndef TESTING @synthesize updateProgressView, batchNumberTextField, statusTextView, lastUpdatedLabel, updateButton; #endif NSString* const PropertiesFilename = @"Properties"; NSString *pathInDocumentDirectory(NSString *fileName) { NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentDirectory = [documentDirectories objectAtIndex:0]; return [documentDirectory stringByAppendingPathComponent:fileName]; }
      
      





appendStatus:



ぞのコヌルバックappendStatus:



ログを蚘録するだけでなく、画面フォヌムも曎新したす。



 - (void) appendStatus:(NSString*)status { NSLog(@"appendStatus(): '%@'", [status stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]); #ifndef TESTING if ([[statusTextView text] length] == 0) [statusTextView setText:@"Status:\n"]; [statusTextView setText:[[statusTextView text] stringByAppendingString:status]]; [statusTextView setText:[[statusTextView text] stringByAppendingString:@"\n"]]; #endif }
      
      





setProcess:



ダりンロヌドむンゞケヌタヌを曎新したす。



 - (void) setProgress:(float)progress { #ifndef TESTING updateProgressView.progress = progress; #endif }
      
      





setCompleteDate:



画面䞊のテキストボックスに曎新日を衚瀺したす。



 - (void) setCompleteDate:(NSString*)date { NSLog(@"setCompleteDate(): '%@'", date); #ifndef TESTING [lastUpdatedLabel setText:date]; #endif } - (bool) updateBatchStatus:(NSString*)batchNumber { NSURL *url = [[[NSURL alloc] initWithString:[NSString stringWithCString:pdf encoding:NSASCIIStringEncoding]] autorelease]; return [NSURLConnection downloadAtURL:url searching:batchNumber viewingOn:self]; }
      
      





次に、いく぀かのiOS固有の課題。 viewDidLoad:



メ゜ッドviewDidLoad:



画面がロヌドされ、䜿甚可胜な状態になったずきにシステムによっお呌び出されたす。 Xcode Interface Builderではフォヌムの蚭蚈時にそれらを倉曎できないため、ここでは手動で回転スラむダヌを䜜成し、ボタンず入力フィヌルドの2぀の芁玠の高さを調敎したす。



 #ifndef TESTING - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. spinnerActivityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; [spinnerActivityIndicatorView setColor:[UIColor blueColor]]; CGSize size = [[self view] frame].size; [spinnerActivityIndicatorView setCenter:CGPointMake(size.width / 2, size.height / 2 + 60)]; [self.view addSubview:spinnerActivityIndicatorView]; CGRect rect = [self.updateButton bounds]; rect.size.height += 10; [self.updateButton setBounds:rect]; rect = [self.batchNumberTextField bounds]; rect.size.height += 20; [self.batchNumberTextField setBounds:rect]; #ifdef DEBUG NSLog(@"DEBUG mode"); #endif }
      
      





viewDidUnload



, .



 - (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. }
      
      





shouldAutorotateToInterfaceOrientation:



. , .



 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation == UIInterfaceOrientationPortrait); } #endif
      
      





launchUpdate:



Update



. , .



 - (IBAction)launchUpdate:(id)sender { [self setProgress:0.0]; #ifndef TESTING [updateButton setEnabled: NO]; [updateProgressView setHidden:NO]; NSString* previousStatus = [statusTextView text]; [statusTextView setText:@""]; NSString* batchNumber = [batchNumberTextField text]; [spinnerActivityIndicatorView startAnimating]; BOOL const ok = [self updateBatchStatus:batchNumber]; [spinnerActivityIndicatorView stopAnimating]; if (!ok) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Internet connectivity problem" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; [alert show]; [alert release]; [statusTextView setText:previousStatus]; } [updateProgressView setHidden:YES]; [updateButton setEnabled: YES]; #endif }
      
      





saveProperties:



loadProperties:



. , .



 - (void) saveProperties { NSDictionary *props = [[NSDictionary alloc] initWithObjectsAndKeys: #ifndef TESTING batchNumberTextField.text, @"batchNumberTextField", statusTextView.text, @"statusTextView", lastUpdatedLabel.text, @"lastUpdatedLabel", #endif nil]; for (NSString* key in props) { NSLog(@"%@ - %@", key, [props objectForKey:key]); } NSString* filename = pathInDocumentDirectory(PropertiesFilename); if ([props writeToFile:filename atomically:YES] == NO) NSLog(@"Unable to save properties into file [%@]", filename); [props release]; } - (void) loadProperties { NSDictionary *props = [[NSDictionary alloc] initWithContentsOfFile:pathInDocumentDirectory(PropertiesFilename)]; for (NSString* key in props) { NSLog(@"%@ - %@", key, [props objectForKey:key]); } #ifndef TESTING [batchNumberTextField setText:[props objectForKey:@"batchNumberTextField"]]; [statusTextView setText:[props objectForKey:@"statusTextView"]]; [lastUpdatedLabel setText:[props objectForKey:@"lastUpdatedLabel"]]; #endif [props release]; } - (IBAction)textFieldReturn:(id)sender { #ifndef TESTING [sender resignFirstResponder]; #endif } -(IBAction)backgroundTouched:(id)sender { #ifndef TESTING [batchNumberTextField resignFirstResponder]; #endif } @end
      
      





それだけです . . ( Apple ).



GitHub — usvisa-app . .



:







!



, , . : 57x57 114x114 , 512x512 1024x1024 AppStore.



— The Great Seal of the United States .







PS



, AppStore «», , , , HTML5, . , . HTML5, . -, , Apple , - — Objective-C, .



PPS



iOS , .



All Articles