I posted a heavy question recently surrounding this topic, but I need to cover the basics first before I can start doing other tasks. I would like to know how to get data from an ip address instead of a standard url that returns JSON data like so:
func simpleGetRequest()
{
let parameters = ["takepic":"true"]
Alamofire.request("http://XXX.XXX.X.XX", parameters:parameters).responseJSON { (response) in
if let status = response.response?.statusCode
{
switch(status)
{
case 200:
print("Successfully taken a pic")
case 500:
print("Failed to connect")
default:
print("Error with everything")
}
}
if let result = response.result.value
{
print(result)
}
}
}
I've heard some things about IPv6 and IPv4, but am not sure as to what they mean. I'll continue to do more research, however I can't find a single source that shows how to communicate with non-local web servers.
Related
I've been searching the internet and still cannot find an answer.
My app talks to other smart home products within the home. For example, it can make requests to the Philips Hue Bridge to control the lights via POST requests. The IP of my bridge is 192.168.0.12. I am making a POST request to this endpoint, however it isn't allowed as the connection is unsecure.
I still want to keep the setting of where external connections to domains are secure since I connect to my own server via a domain, which is secure. So I only want to allow local connections via local IP addresses to be unsecure.
I have tried this:
Yet it doesn't work. I've even tried using Allow Arbitrary Loads just for testing to see if it would work, and it still wouldn't.
My API call:
func getPhilipsHueUsername(completion: #escaping (String?, Error?) -> Void){
var bridgeIP = UserDefaults.standard.string(forKey: "bridgeIP")
let url = "http://" + bridgeIP! + "/api"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = HTTPMethod.post.rawValue
let body = [
"devicetype": "test"
]
do {
let dataToS = try JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
request.httpBody = dataToS
}catch{
print("Error creating data object")
return
}
AF.request(request).responseJSON { (response) in
switch response.result {
case .success(let value):
print(value)
if(response != nil){
let json = JSON(value)
print(value)
}
return completion("",nil)
case .failure(let error):
return completion(nil, error)
}
}
}
ok so i've been trying to get this problem figured out for 2 days now, hoping someone can help.
quick background, i’m making an api request for data. using a function that calls a service function i made. now everything works good on the first load, collectionview loads fine. at some point i run another call for more data. accept now i get a URL Error.
This doesn’t work on my iphone, but does work perfectly on simulator, so not sure what it could be.
heres the service function that makes the api request:
func fetchYoutubeData(interest: String, maxResult: Int, pageToken: String, completion: #escaping(Result<Youtube, WHError>) -> Void) {
let urlString = baseYoutubeURL+interest+youtubeAPIKey+"&maxResults=\(maxResult)&pageToken=\(pageToken)"
guard let url = URL(string: urlString) else {
completion(.failure(.URLError))
return
}
let session = URLSession.shared
let task = session.dataTask(with: url) { (data, _, error) in
if let _ = error {
completion(.failure(.DataError))
return
}
guard let data = data else { return }
do {
let result = try JSONDecoder().decode(Youtube.self, from: data)
print(result)
completion(.success(result))
} catch {
completion(.failure(.JSONError))
}
}
task.resume()
}
heres my controller function that calls service and handles the data on completion:
func fetchNewData(maxResult: Int, pageToken: String) {
guard let interest = self.interest.text else { return }
print(pageToken)
NetworkServices.shared.fetchYoutubeData(interest: interest, maxResult: maxResult, pageToken: pageToken) { [unowned self] (result) in
switch result {
case .success(let youtubeGroup):
let items = youtubeGroup.items
self.youtubeData.items.append(contentsOf: items)
DispatchQueue.main.async {
self.horizontalCollectionView.reloadData()
}
case .failure(let error):
print("DOES GET ERROR")
print(error)
}
}
}
again it works perfectly on simulator, but not on my device, i can get first call to work, but after that, once i use pageToken to get more data, i get a URL Error.
any help would be seriously appreciated
so of course like alot of things , it's something small I missed. I'll keep the question here, incase someone finds themself in a similar situation.
the problem was in the first request I reformat the "interest" to remove spaces from the string and replace them with +. you need to do this for the Youtube API query string that you provide.
i did this in the first request, but for my query for additional data, I forgot to reformat the request in the separate call.
easy fix thankfully
I added "App Transport Security Settings" to the Info.plist file then selected "Allow Arbitrary Loads" and set that value to "YES" and that worked. My assumption is that there was an issue with the security cert on the client development environment that was preventing the app from making calls over HTTPS.
And never fear, I only allow arbitrary loads in the dev and qa environments.
I'm testing out the Kentico Cloud Swift SDK to return some 'article' content types (I have created two of them and they are published).
I am using the Boilerplate code as described here:
The result I get is : [Kentico Cloud] Getting items action has succeeded. Received nil items.
My code:
let client = DeliveryClient.init(projectId: <project id>, previewApiKey: <preview key>, secureApiKey: <secure key>, enableDebugLogging: true)
func getArticles(){
// Note: Using "items" as custom query returns all content items,
// but to map them to a single model, a filter is needed.
let customQuery = "items?system.type=article"
// More about strongly-typed models https://github.com/Kentico/cloud-sdk-swift#using-strongly-typed-models
client.getItems(modelType: Article.self, customQuery: customQuery) { (isSuccess, itemsResponse, error) in
if isSuccess {
// We get here and itemsResponse != nil but items == nil
if let articles = itemsResponse?.items {
for article in articles {
}
}
} else {
if let error = error {
print(error)
}
}
}
}
I believe this error message would appear before ObjectMapper is triggered to convert the JSON into Article objects. I could be wrong though.
Anyone have any ideas?
UPDATE
Interestingly, if I request a single article object like so ...
client.getItem(modelType: Article.self, itemName: <codename>) { (isSuccess, itemResponse, error) in
if isSuccess {
if let article = itemResponse?.item {
// Use your item here
}
} else {
if let error = error {
print(error)
}
}
}
... then it works. I get the Article object. It's just asking for all of the articles that fails.
I'm going to investigate the issue later today, however, from your description, it might be caused by the Delivery API item readiness delay - the project was not fully synced with the delivery API yet. After the publishing/unpublishing item or creating/generating the project, there might be a small delay in processing messages by Delivery API which could cause unavailability of item. This delay might be variable - from my experience, it may vary from a couple of seconds to 2-3 minutes. Nevertheless, I'm going to check it just to be sure. I'll keep you updated.
Edit: I'm pretty sure the project was not synced and processed on the Delivery API at the time you were requested the items. The API returned 200, which caused isSuccess in the callback to be true, however, there might have been none or just a subset of items available - I've reproduced this behavior (screenshot below), although it's by design (the content/messages in Event Hub must be processed asynchronously).
I've also suggested the improvement for Kentico Cloud's documentation to mention/explain the possible delay caused by processing events queue messages from Event Hubs.
Just to be sure - could you try it again with your getArticles custom query?
Edit2: Back to your question about the ObjectMapper. This is not an error just a debug message, however, there shouldn't be probably nil but 0 (zero) in the debug message. This message came from:
private func sendGetItemsRequest<T>(url: String, completionHandler: #escaping (Bool, ItemsResponse<T>?, Error?) -> ()) where T: Mappable {
sessionManager.request(url, headers: self.headers).responseObject { (response: DataResponse<ItemsResponse<T>>) in
switch response.result {
case .success:
if let value = response.result.value {
let deliveryItems = value
if self.isDebugLoggingEnabled {
print("[Kentico Cloud] Getting items action has succeeded. Received \(String(describing: deliveryItems.items?.count)) items.")
}
completionHandler(true, deliveryItems, nil)
}
case .failure(let error):
if self.isDebugLoggingEnabled {
print("[Kentico Cloud] Getting items action has failed. Check requested URL: \(url)")
}
completionHandler(false, nil, error)
}
}
}
Ok. This is very weird. After checking the API by requesting an individual item (see the update in the post above), and getting a result (woot). It now seems the original code (unchanged) now works.
I'm wondering if it takes a while for the data to propagate and be available in the API?
Who knows. Weird.
I'm trying to develop a download accelerator in Swift. It should get the file's size and divide it to n parts. Then it should download them at once by running multiple threads, and then merge the parts.
I read C# - Creating a Download Accelerator, unfortunately it doesn't help me.
I can do the multiple thread part easily by
DispatchQueue.main.async {
// The new thread
}
but the other part is harder. I usually download a file like this:
try Data(contentsOf: URL(string: assetsUrl!)!)
or I can do the thing that is explained in this answer
class Downloader {
class func load(url: URL, to localUrl: URL, completion: #escaping () -> ()) {
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = try! URLRequest(url: url, method: .get)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Success: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: localUrl)
completion()
} catch (let writeError) {
print("error writing file \(localUrl) : \(writeError)")
}
} else {
print("Failure: %#", error?.localizedDescription);
}
}
task.resume()
}
}
But this is not C - it's very simplistic and doesn't accept many arguments. How can I make it get "first 200_000 bytes" from the server?
First of all, the server needs to implement HTTP range requests. If it doesn't, and you don't control the server, then you will not be able to do this.
If the server supports HTTP range requests, then you need to specify the range with request headers, as explained here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
The essentials are that you first send a HEAD request to figure out whether the server supports HTTP range requests. This is determined by whether the response includes the Accept-Ranges header, with a non-zero value.
If the server supports HTTP range requests, then you can make a request for the resource, with the Range header set for example to a value of bytes=0-1023 (depends which format the Accept-Ranges header specified, in this case bytes)
Recently, I found a pure swift socket server and client called IBM BlueSocket.
It is suitable for me that it does server-cleint communication.
It has a pretty simple sample. but I encountered some problems.
1. How to run it on a GUI application's run loop?
2. How to run it and support multi connections?
For what it's worth, I present the world's simplest chat client.
import Foundation
import Socket
// Very simplified chat client for BlueSocket - no UI, it just connects to the echo server,
// exchanges a couple of messages and then disconnects.
// You can run two instances of Xcode on your Mac, with the BlueSocketEchoServer running in one and
// this program running in the other. It has been tested running in the iPhone simulator, i.e.,
// under iOS, without problems.
// License: Public domain.
public class BlueSocketChatClient {
public func runClient() {
do {
let chatSocket = try Socket.create(family: .inet6)
try chatSocket.connect(to: "127.0.0.1", port: 1337)
print("Connected to: \(chatSocket.remoteHostname) on port \(chatSocket.remotePort)")
try readFromServer(chatSocket)
try chatSocket.write(from: "Hello to you too!")
try readFromServer(chatSocket)
try chatSocket.write(from: "Bye now!\n")
try chatSocket.write(from: "QUIT")
sleep(1) // Be nice to the server
chatSocket.close()
}
catch {
guard let socketError = error as? Socket.Error else {
print("Unexpected error ...")
return
}
print("Error reported:\n \(socketError.description)")
}
}
// This is very simple-minded. It blocks until there is input, and it then assumes that all the
// relevant input has been read in one go.
func readFromServer(_ chatSocket : Socket) throws {
var readData = Data(capacity: chatSocket.readBufferSize)
let bytesRead = try chatSocket.read(into: &readData)
guard bytesRead > 0 else {
print("Zero bytes read.")
return
}
guard let response = String(data: readData, encoding: .utf8) else {
print("Error decoding response ...")
return
}
print(response)
}
}
Bill Abt: If you can use this in any way you're welcome to it.
The sample has been recently updated and illustrates use of the GCD based Dispatch API to do multi-threading and supporting multiple connections. It also should give you a idea on how to run it on the main queue (which'll work for either a GUI or server application).