This repository was archived by the owner on Sep 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy pathSession+CAPTCHA.swift
159 lines (141 loc) · 6.54 KB
/
Session+CAPTCHA.swift
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
//
// Session+CAPTCHA.swift
// reddift
//
// Created by sonson on 2015/05/19.
// Copyright (c) 2015年 sonson. All rights reserved.
//
import Foundation
#if os(iOS) || os(tvOS)
import UIKit
public typealias CAPTCHAImage = UIImage
#elseif os(macOS)
import Cocoa
public typealias CAPTCHAImage = NSImage
#endif
/**
Parse simple string response for "/api/needs_captcha"
- parameter data: Binary data is returned from reddit.
- returns: Result object. If data is "true" or "false", Result object has boolean, otherwise error object.
*/
func data2Bool(_ data: Data) -> Result<Bool> {
if let decoded = String(data: data, encoding: .utf8) {
if decoded == "true" {
return Result(value: true)
} else if decoded == "false" {
return Result(value: false)
}
}
return Result(error: ReddiftError.needsCAPTCHAResponseIsInvalid as NSError)
}
/**
Parse simple string response for "/api/needs_captcha"
- parameter data: Binary data is returned from reddit.
- returns: Result object. If data is "true" or "false", Result object has boolean, otherwise error object.
*/
func data2Image(_ data: Data) -> Result<CAPTCHAImage> {
#if os(iOS) || os(tvOS)
let captcha = UIImage(data: data)
#elseif os(macOS)
let captcha = NSImage(data: data)
#endif
return Result(fromOptional: captcha, error: ReddiftError.imageOfCAPTCHAIsInvalid as NSError)
}
/**
Parse JSON contains "iden" for CAPTHA.
{"json": {"data": {"iden": "<code>"},"errors": []}}
- parameter json: JSON object, like above sample.
- returns: Result object. When parsing is succeeded, object contains iden as String.
*/
func idenJSON2String(_ json: JSONAny) -> Result<String> {
if let json = json as? JSONDictionary {
if let j = json["json"] as? JSONDictionary {
if let data = j["data"] as? JSONDictionary {
if let iden = data["iden"] as? String {
return Result(value: iden)
}
}
}
}
return Result(error: ReddiftError.identifierOfCAPTCHAIsMalformed as NSError)
}
extension Session {
/**
Check whether CAPTCHAs are needed for API methods that define the "captcha" and "iden" parameters.
- parameter completion: The completion handler to call when the load request is complete.
- returns: Data task which requests search to reddit.com.
*/
@discardableResult
public func checkNeedsCAPTCHA(_ completion: @escaping (Result<Bool>) -> Void) throws -> URLSessionDataTask {
guard let request = URLRequest.requestForOAuth(with: baseURL, path: "/api/needs_captcha", method: "GET", token: token)
else { throw ReddiftError.canNotCreateURLRequest as NSError }
let closure = {(data: Data?, response: URLResponse?, error: NSError?) -> Result<Bool> in
return Result(from: Response(data: data, urlResponse: response), optional: error)
.flatMap(response2Data)
.flatMap(data2Bool)
}
return executeTask(request, handleResponse: closure, completion: completion)
}
/**
Responds with an iden of a new CAPTCHA.
Use this endpoint if a user cannot read a given CAPTCHA, and wishes to receive a new CAPTCHA.
To request the CAPTCHA image for an iden, use /captcha/iden.
- parameter completion: The completion handler to call when the load request is complete.
- returns: Data task which requests search to reddit.com.
*/
@discardableResult
public func getIdenForNewCAPTCHA(_ completion: @escaping (Result<String>) -> Void) throws -> URLSessionDataTask {
let parameter = ["api_type": "json"]
guard let request = URLRequest.requestForOAuth(with: baseURL, path: "/api/new_captcha", parameter: parameter, method: "POST", token: token)
else { throw ReddiftError.canNotCreateURLRequest as NSError }
let closure = {(data: Data?, response: URLResponse?, error: NSError?) -> Result<String> in
return Result(from: Response(data: data, urlResponse: response), optional: error)
.flatMap(response2Data)
.flatMap(data2Json)
.flatMap(idenJSON2String)
}
return executeTask(request, handleResponse: closure, completion: completion)
}
/**
Request a CAPTCHA image for given an iden.
An iden is given as the captcha field with a BAD_CAPTCHA error, you should use this endpoint if you get a BAD_CAPTCHA error response.
Responds with a 120x50 image/png which should be displayed to the user.
The user's response to the CAPTCHA should be sent as captcha along with your request.
To request a new CAPTCHA, Session.getIdenForNewCAPTCHA.
- parameter iden: Code to get a new CAPTCHA. Use Session.getIdenForNewCAPTCHA.
- parameter completion: The completion handler to call when the load request is complete.
- returns: Data task which requests search to reddit.com.
*/
@discardableResult
public func getCAPTCHA(_ iden: String, completion: @escaping (Result<CAPTCHAImage>) -> Void) throws -> URLSessionDataTask {
guard let request = URLRequest.requestForOAuth(with: baseURL, path: "/captcha/" + iden, method: "GET", token: token)
else { throw ReddiftError.canNotCreateURLRequest as NSError }
let closure = {(data: Data?, response: URLResponse?, error: NSError?) -> Result<CAPTCHAImage> in
return Result(from: Response(data: data, urlResponse: response), optional: error)
.flatMap(response2Data)
.flatMap(data2Image)
}
return executeTask(request, handleResponse: closure, completion: completion)
}
/**
Request a CAPTCHA image
Responds with a 120x50 image/png which should be displayed to the user.
The user's response to the CAPTCHA should be sent as captcha along with your request.
- parameter completion: The completion handler to call when the load request is complete.
- returns: Data task which requests search to reddit.com.
*/
public func getCAPTCHA(_ completion: @escaping (Result<CAPTCHAImage>) -> Void) throws {
do {
try getIdenForNewCAPTCHA { (result) -> Void in
switch result {
case .failure(let error):
completion(Result(error: error))
case .success(let iden):
do {
try self.getCAPTCHA(iden, completion: completion)
} catch { completion(Result(error: error as NSError)) }
}
}
} catch { completion(Result(error: error as NSError)) }
}
}