iOS-How to set themultiple cookies in WKWebview - objective-c

How to set the multiple cookies in WKWebView.I have tried the following approach.
In the below code i am able to save only one cookie i.e "Abp.TenantId" other one is not saved.Can anyone help me out for this.I have tried also with the NSMutableURLRequest but it does not work for me also.
if let url = URL(string: "URL") {
var request = URLRequest(url:url)
request.httpShouldHandleCookies = true
let days: TimeInterval = 30 * 24 * 60 * 60
WKWebsiteDataStore.default().httpCookieStore.add(self)
guard let cookies = HTTPCookie(properties: [
.domain: "domainname/",
.path: "/",
.name: "Abp.AuthToken",
.value: "Abp.AuthToken=token",
.secure: "TRUE",
.expires: NSDate(timeIntervalSinceNow: days),
])else{
return
}
HTTPCookieStorage.shared.setCookie(cookies)
guard let cook = HTTPCookie(properties: [
.domain: "domainname",
.path: "/",
.name: "Abp.TenantId",
.value: "Id",
.secure: "TRUE",
.expires: NSDate(timeIntervalSinceNow: days),
])else{
return
}
HTTPCookieStorage.shared.setCookie(cook)
let arrCookies = HTTPCookieStorage.shared.cookies ?? []
for strcookie in arrCookies {
webView.configuration.websiteDataStore.httpCookieStore.setCookie(strcookie, completionHandler: {
print("cookie setup done")
})
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0)
{
self.webView.load(request)
}}

Related

POST method API with parameters in swiftUI

I am retrieving details from my api with POST method, I have included the parameters here. I am able to print the response in console., but to the view it's quite complicating. Here added the code which i've tried. I appreciate if someone help me to get this done.
My network code goes here:
class HostApi: ObservableObject {
#Published var todos = [HostsHome]()
#Published var amenity = [AmenitiesHome]()
func loadData() {
let Url = String(format: Host_home)
guard let serviceUrl = URL(string: Url) else {
return
}
let parameters: [String : Any] = [
"request" : ["email" : "xxxxxxxxxxx.com",
"starting" : 0,
"ending" : 10]
]
var request = URLRequest(url: serviceUrl)
request.httpMethod = "POST"
request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) else {
return
}
request.httpBody = httpBody
request.timeoutInterval = 20
let session = URLSession.shared
session.dataTask(with: request) { data, response, error in
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .json5Allowed)
print(json)
} catch {
print(error)
}
}
}.resume()
}
}
ContentView code goes here :
struct Hosts_Home: View {
#StateObject var viewModel = HostApi()
var body: some View {
ForEach(viewModel.todos, id: \.title) { todo in
Text(todo.title!)
}
.onAppear {
viewModel.loadData()
}
}
}
[![api[![parameters][1]][1]][2]
[1]: https://i.stack.imgur.com/R5sBV.png
[2]: https://i.stack.imgur.com/yMj0t.png
Found a solution, I changed my network class like this below and it worked.
class HostApi: ObservableObject {
#Published var todos = [HostsHome]()
#Published var amenity = [AmenitiesHome]()
func loadData() {
let url = URL(string: Host_home)
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
let postString = "email=xxxxxxx#gmail.com&starting=0&ending=10";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
if let todoData = data {
let decodedData = try JSONDecoder().decode([HostsHome].self, from: todoData)
DispatchQueue.main.async {
self.todos = decodedData
self.amenity = decodedData[0].amenities
print(decodedData[0].propertyTypeGroup)
}
} else {
print("No data")
}
} catch {
print(error)
}
}
task.resume()
}
}

async image in SwiftUI

I am working on an api, in which i retrieve the texts but the image from the api is not showing inside the view. I have given it an async image. The async image shows as a grey part in the view. Please let me know what is missing here. It would be great if someone would help me out with this.
API modal as:
struct priceRange: Codable {
let status: String
let record: Record
}
struct Record: Codable {
let propertytype: [Property]
let placetype: [Place]
let floorplan: [Floor]
let amenity: [Amenity]
let path: String
}
struct Property: Codable {
let type: String
let image: String
let status: String
let id: Int
}
My network code goes here:
class PRViewModel: ObservableObject {
#Published var floors = [Floor]()
#Published var place = [Place]()
#Published var prop = [Property]()
#Published var res = [Amenity]()
#Published private(set) var exp: priceRange?
#Published private(set) var rec: Record?
func loadData(){
guard let url = URL(string: PR_data) else {
print("Invalid URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request) {(data, response, error) in
do {
if let todoData = data {
let decodedData = try JSONDecoder().decode(priceRange.self, from: todoData)
DispatchQueue.main.async {
self.res = decodedData.record.amenity
self.prop = decodedData.record.propertytype
self.floors = decodedData.record.floorplan
self.place = decodedData.record.placetype
print(decodedData.status)
//print(decodedData.record.path!)
}
} else {
print("No data")
}
} catch {
print(error)
}
}.resume()
}
}
List code goes here :
struct Price_range: View {
#StateObject var viewModel = PRViewModel()
var body: some View {
List(viewModel.prop, id: \.type) { item in
Text(item.type)
AsyncImage(url: URL(string: PR_URL + (viewModel.rec?.path ?? "") + "/" + item.image))
}
.onAppear {
viewModel.loadData()
}
}
}
Edit:
AsyncImage(url: URL(string: PR_URL + (viewModel.exp?.record.path ?? "") + "/" + item.image))
it still remains the same. I want to bring that “path” variable in the “record” modal to the view?
As allready pointed out in the comments you never assign any value to exp and res so they stay nil. You could assign them while you assign your previous properties:
do {
if let todoData = data {
let decodedData = try JSONDecoder().decode(priceRange.self, from: todoData)
DispatchQueue.main.async {
self.exp = decodedData // this
self.rec = decodedData.record // and this
self.res = decodedData.record.amenity
self.prop = decodedData.record.propertytype
self.floors = decodedData.record.floorplan
self.place = decodedData.record.placetype
print(decodedData.status)
//print(decodedData.record.path!)
}
} else {
print("No data")
}
} catch {
print(error)
}
and then do:
AsyncImage(url: URL(string: PR_URL + (viewModel.rec?.path ?? "") + "/" + item.image))
if there is still an issue try to verify your link is valid by using:
let _ = print(PR_URL + (viewModel.rec?.path ?? "") + "/" + item.image)
right before the line with the AsyncImage.

Limiting Loop Range to 100 Rows at at time

I have the following script that takes data from my Sheet and updates records via a POST API call; however there is a limit of 100 calls at a time so I'm looking for a way to add that to my script if possible. I also need to ensure that the header row (row1) is sent. So essentially the first loop is rows 1-101, second loop is row 1 and rows 102-201 etc. Not even sure this is possible
function updateManyUsers() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var [headers, ...rows] = sheet.getDataRange().getDisplayValues();
Logger.log([headers,rows]);
var users = rows.map(r => {
var temp = {};
headers.forEach((h, j) => {
if (r[j] != "") temp[h] = r[j];
});
return temp;
});
var url = 'https://redaccted.zendesk.com/api/v2/users/update_many.json';
var user = 'morris.coyle#redacted_still/token';
var pwd = 'Every_redacted';
var options = {
'method': 'PUT',
'headers': {
'Authorization': "Basic " + Utilities.base64Encode(user + ':' + pwd)
},
'payload': JSON.stringify({ users }),
'contentType': 'application/json',
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response.getContentText());
}
Thanks in advance.
Moz
Description
I have created a simple example of how to slice 100 rows from the data.
I have a simple data set of Header plut 256 rows of data. See screen shot.
Screen shots
Script
function updateManyUsers() {
try {
let spread = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spread.getSheetByName("Sheet1");
let values = sheet.getDataRange().getValues();
console.log("rows = "+values.length);
let headers = values.shift();
let i = 0;
let numUsers = 100;
let j = numUsers;
while( i < values.length ) {
if( j < values.length ) {
var users = [].concat(headers,values.slice(i,j));
}
else {
var users = [].concat(headers,values.slice(i));
}
console.log("header = "+users[0][0]);
console.log("users[1] = "+users[1][0]);
console.log("users[99] = "+users[users.length-1][0]);
i = i+numUsers;
j = j+numUsers;
// Now build your opsions
}
}
catch(err) {
console.log(err);
}
}
1:18:04 PM Notice Execution started
1:18:05 PM Info rows = 257
1:18:05 PM Info header = Header
1:18:05 PM Info users[1] = 1
1:18:05 PM Info users[99] = 100
1:18:05 PM Info header = Header
1:18:05 PM Info users[1] = 101
1:18:05 PM Info users[99] = 200
1:18:05 PM Info header = Header
1:18:05 PM Info users[1] = 201
1:18:05 PM Info users[99] = 256
1:18:05 PM Notice Execution completed
Reference
Array.shift()
Array.concat()
Array.slice()
You can try this for-loop method that iterates every 100:
function updateManyUsers() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var data = sheet.getDataRange().getDisplayValues();
var headers = data.shift(); //remove headers to the array and assign it to headers variable
for (var i = 0; i < data.length; i += 100){
var tempArr = data.slice(i, i+100)
var users = tempArr.map(r => {
var temp = {};
headers.forEach((h, j) => {
if (r[j] != "") temp[h] = r[j];
});
return temp;
});
var url = 'https://redaccted.zendesk.com/api/v2/users/update_many.json';
var user = 'morris.coyle#redacted_still/token';
var pwd = 'Every_redacted';
var options = {
'method': 'PUT',
'headers': {
'Authorization': "Basic " + Utilities.base64Encode(user + ':' + pwd)
},
'payload': JSON.stringify({"users": users }),
'contentType': 'application/json',
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
}
}
Sample data:
......
variable users content on each iteration:
Also, upon checking the documentation, when using Update Many Users batch update the data format should look like this:
{
"users": [
{ "id": 10071, "name": "New Name", "organization_id": 1 },
{ "id": 12307, "verified": true }
]
}
I'd propose to make three functions: main(), get_all_users(), update_users() and run the latter function in the loop this way:
function main() {
var all_users = get_all_users();
for (var i = 0; i < all_users.length; i += 100) {
var users = all_users.slice(i, i + 100);
update_users(users);
}
}
function get_all_users() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var [headers, ...rows] = sheet.getDataRange().getDisplayValues();
Logger.log([headers, rows]);
var users = rows.map((r) => {
var temp = {};
headers.forEach((h, j) => { if (r[j] != "") temp[h] = r[j] });
return temp;
});
return users;
}
function update_users(users) {
var url = "https://redaccted.zendesk.com/api/v2/users/update_many.json";
var user = "morris.coyle#redacted_still/token";
var pwd = "Every_redacted";
var options = {
method: "PUT",
headers: {
Authorization: "Basic " + Utilities.base64Encode(user + ":" + pwd),
},
payload: JSON.stringify({ users }),
contentType: "application/json",
muteHttpExceptions: true,
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response.getContentText());
}
It's always a good idea to keep the main function as short and clear as it's possible. And break the algo into the separate functions where every function does exactly one relatively simply thing: get users from the sheet, send request, etc.

How convert URL building code from Objective-C to Swift? [duplicate]

This question already has answers here:
Swift - encode URL
(19 answers)
Closed 3 years ago.
I have Objective-C code to build a URL. How to write in Swift 3 code?
NSString *urlString = [[NSString alloc]initWithFormat:#"http://smartbaba.in/Familynk/api/registration.php?phone_no=%#&email=%#&password=%#&first_name=%#&last_name=%#",txtmobileno.text,txtemail.text,txtpassword.text,txtfirstname.text,txtlastname.text];
Try This
"http://smartbaba.in/Familynk/api/registration.php?phone_no=\(txtmobileno.text)&email=\(txtemail.text)&password=\(txtpassword.text)&first_name=\(txtfirstname.text)&last_name=\(txtlastname.text)"
Swift conversion can be something like this
// I thing code requires no explanation, its self explanatory
func makeUrl(phoneNo: String, email: String, password: String, firstName: String, lastName: String) {
// first way but not recomended
let urlString = "http://smartbaba.in/Familynk/api/registration.php?phone_no=\(phoneNo)&email=\(email)&password=\(password)&first_name=\(firstName)&last_name=\(lastName)"
print("\(urlString)")
// second way, ASAIK this is good way for constructing URL's
var components = URLComponents()
components.scheme = "http"
components.host = "smartbaba.in"
components.path = "/Familynk/api/registration.php"
components.queryItems = [
URLQueryItem(name: "phone_no", value: phoneNo),
URLQueryItem(name: "email", value: email),
URLQueryItem(name: "password", value: password),
URLQueryItem(name: "first_name", value: firstName),
URLQueryItem(name: "last_name", value: lastName)
]
let url = components.url
print(url) // returns URL
print(url?.absoluteString) // returns url path in string
}
// call function
makeUrl(phoneNo: "12345", email: "test#gmail.com", password: "12345678", firstName: "test", lastName: "user")
You can simply use String interpolation to create a String with multiple parameters like,
let urlString = "http://smartbaba.in/Familynk/api/registration.php?phone_no=\(txtmobileno.text)&email=\(txtemail.text)&password=\(txtpassword.text)&first_name=\(txtfirstname.text)&last_name=\(txtlastname.text)"
Now, to get the URL using urlString,
if let url = URL(string: urlString) {
//use url here...
}
post method using URLSession:
let myUrl = URL(string: "http://smartbaba.in/Familynk/api/registration.php");
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"
let postString = "firstName=James&lastName=Bond";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(error)")
return
}
// You can print out response object
print("response = \(response)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
print(parseJSON)
}
} catch {
print(error)
}
}
task.resume()
If you are using Alamofire then,
let parameters: Parameters = [
"Subject": "hallo"
]
let url = "http://mydomain/mydb/mydb_tesT.NSF/api/data/documents/unid/DD026770D91AA23DC1257EF90035E1C4"
Alamofire.request(url, method:.post, parameters:parameters, headers:headers).responseJSON { response in
switch response.result {
case .success:
debugPrint(response)
case .failure(let error):
print(error)
}
}

Bad Request: there is no photo in the request

I'm trying to send an image from my application to telegram bot like here
https://newfivefour.com/swift-form-data-multipart-upload-URLRequest.html
Here is the code
let BotToken = "12345"
let ChatID = "123"
func SendToTelegram()
{
var request = URLRequest(url: URL(string: "https://api.telegram.org/bot"+BotToken+"/sendPhoto")!)
request.httpMethod = "POST"
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let params = [:] as [String: String]
UIGraphicsBeginImageContextWithOptions(ScreenImage.bounds.size, true, 0.0)
ScreenImage.image?.draw(in: CGRect(x: 0, y: 0, width: ScreenImage.frame.size.width, height: ScreenImage.frame.size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
request.httpBody = createBody(parameters: params,
boundary: boundary,
data: UIImageJPEGRepresentation(image!, 0.7)!,
mimeType: "image/jpg",
filename: "hello.jpg")
print(request.httpBody!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
return
}
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? AnyObject
if let parseJSON = json {
print("resp :\(parseJSON)")
}
} catch let error as NSError {
print("error : \(error)")
}
}
task.resume()
}
and get an error
Bad Request: there is no photo in the request";
"error_code" = 400;
ok = 0;
Where do i make a mistake? I'm new in SWIFT and sorry for my English
I know this question is old but the link provided pushed me in the right direction. This is my solution for server side Swift not iOS but you should be able to use it with minimal changes. Remember if you're using iOS, perform none of these operations on the main thread.
class NetworkManager {
func sendTelegramPhoto(_ photo: Data) {
let url = "https://api.telegram.org/bot\(Constants.Telegram.token)/sendPhoto"
let params: [String: Any] = [
"chat_id": Constants.Telegram.uid,
"photo": photo
]
let _ = sendMultiTypePostRequest(url, parameters: params)
}
private func sendMultiTypePostRequest(_ url: String, parameters: [String:String]) -> NetworkResponse {
var networkResponse = NetworkResponse()
let semaphore = DispatchSemaphore(value: 0)
guard let url = URL(string: url) else {
return networkResponse
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let httpBody = createBody(parameters: parameters,
boundary: boundary,
mimeType: "image/jpeg",
filename: "snapshot.jpg")
let config = URLSessionConfiguration.default
config.requestCachePolicy = .reloadIgnoringLocalCacheData
let session = URLSession(configuration: config)
let task = session.uploadTask(with: urlRequest, from: httpBody) { (data, response, error) in
networkResponse.data = data
networkResponse.response = response
networkResponse.error = error
semaphore.signal()
}
task.resume()
_ = semaphore.wait(timeout: .distantFuture)
return networkResponse
}
private func createBody(parameters: [String: String],
boundary: String,
mimeType: String,
filename: String) -> Data {
var body = Data()
let boundaryPrefix = "--\(boundary)\r\n"
for (key, value) in parameters {
if let data = value as? Data {
body.appendString(boundaryPrefix)
body.appendString("Content-Disposition: form-data; name=\"\(key)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimeType)\r\n\r\n")
body.append(data)
body.appendString("\r\n")
} else if let string = value as? String {
body.appendString(boundaryPrefix)
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(string)\r\n")
}
}
body.appendString("--".appending(boundary.appending("--")))
return body
}
}
private extension Data {
mutating func appendString(_ string: String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
append(data!)
}
}