-
Notifications
You must be signed in to change notification settings - Fork 1
/
README.html
698 lines (480 loc) · 42.5 KB
/
README.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>README</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
/*
This document has been created with Marked.app <http://markedapp.com>, Copyright 2013 Brett Terpstra
Content is property of the document author
Please leave this notice in place, along with any additional credits below.
---------------------------------------------------------------
Title: GitHub
Author: Brett Terpstra
Description: Github README style. Includes theme for Pygmentized code blocks.
*/
html,body{color:black}*:not('#mkdbuttons'){margin:0;padding:0}#wrapper{font:13.34px helvetica,arial,freesans,clean,sans-serif;-webkit-font-smoothing:subpixel-antialiased;line-height:1.4;padding:3px;background:#fff;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px}p{margin:1em 0}a{color:#4183c4;text-decoration:none}#wrapper{background-color:#fff;padding:30px;margin:15px;font-size:14px;line-height:1.6}#wrapper>*:first-child{margin-top:0!important}#wrapper>*:last-child{margin-bottom:0!important}@media screen{#wrapper{box-shadow:0 0 0 1px #cacaca,0 0 0 4px #eee}}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:bold;-webkit-font-smoothing:subpixel-antialiased;cursor:text}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px;color:#333}h4{font-size:16px;color:#333}h5{font-size:14px;color:#333}h6{color:#777;font-size:14px}p,blockquote,table,pre{margin:15px 0}ul{padding-left:30px}ol{padding-left:30px}ol li ul:first-of-type{margin-top:0}hr{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxAEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC) repeat-x 0 0;border:0 none;color:#ccc;height:4px;padding:0}#wrapper>h2:first-child{margin-top:0;padding-top:0}#wrapper>h1:first-child{margin-top:0;padding-top:0}#wrapper>h1:first-child+h2{margin-top:0;padding-top:0}#wrapper>h3:first-child,#wrapper>h4:first-child,#wrapper>h5:first-child,#wrapper>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p,ul li>:first-child,ol li>:first-child{margin-top:0}dl{padding:0}dl dt{font-size:14px;font-weight:bold;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}table{border-collapse:collapse;border-spacing:0;font-size:100%;font:inherit}table th{font-weight:bold;border:1px solid #ccc;padding:6px 13px}table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}code,tt{margin:0 2px;padding:0 5px;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px;font-family:Consolas,'Liberation Mono',Courier,monospace;font-size:12px;color:#333}pre>code{margin:0;padding:0;white-space:pre;border:0;background:transparent}.highlight pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:0}.poetry pre{font-family:Georgia,Garamond,serif!important;font-style:italic;font-size:110%!important;line-height:1.6em;display:block;margin-left:1em}.poetry pre code{font-family:Georgia,Garamond,serif!important;word-break:break-all;word-break:break-word;-webkit-hyphens:auto;-moz-hyphens:auto;hyphens:auto;white-space:pre-wrap}sup,sub,a.footnote{font-size:1.4ex;height:0;line-height:1;vertical-align:super;position:relative}sub{vertical-align:sub;top:-1px}@media print{body{background:#fff}img,pre,blockquote,table,figure{page-break-inside:avoid}#wrapper{background:#fff;border:0}code{background-color:#fff;color:#333!important;padding:0 .2em;border:1px solid #dedede}pre{background:#fff}pre code{background-color:white!important;overflow:visible}}@media screen{body.inverted{color:#eee!important;border-color:#555;box-shadow:none}.inverted #wrapper,.inverted hr .inverted p,.inverted td,.inverted li,.inverted h1,.inverted h2,.inverted h3,.inverted h4,.inverted h5,.inverted h6,.inverted th,.inverted .math,.inverted caption,.inverted dd,.inverted dt,.inverted blockquote{color:#eee!important;border-color:#555;box-shadow:none}.inverted td,.inverted th{background:#333}.inverted h2{border-color:#555}.inverted hr{border-color:#777;border-width:1px!important}::selection{background:rgba(157,193,200,0.5)}h1::selection{background-color:rgba(45,156,208,0.3)}h2::selection{background-color:rgba(90,182,224,0.3)}h3::selection,h4::selection,h5::selection,h6::selection,li::selection,ol::selection{background-color:rgba(133,201,232,0.3)}code::selection{background-color:rgba(0,0,0,0.7);color:#eee}code span::selection{background-color:rgba(0,0,0,0.7)!important;color:#eee!important}a::selection{background-color:rgba(255,230,102,0.2)}.inverted a::selection{background-color:rgba(255,230,102,0.6)}td::selection,th::selection,caption::selection{background-color:rgba(180,237,95,0.5)}.inverted{background:#0b2531;background:#252a2a}.inverted #wrapper{background:#252a2a}.inverted a{color:#acd1d5}}.highlight .c{color:#998;font-style:italic}.highlight .err{color:#a61717;background-color:#e3d2d2}.highlight .k,.highlight .o{font-weight:bold}.highlight .cm{color:#998;font-style:italic}.highlight .cp{color:#999;font-weight:bold}.highlight .c1{color:#998;font-style:italic}.highlight .cs{color:#999;font-weight:bold;font-style:italic}.highlight .gd{color:#000;background-color:#fdd}.highlight .gd .x{color:#000;background-color:#faa}.highlight .ge{font-style:italic}.highlight .gr{color:#a00}.highlight .gh{color:#999}.highlight .gi{color:#000;background-color:#dfd}.highlight .gi .x{color:#000;background-color:#afa}.highlight .go{color:#888}.highlight .gp{color:#555}.highlight .gs{font-weight:bold}.highlight .gu{color:#800080;font-weight:bold}.highlight .gt{color:#a00}.highlight .kc,.highlight .kd,.highlight .kn,.highlight .kp,.highlight .kr{font-weight:bold}.highlight .kt{color:#458;font-weight:bold}.highlight .m{color:#099}.highlight .s{color:#d14}.highlight .na{color:#008080}.highlight .nb{color:#0086b3}.highlight .nc{color:#458;font-weight:bold}.highlight .no{color:#008080}.highlight .ni{color:#800080}.highlight .ne,.highlight .nf{color:#900;font-weight:bold}.highlight .nn{color:#555}.highlight .nt{color:#000080}.highlight .nv{color:#008080}.highlight .ow{font-weight:bold}.highlight .w{color:#bbb}.highlight .mf,.highlight .mh,.highlight .mi,.highlight .mo{color:#099}.highlight .sb,.highlight .sc,.highlight .sd,.highlight .s2,.highlight .se,.highlight .sh,.highlight .si,.highlight .sx{color:#d14}.highlight .sr{color:#009926}.highlight .s1{color:#d14}.highlight .ss{color:#990073}.highlight .bp{color:#999}.highlight .vc,.highlight .vg,.highlight .vi{color:#008080}.highlight .il{color:#099}.highlight .gc{color:#999;background-color:#eaf2f5}.type-csharp .highlight .k,.type-csharp .highlight .kt{color:#00F}.type-csharp .highlight .nf{color:#000;font-weight:normal}.type-csharp .highlight .nc{color:#2b91af}.type-csharp .highlight .nn{color:#000}.type-csharp .highlight .s,.type-csharp .highlight .sc{color:#a31515}
</style>
</head>
<body class="normal">
<div id="wrapper">
<h1 id="photopaycloudsdkforios">PhotoPayCloud SDK for iOS</h1>
<p>PhotoPayCloud is a system for easy and efficient payment data extraction from various payment documents. Documents can be PDF invoices, Excel sheets, Word documents, photos of paper bills and various other formats. PhotoPayCloud provides secure storage of these payment documents, as well as enables easy and simple payment method used for users of mobile banking applications.</p>
<p>This package contains client side SDK for accessing PhotoPayCloud web services on iOS.</p>
<p>Structure of the README document is as follows</p>
<ul>
<li><a href="#1">Getting Started</a> which explains a little more about the structure of this repository</li>
<li><a href="#2">Setting up your Xcode workspace</a> which shows you how to connect the PhotoPayCloud SDK with your Xcode project</li>
<li><a href="#3">Common operations</a> which explains the procedures you have to implement to get a working app</li>
</ul>
<p>Common operations include the following procedures:</p>
<ol>
<li><a href="#301">Initializing the PhotoPayCloud Service for a specific user</a></li>
<li><a href="#302">Checking for unfinished and pending uploads</a></li>
<li><a href="#303">Creating a View Controller which display a list of documents</a></li>
<li><a href="#304">Uploading new documents and images to processing server</a></li>
<li><a href="#305">Displaying user help</a></li>
<li><a href="#306">Retrieving scanning results</a></li>
<li><a href="#307">Opening details view</a></li>
<li><a href="#308">Requesting all documents with specific document state</a></li>
<li><a href="#309">Deleting a document</a></li>
<li><a href="#310">Confirming user’s data when paying a document</a></li>
<li><a href="#311">Retrieving a document thumbnail and preview images</a></li>
<li><a href="#312">Retrieving the whole document</a></li>
<li><a href="#313">Uninitializing PPPhotoPayCloudService object</a></li>
</ol>
<h2 id="aname1agettingstarted"><a name="1"></a> Getting started</h2>
<p>Since this is a private git repository, the easiest way to stay up to date with the latest versions is to setup this repository as a git submodule inside your project’s repository.</p>
<pre><code>cd <your-repo>
git submodule add https://github.com/PhotoPay/ppcloud-ios.git ppcloud-ios
</code></pre>
<p>This will clone the whole ppcloud-ios repository and check out master branch. Any subsequent pulls inside your repository will automatically pull changes in ppcloud-ios repository as well.</p>
<p>Inside ppcloud-ios there are two separate projects. </p>
<p>PhotoPayCloudSDK is a project which builds the PhotoPayCloud framework. This is the framework which communicates with PhotoPay Cloud web services and you should use it inside your application. </p>
<p>There is also a PhotoPayCloudDemo application which shows an example on how to do a proper integration. PhotoPayCloudDemo app additionaly uses AFNetworking library for network communication. If you use it in your application, the integration is even more simplified because you can simply copy AFNetworking wrappers for PhotoPayCloud into your application from PhotoPayCloudDemo app.</p>
<p>To run PhotoPayCloudDemo you need CocoaPods installed. To set up CocoaPods dependencies you should run:</p>
<pre><code>cd <PhotoPayCloudDemo-folder>
pod install
</code></pre>
<h2 id="aname2asettingupyourxcodeworkspace"><a name="2"></a> Setting up your Xcode workspace</h2>
<p>The easiest way to use PhotoPayCloudSDK with your Xcode project is to add it into your Xcode workspace. Simply drag and drop PhotoPayCloudSDK.xcodeproj file to your workspace, below your project, on the same hierarchy level. </p>
<figure>
<img src="Docs/xcode-add-sdkproject.png" alt="Adding PhotoPayCloudSDK to your Xcode workspace" />
<figcaption>Adding PhotoPayCloudSDK to your Xcode workspace</figcaption></figure>
<p>After that, edit the sceme for building your application. Add <strong>PhotoPayCloudFramework</strong> build target into your scheme, and set it to be built before your application’s target. Also, disable “Parallelize Build” option. This will ensure PhotoPayCloudSDK is always rebuilt with the lastest updates before running your application. </p>
<figure>
<img src="Docs/xcode-scheme.png" alt="Setting up your build scheme" />
<figcaption>Setting up your build scheme</figcaption></figure>
<p>Now, start your application’s build scheme. It will result with PhotoPayCloud.embeddedframework being created inside ppcloud-ios repository. Drag and drop it in the Frameworks group in your project. When asked, disable option “Copy items into destination group’s folder”</p>
<figure>
<img src="Docs/xcode-add-framework.png" alt="Adding PhotoPayCloud.embeddedframework to your project" />
<figcaption>Adding PhotoPayCloud.embeddedframework to your project</figcaption></figure>
<p>Now you have everything set up to start the coding part. But first we can cover some of the basic PhotoPayCloudSDK architecture.</p>
<h2 id="aname3acommonoperations"><a name="3"></a> Common operations</h2>
<h3 id="aname301a1.initializingthephotopaycloudserviceforaspecificuser"><a name="301"></a> 1. Initializing the PhotoPayCloud Service for a specific user</h3>
<p>The initialization method like the following should be called whenever a user logs in to the application. The method should specify data about the current user, as well as an object reposnsible for making network requests (PPNetworkManager object). For example, if you use AFNetworking, you can use PPAFNetworkManager class provided in the PhotoPayCloudDemo application, but you have to provide your own AFHTTPRequestOperationManager object.</p>
<pre><code>- (void)photoPayCloudLogin {
PPAFNetworkManager* networkManager = [[PPAFNetworkManager alloc] initWithRequestOperationManager:[PPAppDelegate requestOperationManager]];
[networkManager setMaxConcurrentUploadsCount:1];
PPUser* user = [[PPUser alloc] initWithUserId:[[PPApp sharedApp] userId]
organizationId:@"Your_organisation_name"]; // e.g. @"EBS"
[[PPPhotoPayCloudService sharedService] initializeForUser:user
withNetworkManager:networkManager];
}
</code></pre>
<p>All following tasks depend on successfully initialized PPPhotoPayCloudService object</p>
<h4 id="ppnetworkmanager">PPNetworkManager</h4>
<p>PPNetworkManager is an abstract class for creating web requests for communicating with PhotoPayCloud Web API. It defines the interface which a concrete implementation must provide so that it can be used with PPPhotoPayCloudService object.</p>
<p>For example, PhotoPayCloudDemo application defines a concrete implementation of PPNetworkManager interface which uses AFNetworking library for managing network communication. If you use AFNetworking inside your application, consider using those classes. </p>
<p>If you don’t use AFNetworking, consider switching to it (it’s an impressive library), or contact us so we can come up with some other option.</p>
<figure>
<img src="Docs/afppnetworkmanager.png" alt="Implementation of PPNetworkManager which uses AFNetworking" />
<figcaption>Implementation of PPNetworkManager which uses AFNetworking</figcaption></figure>
<h4 id="ppuser">PPUser</h4>
<p>An object which specifies the user of the PhotoPayCloud service. User is defined with the following properties:</p>
<ul>
<li>userId</li>
<li>organizationId</li>
<li>userType</li>
</ul>
<p>userId must be unique for each user of your app. Organisation ID is the unique string ID of the organization which uses your app. User type can be <em>Person</em>, <em>Business</em> or <em>Craft</em>, but <em>Person</em> is typically used if not specified otherwise.</p>
<p>It’s important to note that userId is never stored locally on the mobile device. The only user’s data which is stored on the user’s phone is MD5 hash of the userId, and it’s only used for identifying documents which need to be uploaded until upload finishes, or until user deletes the document.</p>
<h3 id="aname302a2.checkingforunfinishedandpendinguploads"><a name="302"></a> 2. Checking for unfinished and pending uploads</h3>
<p>After the user logs in into the application, you should check if any pending uploads exist which didn’t successfully complete. If they exist, the user should have the opportunity to continue those uploads, or delete them permanently.</p>
<pre><code>- (void)checkPhotoPayCloudUploads {
// check if PhotoPayCloudService was paused
if ([[PPPhotoPayCloudService sharedService] state] == PPPhotoPayCloudServiceStatePaused) {
// if true, ask user to continue or abort paused requests
PPAlertView* alertView = [[PPAlertView alloc] initWithTitle:@"Some documents failed to upload")
message:@"Would you like to continue uploading these documents?"
completion:^(BOOL cancelled, NSInteger buttonIndex) {
NSError* __autoreleasing error = nil;
if (buttonIndex == 0) {
[[PPPhotoPayCloudService sharedService] deletePendingDocumentsWithError:&error];
} else if (buttonIndex == 1) {
[[PPPhotoPayCloudService sharedService] uploadPendingDocuments];
}
}
cancelButtonTitle:@"Abort"
otherButtonTitles:@"Continue", nil];
[alertView show];
}
}
</code></pre>
<h3 id="aname303a3.creatingaviewcontrollerwhichdisplayalistofdocuments"><a name="303"></a> 3. Creating a View Controller which display a list of documents</h3>
<p>PhotoPay Cloud Home View is a view which contains a list of all user’s documents. This View also has a button which can start camera capture for taking a photo of a user’s bill. </p>
<p>List of all user’s documents is a UITableView, with each table view cell corresponding to one document. Cell can be pressed to open a Details view for a corresponding document.</p>
<p>You can design the Home view as you wish, but the easiest way to set up the basic functionality is to use your own subclass of PPDocumentsTableDataSource class for maintaining a list of PPDocument objects inside your Table view.</p>
<h4 id="ppdocumentstabledatasourcesubclass">PPDocumentsTableDataSource subclass</h4>
<p>To use PPDocumentsTableDataSource as a data source for your UITableViews, you must subclass it and provide methods for creaing UITableViewCells:</p>
<pre><code>// .... header
@interface PPDocumentsDataSource : PPDocumentsTableDataSource
@end
// .... implementation
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Obtain document object for given index path
PPDocument *document = [self itemForIndexPath:indexPath];
PPDocumentTableViewCell *cell = // your UI specific code which uses data from document object
return cell;
}
</code></pre>
<p>One example on how to do that is given in PhotoPayCloudDemo project and is a fairly standard way of achieving this. </p>
<p>An instance method of PPDocumentsTableDataSource class which will definitely help for populating UITableViewCells with data about PPDocument objects is</p>
<pre><code>// Obtain document object for given index path
PPDocument *document = [self itemForIndexPath:indexPath];
</code></pre>
<h4 id="ppdocumentstableviewcontroller">PPDocumentsTableViewController</h4>
<p>Second part of implementing a Home view controller is creating a PPBaseDocumentsTableViewController subclass, commonly called PPDocumentsTableViewController. This is a UITableViewController object responsible for maintaing and controlling UITableView with documents, receiving events on upload progress, and receiving events on document fetch requests.</p>
<p>To implement the subclass, the following code is all that is needed. It sets the data source object, and initializes the SectionCreator object which separates uploading documents into Table Section on top of the list, and processed documents into Table Section the bottom of the list.</p>
<pre><code>// .... header
@interface PPDocumentsTableViewController : PPBaseDocumentsTableViewController
@end
// .... implementation
@implementation PPDocumentsTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.dataSource = [[PPDocumentsDataSource alloc] init];
// Specify section creator object which splits the uploading documents into two sections
// One for uploading documents, one for those which are processing or done
PPSplitTypeDocumentsSectionCreator* sectionCreator = [[PPSplitTypeDocumentsSectionCreator alloc] init];
[sectionCreator setUploadingSectionTitle:_(@"PhotoPayHomeUploadingSectionTitle")];
[sectionCreator setProcessedSectionTitle:_(@"PhotoPayHomeProcessedSectionTitle")];
self.dataSource.sectionCreator = sectionCreator;
[[PPPhotoPayCloudService sharedService] setDataSource:(PPDocumentsDataSource*)[self dataSource]];
}
@end
</code></pre>
<p>If your UITableViewCells support upload progress monitoring with UIProgressView, you can implement a callback method of PPBaseDocumentsTableViewController which will update the UIProgressView.</p>
<pre><code>#pragma mark - PPDocumentUploadDelegate
- (void)localDocument:(PPLocalDocument *)localDocument
didUpdateProgressWithBytesWritten:(long long)totalBytesWritten
totalBytesToWrite:(long long)totalBytesToWrite {
// instead of requesting the whole table to update, we just find the potential cell among visible cells
for (PPDocumentTableViewCell* cell in self.tableView.visibleCells) {
if (cell.document.state == PPDocumentStateUploading) {
[cell refreshProgress];
}
}
}
</code></pre>
<p>You can also perform additional changes to modify the design of this UITableViewController. For example, you can add UIRefreshControl, modify the height of the cells, etc.</p>
<h3 id="homeviewcontroller">HomeViewController</h3>
<p>HomeViewController is a ViewController responsible for holding a PPDocumentsTableViewController with a list of documents, and for extending the possibilities for user interaction by offering a UIButton for taking pictures and a button for opening usage instructions. Typically, it’s implemented as a PPBaseHomeViewController subclass.</p>
<p>It’s enough to provide a UIButton or UIBarButtonItem for opening a camera, UIButton or UIBarButtonItem for opening help, and to set PPDocumentsTableViewController as underlying tableViewController. </p>
<p>Besides that, implementation provided here overrides uploadImage: method, responsible for creating PPLocalDocument for upload, and openDocumentDetailsView: method, responsible for opening a details view controller on tap event on UITableViewCell.</p>
<pre><code>// .... header
/** When opening PhotoPay, user sees view controlled by this view controller */
@interface PPHomeViewController : PPBaseHomeViewController
/** Button which starts the photo capture */
@property (weak, nonatomic) IBOutlet UIButton *cameraButton;
/** Callback on camera button pressed */
- (IBAction)cameraButtonPressed:(id)sender;
@end
// .... implementation
@implementation PPHomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setTitle:_(@"PhotoPayHomeTitle")];
// Add table view controller
self.tableViewController = [[PPDocumentsTableViewController alloc] initWithNibName:@"PPDocumentsTableViewController" bundle:nil];
[self.tableViewController setDocumentStates:PPDocumentStateLocal | PPDocumentStateRemoteUnconfirmed];
[self.tableViewController setDelegate:self];
[self.tableViewController setPollInterval:@(10.0f)];
// Add help button
UIBarButtonItem *helpBarItem = [[UIBarButtonItem alloc] initWithTitle:_(@"PhotoPayHelpButtonTitle")
style:UIBarButtonItemStyleBordered
target:self
action:@selector(openHelp)];
self.navigationItem.rightBarButtonItem = helpBarItem;
}
- (void)uploadImage:(UIImage*)image {
// create a local document for this user
PPLocalDocument *document = [[PPLocalImageDocument alloc] initWithImage:image
processingType:[[PPProfile sharedProfile] photoProcessingType]];
[self uploadDocument:document];
}
- (void)openDocumentDetailsView:(PPDocument*)document {
PPDocumentDetailsViewController* documentDetails =
[[PPDocumentDetailsViewController alloc] initWithNibName:[PPDocumentDetailsViewController defaultXibName]
bundle:nil
document:document];
[[self navigationController] pushViewController:documentDetails animated:YES];
}
</code></pre>
<h3 id="aname304a4.uploadingnewdocumentsandimagestoprocessingserver"><a name="304"></a> 4. Uploading new documents and images to processing server</h3>
<p>You can use provided uploadImage: method in the PPBaseHomeViewController, but your requirements might be different. For example, you might want to specify some other PPDocumentProcessingType, instead of the default PPDocumentProcessingTypeSerbianPhotoInvoice.</p>
<p>Look at the implementation of PPBaseHomeViewController in our open SDK project to see the details of document uploading methods. However, in most cases default implementation should be enough.</p>
<h3 id="aname305a5.displayinguserhelp"><a name="305"></a> 5. Displaying user help</h3>
<p>PPBaseHomeViewController is able to display the default instructions page. Method openHelp is responsible for this. It instantiates PPPagedContentViewController object and displays it modally.</p>
<p>You can override openHelp in your HomeViewController implementation, but in most cases default should be enough.</p>
<h3 id="aname306a6.retrievingscanningresults"><a name="306"></a> 6. Retrieving scanning results</h3>
<p>Scanning results are easily obtained from PPRemoteDocument object by accessing it’s scanResult property. Scanning results are provided as a PPScanResult object. </p>
<pre><code>PPScanResult *scanResult = [[document remoteDocument] scanResult];
</code></pre>
<p>PPScanResult has multiple subclasses. Which PPScanResult subtype is actually used for scanResult, depends on the processing type used when uploading the document. Here is the table of processing type values with PPScanResult subclasses:</p>
<table>
<colgroup>
<col style="text-align:center;"/>
<col style="text-align:center;"/>
</colgroup>
<thead>
<tr>
<th style="text-align:center;">Processing Type</th>
<th style="text-align:center;">PPScanResult</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center;">PPDocumentProcessingTypeAustrianPDFInvoice</td>
<td style="text-align:center;">PPScanResultAustria</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentProcessingTypeAustrianPhotoInvoice</td>
<td style="text-align:center;">PPScanResultAustria</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentProcessingTypeSerbianPDFInvoice</td>
<td style="text-align:center;">PPScanResultSerbia</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentProcessingTypeSerbianPhotoInvoice</td>
<td style="text-align:center;">PPScanResultSerbia</td>
</tr>
</tbody>
</table>
<p>After you cast the scanResult object to specific PPScanResult subtype, you have new methods available for accessing specific values form the documents. For example, you might want to obtain AccountNumber candidate from PPScanResultSerbia object:</p>
<pre><code>PPScanResultSerbia* serbianResult = (PPScanResultSerbia*)[remoteDocument scanResult];
PPElementCandidate* accountCandidate = [[serbianResult mostProbableAccountNumberCandidate] value];
</code></pre>
<p>Each PPElementCandidate object has a confidence level (given as a integer number from 0 to 2000), and a concrete NSString* value.</p>
<p>Besides obtaining the most probable PPElementCandidate object, you can also obtain all candidates for a specific item from given scan result:</p>
<pre><code>PPElementCandidateList* accountCandiadteList = [serbianResult accountNumberCandidateList];
</code></pre>
<p>PPElementCandidateList object contains an NSArray of candidates sorted by decreasing confidence level (meaning, the candidate with the highest confidence level is at the front of the array).</p>
<h3 id="aname307a7.openingdetailsview"><a name="307"></a> 7. Opening details view</h3>
<p>When user taps a specific cell in a HomeViewController list, you should open a view which gives more details about the underlying document. That means you are responsible for developing a ViewController for this. Details View is opened on openDocumentDetailsView: event in your HomeViewController.</p>
<p>You can register your Details View Controller to be the delegate for upload progress reporting:</p>
<pre><code>[[[document localDocument] uploadRequest] setDelegate:self];
</code></pre>
<p>Also, the Details View Controller can be a delegate for document state change, so that you can replace details views on the fly after a document changes it’s state:</p>
<pre><code>[document setDelegate:self];
</code></pre>
<p>You can design the view controller as you think it’s best, but we recommend that PPDocument object in different states has different design of Details View. </p>
<table>
<colgroup>
<col style="text-align:center;"/>
<col style="text-align:center;"/>
</colgroup>
<thead>
<tr>
<th style="text-align:center;">PPDocumentState</th>
<th style="text-align:center;">Design of DetailsViewController</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center;">PPDocumentStateLocalUploadInProgress</td>
<td style="text-align:center;">Present a status that document is being uploaded together with a UIProgressBar with upload progress</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentStateUploadFailed</td>
<td style="text-align:center;">Present a status that document upload failed, with a button to retry the upload</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentStateUnprocessed</td>
<td style="text-align:center;">Present a status that document is received by the server and processing is in progress</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentStateProcessed</td>
<td style="text-align:center;">Present a status that document is processed, together with real scanning results (see 6. Retrieving scanning results). Also, a button for payment should be added, if all scanned data is valid.</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentStateProcessedWithError</td>
<td style="text-align:center;">Present a status that the error has occurred while the document was being processed</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentStatePaid</td>
<td style="text-align:center;">Preset a information that the document was paid</td>
</tr>
</tbody>
</table>
<p>For all states, provide a button for deleting a document.</p>
<h3 id="aname308a8.requestingalldocumentswithspecificdocumentstate"><a name="308"></a> 8. Requesting all documents with specific document state</h3>
<p>In HomeViewController, when you want to display documents, you are required to specify which document states you’re interested in. When you know the states, you can make a request for documents in the following way:</p>
<pre><code>// first, set the document states you're interested in
self.tableViewController.documentStates = PDocumentStateLocal | PPDocumentStateRemoteUnconfirmed;
// then, make a request for documents
[[self tableViewController] requestDocuments];
</code></pre>
<p>If your tableViewController (see section about PPDocumentsTableViewController objects), has pollInterval property set to a NSNumber value other than nil, documents with specified states will be requested periodically, each pollInterval seconds. If pollInterval is nil, only one document request will be made.</p>
<h3 id="aname309a9.deletingadocument"><a name="309"></a> 9. Deleting a document</h3>
<p>To delete a document, use the following code:</p>
<pre><code>[[PPPhotoPayCloudService sharedService] deleteDocument:document error:&error];
</code></pre>
<h3 id="aname310a10.confirmingusersdatawhenpayingadocument"><a name="310"></a> 10. Confirming user’s data when paying a document</h3>
<p>When the user actually makes the payment, it’s required that PhotoPayCloud Service is notified for the correct data used for the actual payment. To notify the Service about confirmed data perform the following steps:</p>
<p>Initialize the proper PPUserConfirmedValues object. Depending on the processing type used by the document, initialize one of the following PPUserConfirmedValues subclasses</p>
<table>
<colgroup>
<col style="text-align:center;"/>
<col style="text-align:center;"/>
</colgroup>
<thead>
<tr>
<th style="text-align:center;">Processing Type</th>
<th style="text-align:center;">PPUserConfirmedValues</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center;">PPDocumentProcessingTypeAustrianPDFInvoice</td>
<td style="text-align:center;">PPUserConfirmedValuesAustria</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentProcessingTypeAustrianPhotoInvoice</td>
<td style="text-align:center;">PPUserConfirmedValuesAustria</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentProcessingTypeSerbianPDFInvoice</td>
<td style="text-align:center;">PPUserConfirmedValuesSerbia</td>
</tr>
<tr>
<td style="text-align:center;">PPDocumentProcessingTypeSerbianPhotoInvoice</td>
<td style="text-align:center;">PPUserConfirmedValuesSerbia</td>
</tr>
</tbody>
</table>
<h3 id="aname311a11.retrievingadocumentthumbnailandpreviewimages"><a name="311"></a> 11. Retrieving a document thumbnail and preview images</h3>
<p>Any document object, no matter if it’s PPLocalDocument or PPRemoteDocument, has an instance method for obtaining thumbnails and preview images. Simply use:</p>
<pre><code>[document thumbnailImageWithSuccess:^(UIImage *thumbnailImage) {
// your code
} failure:^{
// your code
}];
</code></pre>
<p>for accessing thumbails, or </p>
<pre><code>[document previewImageWithSuccess:^(UIImage *previewImage) {
// your code
} failure:^(){
// your code
}];
</code></pre>
<p>for accessing preview images.</p>
<h3 id="aname312a12.retrievingthewholedocument"><a name="312"></a> 12. Retrieving the whole document</h3>
<p>For retrieving the whole document, for example, PDF invoice, use the following instance method on PPDocument object</p>
<pre><code>[document documentBytesWithSuccess:^(NSData *bytes) {
// your code
} failure:{
// your code
}];
</code></pre>
<p>See PPDocumentPreview and PPQLPreviewController classes in the Demo app for a quick demo on how to implement a working document preview View Controller.</p>
<h3 id="aname313a13.uninitializingppphotopaycloudserviceobject"><a name="313"></a> 13. Uninitializing PPPhotoPayCloudService object</h3>
<p>Inverse method to initialization should be performed every time the user logs out from the application. </p>
<pre><code>- (void)photoPayCloudLogout {
[[PPPhotoPayCloudService sharedService] uninitialize];
}
</code></pre>
<!--(
## Overview of PhotoPayCloudSDK architecture
Your integration procedure can also be achieved on much lower level, by interacting directly with PPPhotoPayCloudService object. Using PhotoPayCloudSDK primarily means collaborating with the following classes:
### PPPhotoPayCloudService
PPPhotoPayCloudService is a singleton object responsible for performing all high level requests. It can be accessed using:
[PPPhotoPayCloudService sharedService]
PPPhotoPayCloudService object can perform the following tasks:
Uploading documents to PhotoPay Cloud web API:
- (void)uploadDocument:(PPLocalDocument*)document
delegate:(id<PPDocumentUploadDelegate>)delegate
success:(void (^)(PPLocalDocument* localDocument, PPRemoteDocument* remoteDocument))success
failure:(void (^)(PPLocalDocument* localDocument, NSError* error))failure
canceled:(void (^)(PPLocalDocument* localDocument))canceled;
Uploading all pending documents which failed to upload in the last usage session
- (void)uploadPendingDocuments;
Deleting all pending documents
- (void)deletePendingDocumentsWithError:(NSError**)error;
Retrieving the image for the document. Image size can be specified, for example thumnail size.
- (void)getImageForDocument:(PPRemoteDocument*)document
imageSize:(PPImageSize)imageSize
imageFormat:(PPImageFormat)imageFormat
success:(void (^)(UIImage* image))success
failure:(void (^)(NSError* error))failure
canceled:(void (^)())canceled;
Retrieving the actual document. The whole byte array containing the document is retrieved.
- (void)getDocumentData:(PPRemoteDocument*)document
success:(void (^)(NSData* data))success
failure:(void (^)(NSError* error))failure
canceled:(void (^)())canceled;
Associating the values user used for making the payment with a document
- (void)confirmValues:(PPUserConfirmedValues*)values
forDocument:(PPRemoteDocument*)document
success:(void (^)(void))success
failure:(void (^)(NSError* error))failure
canceled:(void (^)(void))canceled;
Deleting the document
- (void)deleteDocument:(PPDocument*)document
error:(NSError**)error;
Requesting documents with a given status which should populate PPDocumentsTableDataSource object. Only a single request is made.
- (void)requestDocuments:(PPDocumentState)documentStateList;
Requesting documents which should populate PPDocumentsTableDataSource object. After that call, polling is used each _timeInterval_ seconds.
- (void)requestDocuments:(PPDocumentState)documentStateList
pollInterval:(NSTimeInterval)timeInterval;
These features are everything that's required to provide a well designed user experience.
### PPDocument (and subclasses)
PPDocument is an ecapsulation of a document used in the payment process. A PPDocument object can be either PPLocalDocument (for example, an image obtained from the camera which still wasn't uploaded to PhotoPayCloud web service), or PPRemoteDocument (for example, the same image, but uploaded to server, processed and ready for payment). Furthermore, PPLocalDocument can be PPLocalImageDocument, when created from an UIImage object.
The most important properties of a PPDocument objects are documentType (png, jpeg, pdf, etc.), processing type, which defines what kind of processing will PhotoPayCloud server perform on this document, and a PPDocumentState. State defines what's happening with the document at the moment.
Each document starts as a PPLocalDocument in state Created, goes through a variety of states, for example, Uploading, Received, Processing, Processed, etc. For a document, the goal is to finish as a PPRemoteDocument in state Processed. When in this state, the document can be used to prepopulate a payment form with scanned payment data.
For example, for serbian invoices, documents are typically created as a PPLocalImageDocument subtype, with initializer:
- (id)initWithImage:(UIImage*)inImage processingType:(PPDocumentProcessingType)inProcessingType
PPDocumentProcessingType for photos of Serbian invoices is _PPDocumentProcessingTypeSerbianPhotoInvoice_
#### Operations on all PPDocument objects
Observing document state can be done by examining state property
@property (nonatomic, assign) PPDocumentState state;
Also, if needed, there is a PPDocumentStateChangedDelegate protocol available which can notify your object for any state changed events.
Each PPDocument has methods for accessing thumbnail image, preview image and the original document's byte array:
- (void)thumbnailImageWithSuccess:(void (^)(UIImage* thumbnailImage))success
failure:(void (^)(void))failure;
- (void)previewImageWithSuccess:(void (^)(UIImage* previewImage))success
failure:(void (^)(void))failure;
- (void)documentBytesWithSuccess:(void (^)(NSData* bytes))success
failure:(void (^)(void))failure;
Also, there is a way to safely cast a PPDocument to any of it's subclasses. These methods return nil if cast is not successful.
- (PPLocalDocument*)localDocument;
- (PPRemoteDocument*)remoteDocument;
#### Operations on PPLocalDocument objects
Local document object have access to PPUploadRequestOperation for monitoring uploads
#### Operations on PPRemoteDocument objects
Interesting properties of PPRemoteDocument objects are
@property (nonatomic, strong) NSNumber* expectedProcessingTime;
@property (nonatomic, strong) PPScanResult* scanResult;
### PPDocumentsTableDataSource
PPPhotoPayCloudService object maintains it's own object which can be used as a UITableViewDataSource. The concrete implementation of this object can be overridden in your app, but the idea is that PPPhotoPayCloudService singleton object is responsible for maintaining a list of all documents of interest to the current user.
Division of documents in section inside PPDocumentsTableDataSource can be specified separately, but more on that later.
### Rough class diagram of most important classes
![Partial class diagram of PhotoPayCloudSDK](Docs/class-diag.png)
)-->
</div>
</body>
</html>