I'm new to swift and OS X programming. For my first steps I wanted to create a command line tool which logs me into the webinterface of my mobile carrier, and then shows the amount of my data left. I began to write a wrapper around NSURLSession to make things easier for me.
Problem: My program won't accept cookies.
I tried a lot of things, also setting cookie policies on the session object but nothing changed. How can I make my program accept cookies and how to use them in subsequent requests?
HttpClient.swift:
import Foundation
class HttpClient {
private var url: NSURL!
private var session: NSURLSession
internal init(url: String) {
self.url = NSURL(string: url)
self.session = NSURLSession.sharedSession()
session.configuration.HTTPShouldSetCookies = true
session.configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicy.OnlyFromMainDocumentDomain
session.configuration.HTTPCookieStorage?.cookieAcceptPolicy = NSHTTPCookieAcceptPolicy.OnlyFromMainDocumentDomain
}
internal func sendGet() -> String {
var ready = false
var content: String!
var request = NSMutableURLRequest(URL: self.url)
var task = session.dataTaskWithRequest(request) {
(data, response, error) -> Void in
content = NSString(data: data, encoding: NSASCIIStringEncoding) as! String
ready = true
}
task.resume()
while !ready {
usleep(10)
}
if content != nil {
return content
} else {
return ""
}
}
internal func sendPost(params: String) -> String {
var ready = false
var content: String!
var request = NSMutableURLRequest(URL: self.url)
request.HTTPMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.HTTPBody = params.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
request.HTTPShouldHandleCookies = true
var task = session.dataTaskWithRequest(request) {
(data, response, error) -> Void in
content = NSString(data: data, encoding: NSASCIIStringEncoding) as! String
ready = true
}
task.resume()
while !ready {
usleep(10)
}
if content != nil {
return content
} else {
return ""
}
}
internal func setUrl(url: String) {
self.url = NSURL(string: url)
}
}
main.swift
import Foundation
let loginPage = "https://service.winsim.de/"
let dataPage = "https://service.winsim.de/mytariff/invoice/showGprsDataUsage"
var hc = HttpClient(url: loginPage)
println(hc.sendPost("usernameField=username&passwordfield=password"))
hc.setUrl(dataPage)
println(hc.sendGet())
Issue is solved
In fact, cookies are accepted with the above code. I tried logging in to a different site and it worked. Also the login persisted. So why it did not work with my carrier's website?
Stupid me, my carrier has CSRF protection and other hidden form fields which I did not pay attention to. Hence, login did not work. Now I know how to fix it.
For anyone interested, I'll post my updated HttpClient.swift file which is a bit more tidy, I hope.
Please feel free to comment on my code and give me hints for improvement.
import Foundation
public class HttpClient {
private var session: NSURLSession
private var request: NSMutableURLRequest
public init(url: String) {
self.session = NSURLSession.sharedSession()
session.configuration.HTTPShouldSetCookies = true
session.configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicy.OnlyFromMainDocumentDomain
session.configuration.HTTPCookieStorage?.cookieAcceptPolicy = NSHTTPCookieAcceptPolicy.OnlyFromMainDocumentDomain
self.request = NSMutableURLRequest(URL: NSURL(string: url)!)
}
public func send() -> String {
var ready = false
var content: String!
var task = session.dataTaskWithRequest(self.request) {
(data, response, error) -> Void in
content = NSString(data: data, encoding: NSASCIIStringEncoding) as! String
ready = true
}
task.resume()
while !ready {
usleep(10)
}
if content != nil {
return content
} else {
return ""
}
}
public func setUrl(url: String) -> HttpClient {
self.request.URL = NSURL(string: url)
return self
}
public func getMethod() -> String {
return self.request.HTTPMethod
}
public func setMethod(method: String) -> HttpClient {
self.request.HTTPMethod = method
return self
}
public func addFormData(data: Dictionary<String, String>) -> HttpClient {
var params: String = ""
var ctHeader: String? = self.request.valueForHTTPHeaderField("Content-Type")
let ctForm: String = "application/x-www-form-urlencoded"
if(data.count > 0) {
for(name, value) in data {
params += name + "=" + value + "&"
}
params = params.substringToIndex(params.endIndex.predecessor())
self.request.HTTPBody = params.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
if ctHeader != nil {
self.request.setValue(ctForm, forHTTPHeaderField: "Content-Type")
}
}
return self
}
public func removeFormData() -> HttpClient {
self.request.setValue("text/html", forHTTPHeaderField: "Content-Type")
self.request.HTTPBody = "".dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
return self
}
}
Related
I created a reusable Alamofire request which works smoothly. I am trying to get the data from the request Decode it(works fine too) and append it to one of my arrays to display in tableView/collectionView.
I am using MVVM and I append my data in viewModel(you can see below). The thing is I have tableView in my viewController and inside my tableView methods( viewForSupplementaryElementOfKind for instance) the 'stories'(from viewModel) are always empty.
In my viewDidLoad method I call getMainPageData(from viewModel) first and then create my tableView. I assure you the request itself is a success, the only problem is displaying the data.
Please keep in mind that the project has many API calls so I need a solution which will work in all cases when I have to deal with "lists". Thank you in advance
class NetworkManager {
let keychain = KeychainManager()
let base = "SomeBase"
let storageManager = StorageManager()
func setupRequest(path: Paths, method: RequestMethod, body: Encodable? = nil, params: [String: Any]? = nil, header: HeaderType, completion: #escaping((Result<Data,NetworkError>) -> Void)) {
var queries = ""
if let params = params {
queries = params.passingQuery()
}
let url = URL(string: base + path.rawValue + queries)
var request = URLRequest(url: url!)
request.httpMethod = method.rawValue
request.setValue(header.value[0].value, forHTTPHeaderField: "Content-Type")
if let userToken = keychain.getAccessToken(), userToken.count > 0 {
request.setValue("Bearer " + userToken, forHTTPHeaderField: "Authorization")
}
if let body = body {
if let jsonData = body.toJSONData() {
request.httpBody = jsonData
}
}
AF.request(request).validate().responseJSON { response in
if (200...299) ~= response.response?.statusCode ?? -1 {
self.handlingHeaders(response: response)
completion(.success(response.data!))
} else {
do {
if let data = response.data {
let json = try JSONDecoder().decode(ErrorResponse.self, from: data)
completion(.failure(.responseError(json.message)))
}
} catch {
completion(.failure(.serverError))
}
}
}
}
private func handlingHeaders(response: AFDataResponse<Any>) {
let headers = response.response?.headers
if let accessToken = headers?.dictionary["Authorization"] {
keychain.saveToken(token: accessToken)
}
}
}
extension Encodable {
func toJSONData() -> Data? { try? JSONEncoder().encode(self) }
}
var stories = [Story]()
func getMainPageData(completion: #escaping(Result<Void, NetworkError>) -> ()) {
networkManager.setupRequest(path: .mainPageData, method: .get, body: nil, params: nil, header: .application_json) { [self] result in
switch result {
case .success(let data):
do {
let homePageData = try JSONDecoder().decode(MainPageResponse.self, from: data)
stories.append(contentsOf: homePageData.model.stories)
I am new in Swift and SwiftUI. I need to send a POST request to the server when a button is clicked. The request is sent, everything is fine.
ShareHelper.swift:
import Foundation
class ShareHelper {
var dataString: String = ""
var newsMessage: String
var newsUser: String
var newsEmail: String
var newsMedia: String
let newsAPI: String = "https://example.com/api/shareNews"
init(message: String, user: String, email: String, media: String) {
self.newsMessage = message
self.newsUser = user
self.newsEmail = email
self.newsMedia = media
self.RequestPost()
}
func RequestPost() {
let url = URL(string: self.newsAPI)
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
let postString = "message=\(self.newsMessage)&user=\(self.newsUser)&email=\(self.newsEmail)&media=\(self.newsMedia)"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
return
}
if let data = data, let dataString = String(data: data, encoding: .utf8) {
self.dataString = dataString
}
}
task.resume()
}
}
Button from ContentView.swift
Button(action: {
let shareHelper = ShareHelper(message: self.textAreaText, user: self.inputUser, email: self.inputEmail, media: self.inputMedia)
shareHelper.RequestPost()
print("Share helper: \(shareHelper.dataString)")
}, label: {
Text("Send")
})
How to make a message appear in the console if POST request successful sending?
You need to use completion block.
func RequestPost(completion: #escaping((String) -> Void)) {
let url = URL(string: self.newsAPI)
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
let postString = "message=\(self.newsMessage)&user=\(self.newsUser)&email=\(self.newsEmail)&media=\(self.newsMedia)"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
return
}
if let data = data, let dataString = String(data: data, encoding: .utf8) {
DispatchQueue.main.async {
self.dataString = dataString
completion(dataString)
}
}
}
task.resume()
}
And on button
Button(action: {
let shareHelper = ShareHelper(message: "self.textAreaText", user: "self.inputUser", email: "self.inputEmail", media: "self.inputMedia")
shareHelper.RequestPost { (dataString) in
print("Share helper: \(shareHelper.dataString)")
// Or
print("Share helper: \(dataString)")
}
}, label: {
Text("Send")
})
To get a direct link to YouTube video I made request to get the get_video_info file, in this file that link, but I have to parse it, I find the solution to parse it by PHP but I want to parse it directly from my App
I get the data from my code like this:
let youtubeContentID = "sZz7tiToK1U"
if let infoURL = URL(string:"https://www.youtube.com/get_video_info?video_id=\(youtubeContentID)") {
let request = URLRequest(url: infoURL)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
if let error = error {
print(error)
} else if let data = data, let result = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
print(result)
}
})
task.resume()
}
}
I got data with many Ascii symbols like this:
c=WEB&vss_host=s.youtube.com&innertube_api_version=v1&xhr_apiary_host=youtubei.youtube.com&apiary_host_firstparty=&status=ok&t=1&enabled_engage_types=3%2C6%2C4%2C5%2C17%2C1&ssl=1&adaptive_fmts=type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26eotf%3Dbt709%26projection_type%3D1%26lmt%3D1550114115045036%26bitrate%3D16962786%26size%3D3840x2160%26index%3D221-1771%26quality_label%3D2160p%26xtags%3D%26url%3Dhttps%253A%252F%252Fr2---sn-nuj-wxqek.googlevideo.com%252Fvideoplayback%253Fexpire%253D1551500605%2526usequic%253Dno%2526gir%253Dyes%2526mime%253Dvideo%25252Fwebm%2526requiressl%253Dyes%2526keepalive%253Dyes%2526fvip%253D2%2526clen%253D547103152%2526source%253Dyoutube%2526aitags%253D133%25252C134%25252C135%25252C136%25252C137%25252C160%25252C242%25252C243%25252C244%25252C247%25252C248%25252C271%25252C278%25252C313%25252C394%25252C395%25252C396%25252C397%2526signature%253D9C28A5C103FA95701CD3655795DDB7F2C0954828.55534AC9C7BEE5D644C3A34C2CD4A4EEC9E2FD38%2526lmt%253D1550114115045036%2526ip%253D129.208.30.232%2526key%253Dyt6%2526c%253DWEB%2526ei%253D3bB5XKeFFNWd1wbBpqjgAg%2526txp%253D5531432%2526id%253Do-AIOVogCB8KFe32o_VgxSx-LqaEjNBZxiZ1jl81VTXZhF%2526sparams%253Daitags%25252Cclen%25252Cdur%25252Cei%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Ckeepalive%25252Clmt%25252Cmime%25252Cmm%25252Cmn%25252Cms%25252Cmv%25252Cpl%25252Crequiressl%25252Csource%25252Cusequic%25252Cexpire%2526initcwndbps%253D296250%2526itag%253D313%2526ms%253Dau%25252Crdu%2526mt%253D1551478917%2526mv%253Dm%2526dur%253D428.933%2526pl%253D19%2526ipbits%253D0%2526mm%253D31%25252C29%2526mn%253Dsn-nuj-wxqek%25252Csn-hgn7yn7l%26clen%3D547103152%26init%3D0-220%26itag%3D313%26primaries%3Dbt709%26fps%3D30%2Ctype%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26eotf%3Dbt709%26projection_type%3D1%26lmt%3D1550113771564979%26bitrate%3D6318874%26size%3D2560x1440%26index%3D220-1763%26quality_label%3D1440p%26xtags%3D%26url%3Dhttps%253A%252F%252Fr2---sn-nuj-wxqek.googlevideo.com%252Fvideoplayback%253Fexpire%253D1551500605%2526usequic%253Dno%2526gir%253Dyes%2526mime%253Dvideo%25252Fwebm%2526requiressl%253Dyes%2526keepalive%253Dyes%2526fvip%253D2%2526clen%253D155020170%2526source%253Dyoutube%2526aitags%253D133%25252C134%25252C135%25252C136%25252C137%25252C160%25252C242%25252C243%25252C244%25252C247%25252C248%25252C271%25252C278%25252C313%25252C394%25252C395%25252C396%25252C397%2526signature%253D4D7BF761EE4A6DFD048DE3D48550FCE80E61B7D0.D625BCC9645471ABA478F79C197B2208F354E15F%2526lmt%253D1550113771564979%2526ip%253D129.208.30.232%2526key%253Dyt6%2526c%253DWEB%2526ei%253D3bB5XKeFFNWd1wbBpqjgAg%2526txp%253D5531432%2526id%253Do-AIOVogCB8KFe32o_VgxSx- .... ext
I find code can deal with this YouTube request, it created on 2015 so it can't run on the new Swift, so I edited it to work on Swift 4, just copy this code in side a new Swift file then call function h264videosWithYoutubeID(youtubeID: your TouTube ID) and you will get the correct url:
import UIKit
public extension NSURL {
func dictionaryForQueryString() -> [String: AnyObject]? {
if let query = self.query {
return query.dictionaryFromQueryStringComponents()
}
let result = absoluteString?.components(separatedBy: "?")
if result!.count > 1 {
return result!.last?.dictionaryFromQueryStringComponents()
}
return nil
}
}
public extension NSString {
func stringByDecodingURLFormat() -> String {
let result = self.replacingOccurrences(of: "+", with: " ")
return result.removingPercentEncoding!
}
func dictionaryFromQueryStringComponents() -> [String: AnyObject] {
var parameters = [String: AnyObject]()
for keyValue in components(separatedBy: "&") {
let keyValueArray = keyValue.components(separatedBy: "=")
if keyValueArray.count < 2 {
continue
}
let key = keyValueArray[0].stringByDecodingURLFormat()
let value = keyValueArray[1].stringByDecodingURLFormat()
parameters[key] = value as AnyObject
}
return parameters
}
}
public class YoutubeUrlReciver: NSObject {
static let infoURL = "http://www.youtube.com/get_video_info?video_id="
static var userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.79 Safari/537.4"
public static func youtubeIDFromYoutubeURL(youtubeURL: NSURL) -> String? {
if let
youtubeHost = youtubeURL.host,
let youtubePathComponents = youtubeURL.pathComponents {
let youtubeAbsoluteString = youtubeURL.absoluteString
if youtubeHost == "youtu.be" as String? {
return youtubePathComponents[1]
} else if youtubeAbsoluteString?.range(of: "www.youtube.com/embed") != nil {
return youtubePathComponents[2]
} else if youtubeHost == "youtube.googleapis.com" ||
youtubeURL.pathComponents!.first == "www.youtube.com" as String? {
return youtubePathComponents[2]
} else if let
queryString = youtubeURL.dictionaryForQueryString(),
let searchParam = queryString["v"] as? String {
return searchParam
}
}
return nil
}
public static func h264videosWithYoutubeID(youtubeID: String) -> [String: AnyObject]? {
let urlString = String(format: "%#%#", infoURL, youtubeID) as String
let url = NSURL(string: urlString)!
let request = NSMutableURLRequest(url: url as URL)
request.timeoutInterval = 5.0
request.setValue(userAgent, forHTTPHeaderField: "User-Agent")
request.httpMethod = "GET"
var responseString = NSString()
let session = URLSession(configuration: URLSessionConfiguration.default)
let group = DispatchGroup()
group.enter()
session.dataTask(with: request as URLRequest, completionHandler: { (data, response, _) -> Void in
if let data = data as NSData? {
responseString = NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue)!
}
group.leave()
}).resume()
group.wait()
let parts = responseString.dictionaryFromQueryStringComponents()
if parts.count > 0 {
var videoTitle: String = ""
var streamImage: String = ""
if let title = parts["title"] as? String {
videoTitle = title
}
if let image = parts["iurl"] as? String {
streamImage = image
}
if let fmtStreamMap = parts["url_encoded_fmt_stream_map"] as? String {
// Live Stream
if let _: AnyObject = parts["live_playback"]{
if let hlsvp = parts["hlsvp"] as? String {
return [
"url": "\(hlsvp)" as AnyObject,
"title": "\(videoTitle)" as AnyObject,
"image": "\(streamImage)" as AnyObject,
"isStream": true as AnyObject
]
}
} else {
let fmtStreamMapArray = fmtStreamMap.components(separatedBy: ",")
for videoEncodedString in fmtStreamMapArray {
var videoComponents = videoEncodedString.dictionaryFromQueryStringComponents()
videoComponents["title"] = videoTitle as AnyObject
videoComponents["isStream"] = false as AnyObject
return videoComponents as [String: AnyObject]
}
}
}
}
return nil
}
public static func h264videosWithYoutubeURL(youtubeURL: NSURL,completion: ((
_ videoInfo: [String: AnyObject]?, _ error: NSError?) -> Void)?) {
DispatchQueue.global().async {
if let youtubeID = self.youtubeIDFromYoutubeURL(youtubeURL: youtubeURL), let videoInformation = self.h264videosWithYoutubeID(youtubeID: youtubeID) {
DispatchQueue.main.async {
completion?(videoInformation, nil)
}
}else{
DispatchQueue.main.async {
completion?(nil, NSError(domain: "com.player.youtube.backgroundqueue", code: 1001, userInfo: ["error": "Invalid YouTube URL"]))
}
}
}
}
}
I want to make use of the RequestAdapter and RequestRetrier protocols, so I created my own so called AuthenticationHandler class which implements both protocols. I do this because the refresh token may be expired so this mechanism comes in handy.
The RequestAdapter protocol method adapt does get called, but the should RequestRetrier protocol method does not. I have a separate class that does the actual request:
class TestRequest {
var authHandler: AuthenticationHandler?
func executeRequest() {
// For testing purposes a false access token is passed
self.authHandler = AuthenticationHandler(accessToken: "some_default_token")
let sessionManager = SessionManager()
sessionManager.adapter = authHandler
sessionManager.retrier = authHandler
var loginModel : LoginMessage = LoginMessage.init()
loginModel.username = "someUserName"
loginModel.password = "WrongPassword"
do {
let binaryData = try loginModel.serializedData()
// Create a file with this binary data in order to use it as part of the multipart formdata
guard let fileURL = createFileFrom(data: binaryData) else {
print("Error creating file")
return
}
// Note: custom headers have been set in the AuthenticationHandler
sessionManager.upload(multipartFormData: { multipartFormData in
multipartFormData.append(fileURL, withName: "content")
},
to: K.endpointLogin) { (encodingResult) in
switch encodingResult{
case .success(let upload, _, _):
upload.responseJSON { response in
print("Encoding result success...")
print("Statuscode: \(response.response?.statusCode)")
print(response)
}
case .failure(let encodingError):
print("Failure: \(encodingError)")
}
}
} catch {
print(error)
}
}
I have followed the example in the documentation here
I have read several previous posts saying it has to do with retaining the sessionManager. But I think that is also covered. My authentication handler looks like this:
class AuthenticationHandler: RequestAdapter, RequestRetrier {
private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?) -> Void
private let sessionManager: SessionManager = {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
}()
private let lock = NSLock()
private var accessToken: String
private var isRefreshing = false
private var requestsToRetry: [RequestRetryCompletion] = []
init(accessToken: String) {
self.accessToken = accessToken
}
// MARK: - RequestAdapter protocol method
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var urlRequest = urlRequest
if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(K.SERVER_URL) {
urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
urlRequest.setValue("multipart/form-data", forHTTPHeaderField: "Content-Type")
urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
}
return urlRequest
}
// MARK: - RequestRetrier protocol method
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: #escaping RequestRetryCompletion) {
lock.lock() ; defer { lock.unlock() }
}
}
My config is as follows:
Alamofire version: 4.7.2
Xcode version: 9.4.1
Swift version: 4
What am I doing wrong here? Why is the request cancelled and is the should method not called?
Any help is greatly appreciated.
Your core issue is that your SessionManager instance is being deinited, which cancels any ongoing tasks. You should keep it around in a singleton or something similar, which will fix the other issue, that of using a new SessionManager for each request, which is an anti pattern.
I am trying to use the Google Places Web Service through iOS (using swift).
The reason I want to do so - I want to allow browsing places on a map, even if they are not nearby (the only search allowed using the iOS provided library).
I also need to do it programmatically (since in one use-case I want to allow browsing the map, while in another use case - I want the places to be constant and not change based on the map camera).
I can't use the iOS place picker, since I want the map to display information about the places on it (and in one use-case to not change)...
(If you have a better design idea for this problem, let me know..)
When calling the Google Places API web service I get error 303.
On the Google API web service it doesn't count the call, so I assume it failed, although 303 should be redirect.
I built a class for communicating with the web service (I save the server address in the configuration).
This class is also structured in a way to provide the results immediately (and not in a callback).
Why do I get an error instead of a redirect?
Is there any way to handle it?
Any ideas to what can I do to avoid redirection at all?
Thanks!!
Here is (a template) of my code -- I reduced much of my logic and left the call for the web service (PlaceMarker is just a class I return, you can modify it to String):
class GooglePlacesWS : NSObject, NSURLConnectionDelegate, NSURLConnectionDataDelegate , URLSessionDelegate{
var DataReady : Bool!;
var Data : Foundation.Data!;
var opQueue : OperationQueue!;
var _responseData : NSMutableData!;
var error = false;
func getPlacesNear(_ point : CLLocationCoordinate2D, _ radius: Double)->[PlaceMarker]!
{
var retVal = [PlaceMarker]();
var locationJson = ["location": String(format:"%f, %f",point.latitude,point.longitude), "key": “MyKey”];
if (radius > 0){
locationJson["raidus"] = String(format:"%d",Int(radius));
}
// Fires the request to the server
var reply : String = HtmlToText(FireRequest(locationJson));
if reply == "" { return nil}
return retVal;
}
//MARK: Connection
var session : URLSession? = nil;
var dataTasks : URLSessionTask? = nil;
func sendRequestNew(_ request : URLRequest)
{
DataReady = false;
Data = nil;
let task = session!.dataTask(with: request, completionHandler: {data, response,error in
if (error != nil){
NSLog("Error reading data from web service: " + error!.localizedDescription);
self.Data = nil;
self.error = true;
self.DataReady = true;
}
else{
if (data != nil){
self.Data = data;
OSMemoryBarrier();
self.DataReady = true;
}
}
});
task.resume();
dataTasks = task;
}
// Changes a string to an HTML friendly tags (so the XML will transfer a string)
func textToHtml (_ htmlString : String) -> String
{
var retHtml = htmlString;
retHtml = retHtml.replacingOccurrences(of: "&", with: "&");
retHtml = retHtml.replacingOccurrences(of: "<", with: "<");
retHtml = retHtml.replacingOccurrences(of: ">", with: ">");
retHtml = retHtml.replacingOccurrences(of: "\"", with: """);
retHtml = retHtml.replacingOccurrences(of: "'", with: "'");
//retHtml = retHtml.stringByReplacingOccurrencesOfString("\n", withString: "<br>");
return retHtml;
}
// Changes an HTML string to a regular xml (changes the & tags to actual signs)
func HtmlToText(_ textString : String)->String
{
var retString: String = textString;
retString = retString.replacingOccurrences(of: "&", with:"&");
retString = retString.replacingOccurrences(of: "<", with:"<");
retString = retString.replacingOccurrences(of: ">", with:">");
retString = retString.replacingOccurrences(of: """, with:"\"");
retString = retString.replacingOccurrences(of: "'", with:"'");
retString = retString.replacingOccurrences(of: "<br>", with:"\n");
return retString;
}
// Sends the request to the server
func FireRequest (_ query : [String:String]) ->String
{
var retVal : String = "";
do{
// Builds the final URL request (adds the headers, and the WS addy)
let config :UserDefaults = UserDefaults();
//var myDict: NSDictionary?
if let path : String = config.object(forKey: "googleServerAddy") as? String
{
let url = URL(string: path);
//let theRequest = NSMutableURLRequest(url: url!);
var request = URLRequest(url: url!);
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "GET";
request.httpBody = try JSONSerialization.data(withJSONObject: query, options: []);
sendRequestNew(request);
while (DataReady == false)
{
Thread.sleep(forTimeInterval: 0.01);
}
if (!error)
{
let result : Foundation.Data = Data!;
// Reads the response...
retVal = NSString(data: result, encoding:String.Encoding.utf8.rawValue)! as String;
}
else
{
retVal = "";
}
}
}
catch{
}
return retVal;
}
//MARK: NSURLConnection delegates
func connection(_ connection: NSURLConnection, willSend request: URLRequest, redirectResponse response: URLResponse?) -> URLRequest? {
return request;
}
override init() {
super.init();
opQueue = OperationQueue();
session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: opQueue);
}
}
you could use Alamofire, works fine. used it for google streetview images
import Foundation
import Alamofire
import AlamofireImage
class GoogleData {
static let dataService = GoogleData()
func getGoogleImages(_ latitude: Double, longitude: Double, heading: Double, id: String, key: Int){
let url = "https://maps.googleapis.com/maps/api/streetview?size=400x300&location=" + String(latitude) + "," + String(longitude) + "&heading=" + String(heading) + "&fov=120&&pitch=-0.76&key=" + GOOGLE_API_KEY
Alamofire.request(url).responseImage { (response) -> Void in
print(response)
guard let image = response.result.value else { return }
print("alamo \(url) ")
let returnObj = [image]
ImageUtils.saveGoogleImageToFile(image , key: key, id: id)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: googleGetImagesNotificationKey), object: returnObj)
}
}
}
to call it:
GoogleData.dataService.getGoogleImages(yourlatitude, longitude: your.longitude, heading: someHeading, id: someId, key: someKey )
So the problem I had is sourced here:
let url = URL(string: path);
var request = URLRequest(url: url!);
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "GET";
request.httpBody = try JSONSerialization.data(withJSONObject: query, options: []);
It appears - this solution forces a redirect.
I was not aware of the function addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed) so I wanted to use the httpBody for transferring the GET data, which apparently is wrong...
Changing the code to:
let actPath = path + "?" + query;
let url = URL(string: actPath);
var request = URLRequest(url: url!);
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "GET";
solved the issue for me.
notice query is the query for the get string -- something like
location=1.1, 2.2&radius=5&key=MY_KEY