All Products
Search
Document Center

Web Application Firewall:Integrasi SDK aplikasi iOS

Last Updated:Aug 06, 2026

Untuk mengonfigurasi kebijakan mitigasi berbasis skenario bagi aplikasi Anda di Konsol Bot Management, Anda harus mengintegrasikan App Protection SDK dari Web Application Firewall (WAF). Topik ini menjelaskan cara mengintegrasikan App Protection SDK ke dalam aplikasi iOS.

Informasi latar belakang

App Protection SDK menandatangani permintaan dari client aplikasi. Server WAF kemudian memverifikasi signature permintaan tersebut untuk mengidentifikasi dan memblokir permintaan serangan, sehingga melindungi aplikasi Anda.

Batasan

  • SDK iOS tersedia dalam dua versi: versi Identifier for Advertising (IDFA) dan versi non-IDFA. File SDK yang sesuai adalah:

    • AliTigerTally_IDFA.framework

    • AliTigerTally_NOIDFA.framework

    Jika proyek iOS Anda menggunakan IDFA, kami merekomendasikan agar Anda mengintegrasikan SDK versi AliTigerTally_IDFA. Jika tidak, gunakan versi AliTigerTally_NOIDFA.

  • Antarmuka init dapat memakan waktu. Untuk perlindungan optimal, tunggu minimal 2 detik setelah memanggil antarmuka init sebelum memanggil antarmuka vmpSign. Meskipun penundaan ini tidak wajib, interval yang lebih pendek dapat mencegah fitur keamanan bekerja secara maksimal. Anda dapat menyesuaikan interval berdasarkan kebutuhan bisnis Anda.

  • App Protection SDK memerlukan iOS versi 9.0 atau yang lebih baru.

Prasyarat

  • Anda telah memperoleh SDK untuk aplikasi iOS Anda.

    Untuk memperoleh SDK, kirimkan ticket untuk menghubungi Ahli teknis produk.

    Catatan

    File SDK untuk aplikasi iOS diberi nama tigertally-X.Y.Z-xxxx-ios.zip, dengan X.Y.Z sebagai nomor versi. File tersebut berisi dua file framework dan dua file xcframework.

  • Anda telah memperoleh authentication key SDK (juga dikenal sebagai appkey).

    Setelah Anda mengaktifkan Bot Management, Anda dapat Masuk ke Konsol Web Application Firewall 3.0. Dari bilah menu atas, pilih kelompok sumber daya dan wilayah (Chinese Mainland atau Outside Chinese Mainland) untuk instans WAF.. Di panel navigasi sebelah kiri, pilih Protection Config > Bot Management > Bot Management Template. Buka tab App Protection dan klik Obtain and Copy AppKey di pojok kanan atas untuk mengambil authentication key SDK. Kunci ini diperlukan untuk inisialisasi SDK dan harus disertakan dalam kode integrasi Anda.

    Catatan

    Setiap Akun Alibaba Cloud memiliki appkey unik untuk semua nama domain yang dilindungi oleh WAF. appkey ini digunakan untuk integrasi SDK di aplikasi Android, iOS, dan HarmonyOS.

    Contoh authentication key: ****OpKLvM6zliu6KopyHIhmneb_****u4ekci2W8i6F9vrgpEezqAzEzj2ANrVUhvAXMwYzgY_****vc51aEQlRovkRoUhRlVsf4IzO9dZp6nN_****Wz8pk2TDLuMo4pVIQvGaxH3vrsnSQiK****.

Langkah 1: Buat proyek

Di Xcode, buat proyek iOS menggunakan wizard pengaturan. Contoh berikut menunjukkan struktur direktori proyek.

tigerdemo
  tigerdemo
    tool
    AppDelegate.h
    AppDelegate.m
    ViewController.h
    ViewController.m
    main.m
    Main.storyboard
    LaunchScreen.storyboard
    AliTigerTallyApp.entitlements
    Info.plist

Langkah 2: Integrasikan framework

Tambahkan file SDK, AliTigerTally_IDFA.framework atau AliTigerTally_NOIDFA.framework, ke proyek Anda.

  • Versi IDFA

    Setelah menambahkan file tersebut, atur opsi Embed dari AliTigerTally_IDFA.framework menjadi Do Not Embed.

  • Versi non-IDFA

    Tambahkan AliTigerTally_NOIDFA.framework ke bagian Frameworks, Libraries, and Embedded Content proyek Anda, dan atur Embed menjadi Do Not Embed.

Tambahkan file SDK modul Captcha, AliCaptcha.framework, ke proyek Anda, dan atur opsi Embed-nya menjadi Do Not Embed.

Tambahkan file resource AliCaptcha.bundle ke proyek Anda dengan menambahkannya di Build Phases > Copy Bundle Resources.

Langkah 3: Tambahkan library dependensi

Di bagian Frameworks, Libraries, and Embedded Content proyek Xcode Anda, tambahkan library dependensi berikut.

Library dependensi

Diperlukan untuk IDFA

Diperlukan untuk non-IDFA

libc++.tbd

Ya

Ya

libresolv.9.tbd

Ya

Ya

CoreTelephony.framework

Ya

Ya

AdSupport.framework

Ya

Tidak

AppTrackingTransparency.framework

Ya

Tidak

Langkah 4: Tambahkan flag linker

Di Build Settings, cari Other Linker Flags dan tambahkan flag -ObjC.

Langkah 5: Tambahkan kode integrasi

1. Tambahkan file header

  • Untuk versi IDFA, tambahkan pernyataan impor berikut:

    #import <AliTigerTally_IDFA/AliTigerTally.h>
  • Untuk versi non-IDFA, tambahkan pernyataan impor berikut:

    #import <AliTigerTally_NOIDFA/AliTigerTally.h> 

2. Konfigurasi penandatanganan data

  1. Tetapkan identifier pengguna akhir kustom untuk bisnis Anda. Hal ini memungkinkan Anda mengonfigurasi kebijakan perlindungan WAF dengan fleksibilitas lebih tinggi.

    /**
    * Menetapkan akun pengguna.
    *
    * @param account   Informasi akun.
    */
    - (void)setAccount:(NSString *)account;
    • Parameter:

      • account: Tipe: NSString. String yang mengidentifikasi pengguna. Kami merekomendasikan penggunaan format yang telah didesensitisasi.

    • Nilai kembalian: Tidak ada.

    • Contoh:

      // Untuk pengguna tamu, Anda dapat melewati setAccount dan langsung melakukan inisialisasi. Setelah pengguna masuk, panggil setAccount dan lakukan inisialisasi ulang.
      [[AliTigerTally sharedInstance] setAccount:@"testAccount"];
  2. Inisialisasi SDK untuk melakukan pengumpulan data.

    Setiap inisialisasi mengumpulkan informasi perangkat satu kali. Anda dapat memanggil fungsi init lagi untuk melakukan inisialisasi ulang pengumpulan data untuk skenario bisnis yang berbeda.

    Inisialisasi mendukung tiga mode pengumpulan data: lengkap, privasi kustom, dan non-privasi. Mode non-privasi mengecualikan bidang yang terkait privasi pengguna akhir, seperti IDFA dan IDFV.

    Catatan

    Untuk memenuhi persyaratan internal Anda, pilih mode pengumpulan data yang sesuai guna memastikan integritas data. Data yang lebih lengkap meningkatkan deteksi ancaman.

    // Callback inisialisasi. Mengembalikan kode status pemanggilan antarmuka.
    typedef void (^TTInitListener)(int);
    
    /**
     * Menginisialisasi SDK.
     *
     * @param appkey        Kunci.
     * @param options       Parameter opsional.
     * @param onInitFinish  Callback saat inisialisasi selesai.
     * @return Menunjukkan apakah inisialisasi berhasil.
     */
    - (int)init:(NSString *)appkey collectType:(TTCollectType)type options:(NSMutableDictionary *_Nullable)options listener:(TTInitListener _Nullable)onInitFinish;
    • Parameter:

      • appkey: Tipe: NSString. Authentication key SDK Anda.

      • collectType: Tipe: TTCollectType. Mode pengumpulan data. Nilai yang valid:

        Bidang

        Deskripsi

        Contoh

        TT_DEFAULT

        Mengumpulkan semua data.

        TT_DEFAULT

        TT_NO_BASIC_DATA

        Tidak mengumpulkan data perangkat dasar.

        Termasuk nama perangkat, versi OS, dan resolusi layar.

        TT_NO_X | TT_NO_Y

        (di mana X dan Y merepresentasikan jenis bidang data yang dikecualikan)

        TT_NO_UNIQUE_DATA

        Tidak mengumpulkan data identifier unik.

        Termasuk IDFV dan IDFA.

        TT_NO_EXTRA_DATA

        Tidak mengumpulkan data perangkat yang diperluas.

        Termasuk informasi Wi-Fi yang terhubung (SSID, BSSID) dan daftar jaringan Wi-Fi terdekat.

        TT_NOT_GRANTED

        Tidak mengumpulkan data privasi yang disebutkan sebelumnya.

        TT_NOT_GRANTED

      • options: Tipe: NSMutableDictionary. Parameter opsional untuk pengumpulan data. Nilai default adalah nil. Parameter berikut tersedia:

        Bidang

        Deskripsi

        Contoh

        IPv6

        Menentukan apakah akan menggunakan nama domain IPv6 untuk melaporkan informasi perangkat.

        • 0 (default): Menggunakan nama domain IPv4.

        • 1: Menggunakan nama domain IPv6.

        1

        Intl

        Menentukan apakah akan menggunakan nama domain di luar Tiongkok daratan untuk melaporkan informasi perangkat.

        • 0 (default): Melaporkan ke Tiongkok daratan.

        • 1: Melaporkan ke wilayah di luar Tiongkok daratan.

        1

        CustomUrl

        Menetapkan nama domain server pelaporan data.

        https://cloudauth-device.us-west-1.aliyuncs.com

        CustomHost

        Menetapkan host server pelaporan data.

        cloudauth-device.us-west-1.aliyuncs.com

        Catatan

        Untuk sebagian besar situs internasional, cukup atur parameter Intl. Untuk melaporkan data ke situs tertentu, atur parameter CustomUrl dan CustomHost. Situs yang tersedia meliputi:

        • Jika Intl diatur ke 0, data dilaporkan ke situs default di Tiongkok (Shanghai): https://cloudauth-device.cn-shanghai.aliyuncs.com

        • Jika Intl diatur ke 1:

          • Situs default adalah Singapura: https://cloudauth-device.ap-southeast-1.aliyuncs.com

          • Indonesia (Jakarta): https://cloudauth-device.ap-southeast-5.aliyuncs.com

          • AS (Silicon Valley): https://cloudauth-device.us-west-1.aliyuncs.com

          • Jerman (Frankfurt): https://cloudauth-device.eu-central-1.aliyuncs.com

          • Tiongkok (Hong Kong): https://cloudauth-device.cn-hongkong.aliyuncs.com

      • listener: Tipe: TTInitListener. Antarmuka callback inisialisasi SDK. Anda dapat menggunakan callback ini untuk menentukan status inisialisasi. Nilai default adalah nil.

        TTCode

        Kode

        Deskripsi

        TT_SUCCESS

        0

        Inisialisasi berhasil.

        TT_NOT_INIT

        -1

        SDK belum diinisialisasi.

        TT_NOT_PERMISSION

        -2

        Izin iOS yang diperlukan belum diberikan.

        TT_UNKNOWN_ERROR

        -3

        Terjadi kesalahan sistem yang tidak diketahui.

        TT_NETWORK_ERROR

        -4

        Terjadi kesalahan jaringan.

        TT_NETWORK_ERROR_EMPTY

        -5

        Terjadi kesalahan jaringan. Konten yang dikembalikan adalah string kosong.

        TT_NETWORK_ERROR_INVALID

        -6

        Format respons jaringan tidak valid.

        TT_PARSE_SRV_CFG_ERROR

        -7

        Gagal mengurai konfigurasi server.

        TT_NETWORK_RET_CODE_ERROR

        -8

        Gerbang mengembalikan respons kegagalan.

        TT_APPKEY_EMPTY

        -9

        appkey kosong.

        TT_PARAMS_ERROR

        -10

        Terjadi kesalahan parameter lainnya.

        TT_FGKEY_ERROR

        -11

        Terjadi kesalahan perhitungan kunci.

        TT_APPKEY_ERROR

        -12

        Versi SDK tidak cocok dengan versi appkey.

    • Nilai kembalian: Tipe: int. kode kesalahan. 0 menunjukkan keberhasilan, dan angka negatif menunjukkan kegagalan.

    • Contoh:

      // appkey adalah kunci autentikasi yang diberikan kepada Anda di konsol Alibaba Cloud.
      NSString *appKey = @"xxxxxxxxxxxxxxxxxxxxx";
      // Parameter opsional. Anda dapat mengonfigurasi IPv6 dan pelaporan dari luar Tiongkok daratan.
      NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
      [options setValue:@"0" forKey:@"IPv6"];     // Gunakan IPv4.
      [options setValue:@"0" forKey:@"Intl"];     // Laporkan data dari dalam Tiongkok daratan.
      // [options setValue:@"1" forKey:@"Intl"];  // Laporkan data dari luar Tiongkok daratan.
      // Laporkan data ke wilayah AS (Barat).
      // [options setValue:@"https://cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomUrl"];
      // [options setValue:@"cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomHost"];
      
      // Pemanggilan inisialisasi mengumpulkan informasi perangkat satu kali. Anda dapat memanggil fungsi init lagi untuk mengumpulkan informasi untuk skenario bisnis yang berbeda.
      // Pengumpulan data lengkap.
      if (0 == [[AliTigerTally sharedInstance] init:appkey collectType:TT_DEFAULT options:options listener:nil]) {
          NSLog(@"Inisialisasi berhasil");
      } else {
          NSLog(@"Inisialisasi gagal");
      }
      
      // Untuk mengumpulkan jenis data pribadi tertentu, gunakan operator "|" untuk menentukan beberapa jenis.
      TTCollectType collectPrivacy = TT_NO_BASIC_DATA | TT_NO_EXTRA_DATA;
      int ret = [[AliTigerTally sharedInstance] init:appkey collectType:collectPrivacy options:options listener:nil];
      
      // Jangan kumpulkan data pribadi.
      int ret = [[AliTigerTally sharedInstance] init:appkey collectType:TT_NOT_GRANTED options:options listener:nil];
  3. Hashing data.

    Antarmuka penandatanganan kustom ini melakukan hashing terhadap data input untuk menghasilkan string whash, yang berfungsi sebagai signature kustom. Untuk permintaan POST, PUT, dan PATCH, gunakan badan permintaan sebagai input. Untuk permintaan GET dan DELETE, gunakan URL lengkap. Anda juga harus menambahkan string whash yang dihasilkan ke bidang ali_sign_whash dalam Header permintaan HTTP.

    // Jenis permintaan:
    typedef NS_ENUM(NSInteger, TTRequestType) {
        TT_GET=0, TT_POST, TT_PUT, TT_PATCH, TT_DELETE
    };
    
    /**
     * Melakukan hashing terhadap data signature kustom.
     * @param type  Jenis data.
     * @param input Data yang akan ditandatangani.
     * @return String whash.
     */
    - (NSString *)vmpHash:(TTRequestType)type input:(NSData *)input;
    • Parameter:

      • type: Tipe: TTTypeRequest. Jenis data. Nilai yang valid:

        • GET: Data permintaan GET.

        • POST: Data permintaan POST.

        • PUT: Data permintaan PUT.

        • PATCH: Data permintaan PATCH.

        • DELETE: Data permintaan DELETE.

      • input: Tipe: NSData. Data yang akan ditandatangani. Masukkan badan atau URL berdasarkan type.

    • Nilai kembalian: Tipe: NSString. String whash.

    • Contoh:

      // Permintaan GET
      NSString *url = @"https://tigertally.aliyun.com/apptest";
      NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_GET input:[url dataUsingEncoding:NSUTF8StringEncoding]];
      NSLog(@"whash: %@", whash);
      
      // Permintaan POST
      NSString *body = @"hello world";
      NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_POST input:[body dataUsingEncoding:NSUTF8StringEncoding]];
      NSLog(@"whash: %@", whash);
    Catatan

    Pemanggilan antarmuka ini hanya diperlukan jika Anda telah mengaktifkan opsi penandatanganan kustom di konsol WAF. Jika Anda menggunakan konfigurasi signature default, Anda dapat melewati langkah ini.

  4. Penandatanganan data.

    Metode ini menggunakan teknologi VMP untuk menandatangani data input dan mengembalikan sebuah string wtoken untuk otentikasi permintaan.

    /**
     * Menandatangani data.
     * @param input Data yang akan ditandatangani.
     * @return string wtoken.
     */
    - (NSString *)vmpSign:(NSData *)input;
    • Parameter:

      • input: Tipe: NSData. Data yang akan ditandatangani. Biasanya berupa seluruh request body atau whash dari custom signing.

    • Nilai kembalian: Tipe: NSString. String wtoken.

    • Contoh:

      // Signature default yang dikonfigurasi di Konsol (custom signing tidak dipilih).
      NSString *body = @"hello world";
      NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
      NSLog(@"wtoken: %@", wtoken);
      
      // Custom signing yang dikonfigurasi di Konsol.
      // Custom signing untuk permintaan POST.
      NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_POST input:[body dataUsingEncoding:NSUTF8StringEncoding]];
      NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
      NSLog(@"whash: %@, wtoken: %@", whash, wtoken);
      
      // Custom signing untuk permintaan GET.
      NSString *url = @"https://tigertally.aliyun.com/apptest";
      NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_GET input:[url dataUsingEncoding:NSUTF8StringEncoding]];
      NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
      NSLog(@"whash: %@, wtoken: %@", whash, wtoken);
      Catatan
      • Saat menggunakan custom signing, input untuk antarmuka vmpSign harus berupa string whash yang dihasilkan oleh vmpHash. Saat Anda mengonfigurasi kebijakan berbasis skenario untuk App Protection, Anda harus mengatur Custom Signing Field menjadi ali_sign_whash.

      • Saat memanggil vmpHash untuk menghasilkan whash untuk permintaan GET, pastikan URL input identik dengan URL permintaan jaringan akhir. Perhatikan dengan cermat pengkodean URL, karena beberapa framework secara otomatis melakukan encode terhadap karakter atau parameter tertentu.

      • Input untuk antarmuka vmpHash tidak boleh berupa array byte atau string kosong. Jika input berupa URL, URL tersebut harus mencakup path atau parameter.

      • Saat memanggil vmpSign, jika request body kosong (misalnya pada permintaan GET atau permintaan POST tanpa body), berikan nil atau representasi NSData dari string kosong, seperti [@"" dataUsingEncoding:NSUTF8StringEncoding].

      • Jika whash atau wtoken mengembalikan salah satu string berikut, hal ini menunjukkan adanya kesalahan:

        • you must call init first: Fungsi init belum dipanggil.

        • you must input correct data: Data input tidak valid.

        • you must input correct type: Tipe input tidak valid.

3. Verifikasi sekunder

  1. Periksa hasil.

    Untuk menentukan apakah diperlukan verifikasi sekunder, periksa cookie dan badan dari tanggapan. Jika header berisi beberapa bidang Set-Cookie, gabungkan menjadi satu string cookie sebelum memanggil antarmuka ini.

    /**
     * Memeriksa apakah perlu melakukan verifikasi sekunder.
     *
     * @param cookie  Cookie tanggapan.
     * @param body    Badan tanggapan.
     * @return 0: Lolos, 1: Diperlukan verifikasi sekunder.
     */
    - (int)cptCheck:(NSString *)cookie body:(NSData *)body;
    • Parameter:

      • cookie: Tipe: NSString. Semua cookie dalam tanggapan permintaan.

      • body: Tipe: NSData. Merupakan seluruh body dari tanggapan permintaan.

    • Nilai kembalian: Tipe: int. Hasil keputusan. 0 menunjukkan permintaan lolos, dan 1 menunjukkan bahwa verifikasi sekunder diperlukan.

      • Contoh:

      NSString *cookie = @"key1=value1;key2=value2;";
      NSData *body = xxx;
      int recheck = [[AliTigerTally sharedInstance] cptCheck:cookie body:body];
      NSLog(@"recheck: %d", recheck);
  2. Buat slider.

    Jika hasil dari cptCheck menunjukkan perlunya verifikasi, buat objek slider. Objek TTCaptcha menyediakan metode show dan dismiss untuk mengontrol jendela slider. Gunakan TTOption untuk mengonfigurasi parameternya dan TTDelegate untuk menangani callback statusnya. Untuk menyesuaikan jendela slider, Anda harus menyediakan URL ke halaman kustom, yang dapat berupa file HTML lokal atau halaman remote.

    /**
     * Menampilkan slider untuk verifikasi.
     *
     * @param view      Komponen induk.
     * @param option    Parameter.
     * @param detegate  Protokol callback.
     */
    - (TTCaptcha *)cptCreate:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)detegate;
    
    
    @protocol TTDelegate <NSObject>
    @required
    // Verifikasi slider berhasil.
    - (void)success:(TTCaptcha *)captcha data:(NSString *)data;
    
    // Verifikasi slider gagal.
    - (void)failed:(TTCaptcha *)captcha code:(NSString *)code;
    @end
    
    
    @interface TTOption : NSObject
    // Ketuk untuk membatalkan.
    @property (nonatomic, assign) BOOL cancelable;
    
    // Halaman kustom.
    @property (nonatomic, strong) NSString *customUri;
    
    // Bahasa.
    @property (nonatomic, strong) NSString *language;
    @end
    
    
    @interface TTCaptcha : NSObject
    
    - (instancetype)init:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)delegate;
    
    // Mendapatkan traceId slider untuk analitik data.
    - (NSString *)getTraceId;
    
    // Menampilkan slider.
    - (void)show;
    
    // Menutup slider.
    - (void)dismiss;
    
    @end
    • Parameter:

      • view: Tipe: View. Tampilan halaman saat ini.

      • option: Tipe: TTOption. Parameter konfigurasi slider.

      • delegate: Tipe: TTDelegate. Callback status slider.

    • Nilai kembalian: Tipe: TTCaptcha. Objek slider.

    • Contoh:

      #pragma mark - TTDelegate
      - (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code {
          NSLog(@"captcha gagal: %@", code);
      }
      
      - (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data {
          NSLog(@"captcha berhasil: %@", data);
      }
      
      TTOption *option = [[TTOption alloc] init];
      // option.customUri = @"ali-tt-captcha-demo-ios";
      option.language   = @"cn";
      option.cancelable = true;
      
      TTCaptcha *captcha = [[AliTigerTally sharedInstance] cptCreate:[self view] option:option delegate:self];
      [captcha show];
      Catatan

      Kesalahan "Verifikasi gagal" menunjukkan bahwa terdeteksi adanya anomali selama atau setelah interaksi pengguna dengan slider.

      Tabel berikut menjelaskan kode kesalahan.

      • 1001: Tantangan verifikasi gagal.

      • 1002: Terjadi anomali sistem.

      • 1003: Parameter yang ditentukan tidak valid.

      • 1005: Verifikasi dibatalkan.

      • 8001: Gagal menampilkan slider.

      • 8002: Terjadi anomali pada data verifikasi slider.

      • 8003: Terjadi anomali internal selama verifikasi slider.

      • 8004: Terjadi kesalahan jaringan.

Praktik terbaik

#import "DemoController.h"
#if __has_include(<AliTigerTally_NOIDFA/AliTigerTally_NOIDFA.h>)
    #import <AliTigerTally_NOIDFA/AliTigerTally_NOIDFA.h>
#else
    #import <AliTigerTally_IDFA/AliTigerTally_IDFA.h>
#endif

@interface DemoController () <TTDelegate>

@end

static NSString *kAppHost = @"******";
static NSString *kAppUrl  = @"******";
static NSString *kAppkey  = @"******";


@implementation DemoController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Lakukan pengaturan tambahan setelah memuat tampilan.


    [self doTest];
}

- (void)doTest {

    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        // Inisialisasi SDK.
        NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
        // [options setValue:@"1" forKey:@"Intl"];     // Konfigurasi untuk pelaporan internasional.
        int code = [[AliTigerTally sharedInstance] init:kAppkey collectType:TT_DEFAULT options:options listener:nil];
        NSLog(@"tigertally init: %d", code);

        // Jangan lakukan panggilan sinkron segera.
        [NSThread sleepForTimeInterval:2.0];

        // Hasilkan signature.
        NSString *body = @"hello world";
        NSString *whash = nil, *wtoken = nil;
        // Penandatanganan kustom.
        whash  = [[AliTigerTally sharedInstance] vmpHash:TT_Post input:[body dataUsingEncoding:NSUTF8StringEncoding]];
        wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
        NSLog(@"tigertally vmp: %@, %@", whash, wtoken);

        // Penandatanganan default.
        // wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
        // NSLog(@"tigertally vmp: %@", wtoken);

        [self doPost:kAppUrl host:kAppHost whash:whash wtoken:wtoken body:[body dataUsingEncoding:NSUTF8StringEncoding] callback:^(NSInteger code, NSString* cookie, NSData *body) {
            int check = [[AliTigerTally sharedInstance] cptCheck:cookie body:body];
            NSLog(@"hasil captcha:%d", check);

            if (check == 0) return;
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                [self doShow];
            }];
        }];
    }];
    [thread start];
}

- (void)doShow {
    NSLog(@"captcha show");
    
    TTOption *option = [[TTOption alloc] init];
//    option.customUri = @"ali-tt-captcha-demo-ios";
    option.language   = @"cn";
    option.cancelable = true;
    
    TTCaptcha *captcha = [[AliTigerTally sharedInstance] cptCreate:[self view] option:option delegate:self];
    [captcha show];
}

- (void)doPost:(NSString *)url host:(NSString *)host whash:(NSString *)whash wtoken:(NSString *)wtoken body:(NSData *)body callback:(void(^)(NSInteger code, NSString* cookie, NSData *body))callback {
    NSLog(@"mulai reqeust post");
    
    NSURL* requestUrl = [NSURL URLWithString: url];
    NSData* requestBody = body;
    
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
    [request setValue: @"text/x-markdown" forHTTPHeaderField: @"Content-Type"];
    [request setValue: host forHTTPHeaderField: @"HOST"];
    [request setValue: wtoken forHTTPHeaderField:@"wToken"];
    if (whash) {
        [request setValue:whash forHTTPHeaderField:@"ali_sign_whash"];
    }
    request.HTTPMethod = @"post";
    request.HTTPBody = requestBody;
    
    NSURLSessionDataTask* dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            NSLog(@"tiger tally sign gagal: %@", [error description]);
            callback(-1, nil, [[error description] dataUsingEncoding:NSUTF8StringEncoding]);
            return;
        }
        
        NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
        
        NSInteger code = httpResponse.statusCode;
        NSString* body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSMutableString* cookies = [[NSMutableString alloc] init];
        for (NSHTTPCookie* cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
            if ([url containsString:[cookie domain]]) {
                NSLog(@"domain:%@, path: %@, name:%@, value: %@", [cookie domain], [cookie path], [cookie name], [cookie value]);
                [cookies appendFormat: @"%@=%@;", [cookie name], [cookie value]];
            }
        }
        NSLog(@"kode respons: %ld", code);
        NSLog(@"cookie respons: %@", cookies);
        NSLog(@"badan respons: %@", body ? (body.length > 100 ? [body substringToIndex:100]:body) : @"");
        
        callback(code, cookies, data);
    }];
    [dataTask resume];
}

#pragma mark - TTDelegate
- (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code {
    NSLog(@"captcha gagal: %@", code);

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:code preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
    [self presentViewController:alert animated:true completion:nil];
}

- (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data {
    NSLog(@"captcha berhasil: %@", data);

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:data preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
    [self presentViewController:alert animated:true completion:nil];
}

@end