afmotion 0.9.0 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,74 +1,25 @@
1
- module AFMotion
2
- class ClientDSL
3
- def initialize(client)
4
- @client = client
5
- end
6
-
7
- def header(header, value)
8
- @client.setDefaultHeader(header, value: value)
9
- end
10
-
11
- def authorization(options = {})
12
- @client.authorization = options
13
- end
14
-
15
- def operation(operation)
16
- klass = operation
17
- if operation.is_a?(Symbol) or operation.is_a?(String)
18
- klass = case operation.to_s
19
- when "json"
20
- AFJSONRequestOperation
21
- when "plist"
22
- AFPropertyListRequestOperation
23
- when "xml"
24
- AFXMLRequestOperation
25
- when "http"
26
- AFHTTPRequestOperation
27
- else
28
- raise "Not a valid operation: #{operation.inspect}"
29
- end
30
- end
31
-
32
- @client.registerHTTPOperationClass(klass)
33
- end
34
-
35
- def parameter_encoding(encoding)
36
- enc = encoding
37
- if encoding.is_a?(Symbol) or encoding.is_a?(String)
38
- enc = case encoding.to_s
39
- when "json"
40
- AFJSONParameterEncoding
41
- when "plist"
42
- AFPropertyListParameterEncoding
43
- when "form"
44
- AFFormURLParameterEncoding
45
- else
46
- p "Not a valid parameter encoding: #{encoding.inspect}; using AFFormURLParameterEncoding"
47
- AFFormURLParameterEncoding
48
- end
49
- end
50
- @client.parameterEncoding = enc
51
- end
52
- end
53
- end
1
+ motion_require 'version'
2
+ motion_require 'client_shared'
54
3
 
55
4
  module AFMotion
56
5
  class Client
57
6
  class << self
58
7
  attr_accessor :shared
59
8
 
60
- # Returns an instance of AFHTTPClient
9
+ # Returns an instance of AFHTTPRequestOperationManager
61
10
  def build(base_url, &block)
62
- client = AFHTTPClient.clientWithBaseURL(base_url.to_url)
11
+ operation_manager = AFHTTPRequestOperationManager.alloc.initWithBaseURL(base_url.to_url)
63
12
  if block
64
- dsl = AFMotion::ClientDSL.new(client)
13
+ dsl = AFMotion::ClientDSL.new(operation_manager)
65
14
  dsl.instance_eval(&block)
66
15
  end
67
- client
16
+ if !operation_manager.operationQueue
17
+ operation_manager.operationQueue = NSOperationQueue.mainQueue
18
+ end
19
+ operation_manager
68
20
  end
69
21
 
70
22
  # Sets AFMotion::Client.shared as the built client
71
- # TODO: Make sure this only happens once (dispatch_once not available)
72
23
  def build_shared(base_url, &block)
73
24
  self.shared = self.build(base_url, &block)
74
25
  end
@@ -76,135 +27,111 @@ module AFMotion
76
27
  end
77
28
  end
78
29
 
79
- class AFHTTPClient
80
- AFMotion::HTTP_METHODS.each do |method|
81
- # EX client.get('my/resource.json')
82
- define_method "#{method}", -> (path, parameters = {}, &callback) do
83
- if multipart?
84
- @operation = create_multipart_operation(method, path, parameters, &callback)
85
- self.enqueueHTTPRequestOperation(@operation)
86
- @multipart = nil
87
- else
88
- @operation = create_operation(method, path, parameters, &callback)
89
- self.enqueueHTTPRequestOperation(@operation)
90
- end
91
- @operation
30
+ module AFMotion
31
+ class ClientDSL
32
+ def initialize(operation_manager)
33
+ @operation_manager = WeakRef.new(operation_manager)
92
34
  end
93
- end
94
35
 
95
- def create_multipart_operation(method, path, parameters = {}, &callback)
96
- multipart_callback = callback.arity == 1 ? nil : lambda { |formData|
97
- callback.call(nil, formData)
98
- }
99
- upload_callback = callback.arity > 2 ? lambda { |bytes_written_now, total_bytes_written, total_bytes_expect|
100
- case callback.arity
101
- when 3
102
- callback.call(nil, nil, total_bytes_written.to_f / total_bytes_expect.to_f)
103
- when 5
104
- callback.call(nil, nil, bytes_written_now, total_bytes_written, total_bytes_expect)
105
- end
106
- } : nil
107
- request = self.multipartFormRequestWithMethod(method, path: path,
108
- parameters: parameters,constructingBodyWithBlock: multipart_callback)
109
- operation = self.HTTPRequestOperationWithRequest(request,
110
- success: lambda {|operation, responseObject|
111
- result = AFMotion::HTTPResult.new(operation, responseObject, nil)
112
- case callback.arity
113
- when 1
114
- callback.call(result)
115
- when 2
116
- callback.call(result, nil)
117
- when 3
118
- callback.call(result, nil, nil)
119
- when 5
120
- callback.call(result, nil, nil, nil, nil)
121
- end
122
- }, failure: lambda {|operation, error|
123
- result = AFMotion::HTTPResult.new(operation, nil, error)
124
- case callback.arity
125
- when 1
126
- callback.call(result)
127
- when 2
128
- callback.call(result, nil)
129
- when 3
130
- callback.call(result, nil, nil)
131
- when 5
132
- callback.call(result, nil, nil, nil, nil)
133
- end
134
- })
135
- if upload_callback
136
- operation.setUploadProgressBlock(upload_callback)
36
+ def header(header, value)
37
+ @operation_manager.headers[header] = value
137
38
  end
138
- operation
139
- end
140
39
 
141
- def create_operation(method, path, parameters = {}, &callback)
142
- request = self.requestWithMethod(method.upcase, path:path, parameters:parameters)
143
- self.HTTPRequestOperationWithRequest(request, success: lambda {|operation, responseObject|
144
- result = AFMotion::HTTPResult.new(operation, responseObject, nil)
145
- callback.call(result)
146
- }, failure: lambda {|operation, error|
147
- result = AFMotion::HTTPResult.new(operation, nil, error)
148
- callback.call(result)
149
- })
150
- end
40
+ def authorization(options = {})
41
+ @operation_manager.requestSerializer.authorization = options
42
+ end
151
43
 
152
- def multipart!
153
- @multipart = true
154
- self
155
- end
44
+ def operation_queue(operation_queue)
45
+ @operation_manager.operationQueue = operation_queue
46
+ end
156
47
 
157
- def multipart?
158
- !!@multipart
159
- end
48
+ OPERATION_TO_REQUEST_SERIALIZER = {
49
+ json: AFJSONRequestSerializer,
50
+ plist: AFPropertyListRequestSerializer
51
+ }
52
+ def request_serializer(serializer)
53
+ if serializer.is_a?(Symbol) || serializer.is_a?(String)
54
+ @operation_manager.requestSerializer = OPERATION_TO_REQUEST_SERIALIZER[serializer.to_sym].serializer
55
+ elsif serializer.is_a?(Class)
56
+ @operation_manager.requestSerializer = serializer.serializer
57
+ else
58
+ @operation_manager.requestSerializer = serializer
59
+ end
60
+ end
160
61
 
161
- # options can be
162
- # - {username: ___, password: ____}
163
- # or
164
- # - {token: ___ }
165
- def authorization=(options = {})
166
- if options.nil?
167
- clearAuthorizationHeader
168
- elsif options[:username] && options[:password]
169
- setAuthorizationHeaderWithUsername(options[:username], password: options[:password])
170
- elsif options[:token]
171
- setAuthorizationHeaderWithToken(options[:token])
172
- else
173
- raise "Not a valid authorization hash: #{options.inspect}"
62
+ OPERATION_TO_RESPONSE_SERIALIZER = {
63
+ json: AFJSONResponseSerializer,
64
+ xml: AFXMLParserResponseSerializer,
65
+ plist: AFPropertyListResponseSerializer,
66
+ image: AFImageResponseSerializer,
67
+ http: AFHTTPResponseSerializer,
68
+ form: AFHTTPResponseSerializer
69
+ }
70
+ def response_serializer(serializer)
71
+ if serializer.is_a?(Symbol) || serializer.is_a?(String)
72
+ @operation_manager.responseSerializer = OPERATION_TO_RESPONSE_SERIALIZER[serializer.to_sym].serializer
73
+ elsif serializer.is_a?(Class)
74
+ @operation_manager.responseSerializer = serializer.serializer
75
+ else
76
+ @operation_manager.responseSerializer = serializer
77
+ end
174
78
  end
175
79
  end
80
+ end
176
81
 
177
- class HeaderWrapper
178
- def initialize(client)
179
- @client = WeakRef.new(client)
180
- end
82
+ # TODO: remove this when/if https://github.com/AFNetworking/AFNetworking/issues/1388 resolved
83
+ module AFMotion
84
+ module QueryPairHelper
85
+ module_function
181
86
 
182
- def [](header)
183
- @client.defaultValueForHeader(header)
87
+ def af_QueryStringPairsFromDictionary(dictionary)
88
+ af_QueryStringPairsFromKeyAndValue(nil, dictionary)
184
89
  end
185
90
 
186
- def []=(header, value)
187
- @client.setDefaultHeader(header, value: value)
188
- end
91
+ def af_QueryStringPairsFromKeyAndValue(key, value)
92
+ mutableQueryStringComponents = []
93
+ if value.is_a?(NSDictionary)
94
+ sortDescriptor = NSSortDescriptor.sortDescriptorWithKey("description", ascending: true, selector:'caseInsensitiveCompare:')
95
+ value.allKeys.sortedArrayUsingDescriptors([sortDescriptor]).each do |nestedKey|
96
+ nestedValue = value[nestedKey]
97
+ if nestedValue
98
+ mutableQueryStringComponents += af_QueryStringPairsFromKeyAndValue(key ? "#{key}[#{nestedKey}]" : nestedKey, nestedValue)
99
+ end
100
+ end
101
+ elsif value.is_a?(NSArray)
102
+ value.each do |obj|
103
+ mutableQueryStringComponents += af_QueryStringPairsFromKeyAndValue(key, obj)
104
+ end
105
+ elsif value.is_a?(NSSet)
106
+ value.each do |obj|
107
+ mutableQueryStringComponents += af_QueryStringPairsFromKeyAndValue(key, obj)
108
+ end
109
+ else
110
+ mutableQueryStringComponents << AFQueryStringPair.alloc.initWithField(key, value: value)
111
+ end
189
112
 
190
- def delete(header)
191
- value = self[header]
192
- @client.setDefaultHeader(header, value: nil)
193
- value
113
+ mutableQueryStringComponents
194
114
  end
195
115
  end
196
116
 
197
- def headers
198
- @header_wrapper ||= HeaderWrapper.new(self)
117
+ class MultipartParametersWrapper < Hash
118
+ def initialize(parameters)
119
+ super()
120
+ query_pairs = QueryPairHelper.af_QueryStringPairsFromDictionary(parameters)
121
+ query_pairs.each do |key_pair|
122
+ self[key_pair.field] = key_pair.value
123
+ end
124
+ end
199
125
  end
126
+ end
200
127
 
201
- private
202
- # To force RubyMotion pre-compilation of these methods
203
- def dummy
204
- self.getPath("", parameters: nil, success: nil, failure: nil)
205
- self.postPath("", parameters: nil, success: nil, failure: nil)
206
- self.putPath("", parameters: nil, success: nil, failure: nil)
207
- self.deletePath("", parameters: nil, success: nil, failure: nil)
208
- self.patchPath("", parameters: nil, success: nil, failure: nil)
128
+ class AFHTTPRequestOperationManager
129
+ include AFMotion::ClientShared
130
+
131
+ AFMotion::HTTP_METHODS.each do |method|
132
+ # EX client.get('my/resource.json')
133
+ define_method "#{method}", -> (path, parameters = {}, &callback) do
134
+ create_operation(method, path, parameters, &callback)
135
+ end
209
136
  end
210
137
  end
@@ -1,9 +1,15 @@
1
1
  module AFMotion
2
2
  class HTTPResult
3
- attr_accessor :operation, :object, :error
3
+ attr_accessor :operation, :object, :error, :task
4
4
 
5
- def initialize(operation, responseObject, error)
6
- self.operation = operation
5
+ def initialize(operation_or_task, responseObject, error)
6
+ if operation_or_task.is_a?(NSURLSessionTask) ||
7
+ # cluser class ugh
8
+ operation_or_task.class.to_s.include?("Task")
9
+ self.task = operation_or_task
10
+ else
11
+ self.operation = operation_or_task
12
+ end
7
13
  self.object = responseObject
8
14
  self.error = error
9
15
  end
@@ -17,6 +23,9 @@ module AFMotion
17
23
  end
18
24
 
19
25
  def body
26
+ if task && task.currentRequest
27
+ raise "Cannot call result.body of a task"
28
+ end
20
29
  if operation && operation.responseString
21
30
  operation.responseString
22
31
  end
@@ -1,83 +1,69 @@
1
1
  module AFMotion
2
2
  module Operation
3
- module HTTP
4
- def self.for_request(request, &callback)
5
- operation = AFHTTPRequestOperation.alloc.initWithRequest(request)
6
- operation.setCompletionBlockWithSuccess(
7
- lambda { |operation, responseObject|
8
- result = AFMotion::HTTPResult.new(operation, responseObject, nil)
9
- callback.call(result)
10
- },
11
- failure: lambda {|operation, error|
12
- result = AFMotion::HTTPResult.new(operation, nil, error)
13
- callback.call(result)
14
- }
15
- )
16
- operation
3
+ module_function
4
+ def for_request(ns_url_request, &callback)
5
+ operation = AFHTTPRequestOperation.alloc.initWithRequest(ns_url_request)
6
+ success_block = success_block_for_http_method(ns_url_request.HTTPMethod, callback)
7
+ operation.setCompletionBlockWithSuccess(success_block, failure: failure_block(callback))
8
+ operation
9
+ end
10
+
11
+ def success_block_for_http_method(http_method, callback)
12
+ if http_method.downcase.to_sym == :head
13
+ return lambda { |operation|
14
+ AFMotion::HTTPResult.new(operation, nil, nil)
15
+ }
17
16
  end
17
+
18
+ lambda { |operation, responseObject|
19
+ result = AFMotion::HTTPResult.new(operation, responseObject, nil)
20
+ callback.call(result)
21
+ }
18
22
  end
19
23
 
20
- module JSON
21
- def self.for_request(request, &callback)
22
- operation = AFJSONRequestOperation.JSONRequestOperationWithRequest(request,
23
- success: lambda { |request, response, json|
24
- result = AFMotion::HTTPResult.new(operation, json, nil)
25
- callback.call(result)
26
- },
27
- failure: lambda { |request, response, error, json|
28
- result = AFMotion::HTTPResult.new(operation, json, error)
29
- callback.call(result)
30
- }
31
- )
32
- end
24
+ def failure_block(callback)
25
+ lambda { |operation, error|
26
+ result = AFMotion::HTTPResult.new(operation, nil, error)
27
+ callback.call(result)
28
+ }
33
29
  end
30
+ end
34
31
 
35
- module XML
36
- def self.for_request(request, &callback)
37
- operation = AFXMLRequestOperation.XMLParserRequestOperationWithRequest(request,
38
- success: lambda { |request, response, document_or_parser|
39
- result = AFMotion::HTTPResult.new(operation, document_or_parser, nil)
40
- callback.call(result)
41
- },
42
- failure: lambda { |request, response, error, document_or_parser|
43
- result = AFMotion::HTTPResult.new(operation, document_or_parser, error)
44
- callback.call(result)
45
- }
46
- )
47
- end
32
+ module Serialization
33
+ def with_request_serializer(serializer_klass)
34
+ self.requestSerializer = serializer_klass.serializer
35
+ self
48
36
  end
49
37
 
50
- module PLIST
51
- def self.for_request(request, &callback)
52
- operation = AFPropertyListRequestOperation.propertyListRequestOperationWithRequest(request,
53
- success: lambda { |request, response, propertyList|
54
- result = AFMotion::HTTPResult.new(operation, propertyList, nil)
55
- callback.call(result)
56
- },
57
- failure: lambda { |request, response, error, propertyList|
58
- result = AFMotion::HTTPResult.new(operation, propertyList, error)
59
- callback.call(result)
60
- }
61
- )
62
- end
38
+ def with_response_serializer(serializer_klass)
39
+ self.responseSerializer = serializer_klass.serializer
40
+ self
63
41
  end
64
42
 
65
- module Image
66
- def self.for_request(request, &callback)
67
- operation = AFImageRequestOperation.imageRequestOperationWithRequest(request,
68
- imageProcessingBlock: lambda {|ui_image|
69
- return ui_image
70
- },
71
- success: lambda { |request, response, ui_image|
72
- result = AFMotion::HTTPResult.new(operation, ui_image, nil)
73
- callback.call(result)
74
- },
75
- failure: lambda { |request, response, error|
76
- result = AFMotion::HTTPResult.new(operation, nil, error)
77
- callback.call(result)
78
- }
79
- )
80
- end
43
+ def json!
44
+ with_request_serializer(AFJSONRequestSerializer).
45
+ with_response_serializer(AFJSONResponseSerializer)
46
+ end
47
+
48
+ def xml!
49
+ with_response_serializer(AFXMLParserResponseSerializer)
50
+ end
51
+
52
+ def plist!
53
+ with_request_serializer(AFPropertyListRequestSerializer).
54
+ with_response_serializer(AFPropertyListResponseSerializer)
55
+ end
56
+
57
+ def image!
58
+ with_response_serializer(AFImageResponseSerializer)
81
59
  end
82
60
  end
61
+ end
62
+
63
+ class AFHTTPRequestOperation
64
+ include AFMotion::Serialization
65
+ end
66
+
67
+ class AFHTTPRequestOperationManager
68
+ include AFMotion::Serialization
83
69
  end
File without changes
@@ -0,0 +1,41 @@
1
+ class AFHTTPRequestSerializer
2
+ class HeaderWrapper
3
+ def initialize(serializer)
4
+ @serializer = WeakRef.new(serializer)
5
+ end
6
+
7
+ def [](header)
8
+ @serializer.HTTPRequestHeaders[header]
9
+ end
10
+
11
+ def []=(header, value)
12
+ @serializer.setValue(value, forHTTPHeaderField: header)
13
+ end
14
+
15
+ def delete(header)
16
+ value = self[header]
17
+ self[header] = nil
18
+ value
19
+ end
20
+ end
21
+
22
+ def headers
23
+ @header_wrapper ||= HeaderWrapper.new(self)
24
+ end
25
+
26
+ # options can be
27
+ # - {username: ___, password: ____}
28
+ # or
29
+ # - {token: ___ }
30
+ def authorization=(options = {})
31
+ if options.nil?
32
+ clearAuthorizationHeader
33
+ elsif options[:username] && options[:password]
34
+ setAuthorizationHeaderFieldWithUsername(options[:username], password: options[:password])
35
+ elsif options[:token]
36
+ setAuthorizationHeaderFieldWithToken(options[:token])
37
+ else
38
+ raise "Not a valid authorization hash: #{options.inspect}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,128 @@
1
+ motion_require 'http_client'
2
+ motion_require 'version'
3
+
4
+ =begin
5
+ AFMotion::SessionClient.build("http://google.com") do |client|
6
+ client.session_configuration :default # :ephemeral
7
+ client.session_configuration :background, "com.usepropeller.afmotion"
8
+ client.session_configuration my_session_configuration
9
+
10
+ response_serializer
11
+
12
+ request_serializer
13
+ end
14
+ =end
15
+ module AFMotion
16
+ class SessionClient
17
+ class << self
18
+
19
+ attr_accessor :shared
20
+
21
+ # Returns an instance of AFHTTPRequestOperationManager
22
+ def build(base_url, &block)
23
+ dsl = AFMotion::SessionClientDSL.new(base_url)
24
+ case block.arity
25
+ when 0
26
+ dsl.instance_eval(&block)
27
+ when 1
28
+ block.call(dsl)
29
+ end
30
+
31
+ dsl.to_session_manager
32
+ end
33
+
34
+ # Sets AFMotion::Client.shared as the built client
35
+ def build_shared(base_url, &block)
36
+ self.shared = self.build(base_url, &block)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ module AFMotion
43
+ class SessionClientDSL < ClientDSL
44
+ class Config
45
+ attr_accessor :responseSerializer, :operationQueue, :requestSerializer, :sessionConfiguration
46
+
47
+ class MockRequestSerializer
48
+ attr_accessor :authorization
49
+ end
50
+
51
+ def requestSerializer
52
+ @requestSerializer ||= MockRequestSerializer.new
53
+ end
54
+
55
+ def requestSerializer=(requestSerializer)
56
+ if @requestSerializer && @requestSerializer.is_a?(MockRequestSerializer)
57
+ requestSerializer.authorization = @requestSerializer.authorization
58
+ end
59
+ @requestSerializer = requestSerializer
60
+ end
61
+
62
+ def headers
63
+ @headers ||= {}
64
+ end
65
+ end
66
+
67
+ attr_accessor :operation_manager
68
+ alias_method :config, :operation_manager
69
+
70
+ def initialize(base_url)
71
+ @base_url = base_url
72
+ @operation_manager = Config.new
73
+ end
74
+
75
+ def to_session_manager
76
+ session_manager = AFHTTPSessionManager.alloc.initWithBaseURL(@base_url.to_url,
77
+ sessionConfiguration: config.sessionConfiguration)
78
+ session_manager.responseSerializer = config.responseSerializer if config.responseSerializer
79
+ if !config.requestSerializer.is_a?(Config::MockRequestSerializer)
80
+ session_manager.requestSerializer = config.requestSerializer
81
+ elsif config.requestSerializer.authorization
82
+ session_manager.requestSerializer.authorization = config.requestSerializer.authorization
83
+ end
84
+ config.headers.each do |key, value|
85
+ session_manager.requestSerializer.headers[key] = value
86
+ end
87
+ session_manager.operationQueue = config.operationQueue if config.operationQueue
88
+ session_manager
89
+ end
90
+
91
+ SESSION_CONFIGURATION_SHORTHAND = {
92
+ default: :defaultSessionConfiguration,
93
+ ephemeral: :ephemeralSessionConfiguration,
94
+ background: "backgroundSessionConfiguration:".to_sym
95
+ }
96
+
97
+ def session_configuration(session_configuration, identifier = nil)
98
+ if session_configuration.is_a?(Symbol) || session_configuration.is_a?(String)
99
+ method_signature = SESSION_CONFIGURATION_SHORTHAND[session_configuration.to_sym]
100
+ ns_url_session_configuration = begin
101
+ if identifier
102
+ NSURLSessionConfiguration.send(method_signature, identifier)
103
+ else
104
+ NSURLSessionConfiguration.send(method_signature)
105
+ end
106
+ end
107
+ self.config.sessionConfiguration = ns_url_session_configuration
108
+ elsif session_configuration.is_a?(NSURLSessionConfiguration) ||
109
+ # cluster class smh
110
+ session_configuration.class.to_s.include?("URLSessionConfiguration")
111
+ self.config.sessionConfiguration = session_configuration
112
+ else
113
+ raise "Invalid type for session_configuration; need Symbol, String, or NSURLSessionConfiguration, but got #{session_configuration.class}"
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ class AFHTTPSessionManager
120
+ include AFMotion::ClientShared
121
+
122
+ AFMotion::HTTP_METHODS.each do |method|
123
+ # EX client.get('my/resource.json')
124
+ define_method "#{method}", -> (path, parameters = {}, &callback) do
125
+ create_task(method, path, parameters, &callback)
126
+ end
127
+ end
128
+ end
@@ -1,5 +1,5 @@
1
1
  module AFMotion
2
- VERSION = "0.9.0"
2
+ VERSION = "2.0.0.rc1"
3
3
 
4
4
  HTTP_METHODS = [:get, :post, :put, :delete, :patch, :head]
5
5
  end
data/lib/afmotion.rb CHANGED
@@ -1,15 +1,21 @@
1
1
  require "afmotion/version"
2
2
  require 'motion-cocoapods'
3
+ require 'motion-require'
3
4
 
4
5
  unless defined?(Motion::Project::Config)
5
6
  raise "This file must be required within a RubyMotion project Rakefile."
6
7
  end
7
8
 
9
+ Motion::Require.all(Dir.glob(File.join(File.dirname(__FILE__), 'afmotion/**/*.rb')))
10
+
8
11
  Motion::Project::App.setup do |app|
9
12
  Dir.glob(File.join(File.dirname(__FILE__), 'afmotion/**/*.rb')).each do |file|
10
- app.files.unshift(file)
11
13
  if app.respond_to?("exclude_from_detect_dependencies")
12
14
  app.exclude_from_detect_dependencies << file
13
15
  end
14
16
  end
17
+
18
+ app.pods do
19
+ pod 'AFNetworking', '~> 2.0.0'
20
+ end
15
21
  end