iOS/Swift & Coinbase Pro API - Subscribe to Websocket Feed - ios

I am attempting to subscribe to a websocket feed using Swift. Per the new Coinbase Pro API documentation for their websocket feed:
To begin receiving feed messages, you must first send a subscribe message to the server indicating which channels and products to receive. This message is mandatory — you will be disconnected if no subscribe has been received within 5 seconds.
The first thing I did was add Starscream to the project to make connecting to websockets easier to implement. I followed the steps on the README and added the delegate methods appropriately.
Next, I successfully sent an HTTP GET request (I get a 200 code in response) by creating a URLSession object and calling dataTask(with: ) after setting up a request, like so:
let session = URLSession.shared
guard let url = URL(string: "https://api.pro.coinbase.com/users/self/verify") else {
print("Could not create URL.")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
let body: [String: Any] = ["type": "subscribe",
"channels": [["name": "heartbeat"],
["product_ids": ["BTC-USD"]]]]
let data = try! JSONSerialization.data(withJSONObject: body,
options: JSONSerialization.WritingOptions.sortedKeys)
request.httpBody = data
let task = session.dataTask(with: request) { (data, response, error) in
// Check for errors, clean up data, etc.
}
task.resume
Everything appears to be linked up correctly, but I am still not receiving the "subscription" messages from the websocket feed. What am I missing?

Related

SwiftUI: URL Request over specific browser

I want to retrieve a json from an intranet from outside the intranet in an iPad app. I have a VPN connection configured for this, but it only runs through the VMWare web browser. Safari uses the normal connection.
Is it possible, similar to the browser schemas, to use awbs:// instead of https:// and trigger the VPN configuration from an app? I don't want to open the browser though, I just want to use it. ;)
func getData() async {
#State var value: [responsejson]?
guard let url = URL(string: "https:/...") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {data, response, error in
if let data = data {
if let decodedResponse = try? JSONDecoder().decode(responsejson.self, from: data) {
DispatchQueue.main.async {
self.response = decodedResponse.text
}
return
}
}
print("Error: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
I tried to replace here https with awbs, but unfortunately it did not work
Task <1039D056-FDC4-46EE-9635-373AF8723409>.<1> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL, NSErrorFailingURLStringKey=awbs://(...)
http,https or awbs is not just a text, but a definition of the protocol. Protocol defines how the data will be transferred, and there is a specific list of protocol that iOS support. see https://en.wikipedia.org/wiki/Internet_protocol_suite (don't be confused with app-scheme, when you open a link and specific app response to it, these are two different things).
So when you try to create an instance of a URL object it actually parses a protocol and uses instructions to make a Request.

HTTP DELETE Works From Browser But Not From Postman or IOS App

When attempting an http request to my rest api, I continually get a 401 error when using the following code. I don not get this error making any other type of request. I have provided the function that makes the request below.
func deleteEvent(id: Int){
eventUrl.append(String(id))
let request = NSMutableURLRequest(url: NSURL(string: eventUrl)! as URL)
request.httpMethod = "DELETE"
print(eventUrl)
eventUrl.removeLast()
print(self.token!)
request.allHTTPHeaderFields = ["Authorization": "Token \(self.token)"]
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
if error != nil {
print("error=\(String(describing: error))")
//put variable that triggers error try again view here
return
}
print("response = \(String(describing: response))")
}
task.resume()
}
When sending the delete request with postman, the rest api just returns the data I want to delete but does not delete it. For reference I have posted the view and permissions classes associated with this request Any help understanding why this may be resulting in an error is greatly appreciated!
Views.py
class UserProfileFeedViewSet(viewsets.ModelViewSet):
"""Handles creating, reading and updating profile feed items"""
authentication_classes = (TokenAuthentication,)
serializer_class = serializers.ProfileFeedItemSerializer
queryset = models.ProfileFeedItem.objects.all()
permission_classes = (permissions.UpdateOwnStatus, IsAuthenticated)
def perform_create(self, serializer):
"""Sets the user profile to the logged in user"""
#
serializer.save(user_profile=self.request.user)
Permissions.py
class UpdateOwnStatus(permissions.BasePermission):
"""Allow users to update their own status"""
def has_object_permission(self, request, view, obj):
"""Check the user is trying to update their own status"""
if request.method in permissions.SAFE_METHODS:
return True
return obj.user_profile.id == request.user.id
HEADER SENT WITH DELETE REQUEST VIA POSTMAN
Preface: You leave out too much relevant information from the question for it to be properly answered. Your Swift code looks, and please don't be offended, a bit beginner-ish or as if it had been migrated from Objective-C without much experience.
I don't know why POSTMAN fails, but I see some red flags in the Swift code you might want to look into to figure out why your iOS app fails.
I first noticed that eventUrl seems to be a String property of the type that contains the deleteEvent function. You mutate it by appending the event id, construct a URL from it (weirdly, see below), then mutate it back again. While this in itself is not necessarily wrong, it might open the doors for racing conditions depending how your app works overall.
More importantly: Does your eventUrl end in a "/"? I assume your DELETE endpoint is of the form https://somedomain.com/some/path/<id>, right? Now if eventUrl just contains https://somedomain.com/some/path your code constructs https://somedomain.com/some/path<id>. The last dash is missing, which definitely throws your backend off (how I cannot say, as that depends how the path is resolved in your server app).
It's hard to say what else is going from from the iOS app, but other than this potential pitfall I'd really recommend using proper Swift types where possible. Here's a cleaned up version of your method, hopefully that helps you a bit when debugging:
func deleteEvent(id: Int) {
guard let baseUrl = URL(string: eventUrl), let token = token else {
// add more error handling code here and/or put a breakpoint here to inspect
print("Could not create proper eventUrl or token is nil!")
return
}
let deletionUrl = baseUrl.appendingPathComponent("\(id)")
print("Deletion URL with appended id: \(deletionUrl.absoluteString)")
var request = URLRequest(url: deletionUrl)
request.httpMethod = "DELETE"
print(token) // ensure this is correct
request.allHTTPHeaderFields = ["Authorization": "Token \(token)"]
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Encountered network error: \(error)")
return
}
if let httpResponse = response as? HTTPURLResponse {
// this is basically also debugging code
print("Endpoint responded with status: \(httpResponse.statusCode)")
print(" with headers:\n\(httpResponse.allHeaderFields)")
}
// Debug output of the data:
if let data = data {
let payloadAsSimpleString = String(data: data, encoding: .utf8) ?? "(can't parse payload)"
print("Response contains payload\n\(payloadAsSimpleString)")
}
}
task.resume()
}
This is obviously still limited in terms of error handling, etc., but a little more swifty and contains more console output that will hopefully be helpful.
The last important thing is that you have to ensure iOS does not simply block your request due to Apple Transport Security: Make sure your plist has the expected entries if needed (see also here for a quick intro).

URLSession.Datatask returns 0 bytes of data

Trying to figure this one out, I'm stumped. When making a REST call to get json data back from a response (GET or POST, each should return data) I get back 0 bytes.
This is pre-serialization. The POST successfully creates a message on the backend, and the backend shows a response being sent; with charles proxy on, I've confirmed that there is a response with valid JSON data.
Any ideas why this would be failing ONLY in iOS? Postman/Charles proxy (from the iOS calls!) shows valid data in the response, but the debugger picks up nothing.
Thanks in advance for anything thoughts.
let components = URLComponents(string: "mysuperValidURL.com")
guard let url = components?.url else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
setUrlRequestToken(request: &request)
let message = ChatMessage(content: message, group: group, userId: userId)
let jsonEncoder = JSONEncoder()
guard let data = try? jsonEncoder.encode(message) else {
return
}
URLSession.shared.uploadTask(with: request, from: data) { (data, response, error) in
// Here there be 0 bytes
}.resume()
}
Data will sometimes come back as 0 bytes in the debugger; add a print with debug description to ensure you're getting data. In this case it was a failure of the debugger mixed with a later serialization error that caused it to appear to be broken.
TLDR; don't trust the realtime debugger, use some prints to sanity check.

http request delete and put

Below is my code for HTTP request for getting and post. What I wanted to know is how to do HTTP request for delete and put. I have made it possible for get and post to work. I want to know how on the part of delete and put based on my code below. what to change in my postcode when I want to change it to delete? what is lacking? I wanted to delete using ID
like for example "id": 16,
let parameters = ["name": "test", "desc": "test" , "reward":"1.00" , "sched":"2018-04-05T11:49:51+08:00", "occurrence":["name": "once"]
, "status": "created", "created_by": "test#gmail.com","created_for": "test.com"] as [String : Any]
guard let url = URL(string: "http://test.tesst.eu:8000/api/v1/test/") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else { return }
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
get
guard let url = URL(string: "http://test.test:8000/api/v1/test") else { return }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
The only one that differs is GET with the parameters are in the url itself , POST & DELETE & PUT are work the same way , just change the httpMethod parameter and specify httpBody if you wanted to , meaning
DELETE : means delete a resource from a specific url
PUT : place a resource in to a web server
//
let parameters = ["ID": "16"] as [String : Any]
guard let url = URL(string: "http://test.tesst.eu:8000/api/v1/test/") else { return }
var request = URLRequest(url: url)
request.httpMethod = "DELETE"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject:parameters, options: []) else { return }
request.httpBody = httpBody
You should go through difference between each method types. It will help you, what should you do and when.
PUT
Store an entity at a URI. PUT can create a new entity or update an existing one. A PUT request is idempotent. Idempotency is the main difference between the expectations of PUT versus a POST request.
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate successful completion of the request. If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be given that reflects the nature of the problem. The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not understand or implement and MUST return a 501 (Not Implemented) response in such cases.
Modify the address with an ID of 1:
PUT /addresses/1
Note: PUT replaces an existing entity. If only a subset of data elements are provided, the rest will be replaced with empty or null.
urlRequestInstance.httpMethod = "PUT"
DELETE
Request that a resource be removed; however, the resource does not have to be removed immediately. It could be an asynchronous or long-running request.
The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden by human intervention (or other means) on the origin server. The client cannot be guaranteed that the operation has been carried out, even if the status code returned from the origin server indicates that the action has been completed successfully. However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location.
A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if the action has not yet been enacted, or 204 (No Content) if the action has been enacted but the response does not include an entity.
Delete an address with an ID of 1:
DELETE /addresses/1
urlRequestInstance.httpMethod = "DELETE"
Here are nice tutorial references for you:
Understanding REST
REST Methods
What is the usefulness of PUT and DELETE HTTP request methods?
Now you can try a sample code, answered by Sh_Khan

Twilio Send SMS in swift

I'm trying to make an app that when you press a button it sends a text message to a preset number, and I am using Twilio for the SMS messages. The issue is that I am using Swift for my app, and they have no examples in swift currently. This is my code for sending the message:
func sendSMS()
{
let twilioSID = "AC11ed62fdb971a8f56d9be531a5ce40c2"
let twilioSecret = "mySecretID"
let fromNumber = "number"
let toNumber = "number"
let message = "This is a test message"
// Build the request
let request = NSMutableURLRequest(URL: NSURL(string:"https://\(twilioSID):\(twilioSecret)#api.twilio.com/2010-04-01/Accounts/\(twilioSID)/SMS/Messages")!)
request.HTTPMethod = "POST"
request.HTTPBody = "From=\(fromNumber)&To=\(toNumber)&Body=\(message)".dataUsingEncoding(NSUTF8StringEncoding)
// Build the completion block and send the request
NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data, response, error) in
print("Finished")
if let data = data, responseDetails = NSString(data: data, encoding: NSUTF8StringEncoding) {
// Success
print("Response: \(responseDetails)")
} else {
// Failure
print("Error: \(error)")
}
}).resume()
}
But whenever I try and run that function, I get this
Finished
Response: <?xml version='1.0' encoding='UTF-8'?>
<TwilioResponse><RestException><Code>20003</Code><Detail>Your AccountSid or AuthToken was incorrect.</Detail><Message>Authentication Error - No credentials provided</Message><MoreInfo>https://www.twilio.com/docs/errors/20003</MoreInfo><Status>401</Status></RestException></TwilioResponse>
I know that my credentials are correct.....
Is there a better way of doing this? Does anyone have an example out there?
Matthew, as mentioned in the comments above/we do not recommend that you send SMS from the REST API via client side code for security reasons.
We suggest that you wrap your credentials and sending of the SMS in a backend app using one of the available helper libraries in the examples seen here:
https://www.twilio.com/docs/api/rest/sending-messages
A colleague of mine actually wrote a post to address this for the Android community and it looks like we should definitely do the same for Swift, which would be more my speed.
[Update]: How to send an SMS from Swift post.
Hope this helps.

Resources