replacing %3F instead of `?` in url - string

i have this problem when i want call this method in request. It seems to be replacing %3f instead of ? in swift4
its my ApiRouter
struct ApiRouter {
enum Router: URLRequestConvertible {
case getAllPlcae(id: Int, paseSize: Int, pageNumber: Int, countryID: Int, cityID: Int)
var method: Alamofire.HTTPMethod {
switch self {
case .getAllPlcae:
return .get
}
}
func asURLRequest() throws -> URLRequest {
let result: (path: String, parameters: [String: AnyObject]?) = {
switch self {
case .getAllPlcae(let id, let pageSize, let pageNumber, let countryID, let cityID):
return("Location?textToSearch=&tagIds=&id=\(id)&pageSize=\(pageSize)&pageNumber=\(pageNumber)&countryID=\(countryID)&cityID=\(cityID)",nil)
}
}()
// MARK: - Set HTTP Header Field
let url = URL(string: Constants.ApiConstants.baseURLString)!
var urlRequest = URLRequest(url: url.appendingPathComponent(result.path))
urlRequest.httpMethod = method.rawValue
if let token = User.getToken() {
urlRequest.setValue(token, forHTTPHeaderField: "Authorization")
}
let encoding = try Alamofire.URLEncoding.default.encode(urlRequest, with: result.parameters)
return encoding
}
}}
when i call this request , just like down
Alamofire.request(ApiRouter.Router.getAllPlcae(id: 0, paseSize: 10, pageNumber: 1, countryID: 0, cityID: 0)).responseArray { (response: DataResponse<[Place]>) in }
its my url request
Location%3FtextToSearch=&tagIds=&id=0&pageSize=10&pageNumber=1&countryID=0&cityID=0
It seems to be replacing %3f instead of ?
how can fix it ?

I found the solution for this question. We should remove Percent Encoding.
// MARK: - Set HTTP Header Field
let base = URL(string: Constants.ApiConstants.baseURLString)!
let baseAppend = base.appendingPathComponent(result.path).absoluteString.removingPercentEncoding
let url = URL(string: baseAppend!)
var urlRequest = URLRequest(url: url!)
urlRequest.httpMethod = method.rawValue
if let token = User.getToken() {
urlRequest.setValue(token, forHTTPHeaderField: "Authorization")
}
let encoding = try Alamofire.URLEncoding.default.encode(urlRequest, with: result.parameters)
return encoding

Post with url parameters
https://github.com/Moya/Moya/issues/1030#issuecomment-646910369
public var task: Task {
...
return .uploadCompositeMultipart(multipartData, urlParameters: ["key":"value"])
}

Related

Type 'SwiftUIWebView' does not conform to protocol 'UIViewRepresentable'

I am a new commer, trying to create Webview with Xcode 12.0.1. I make the code identical to other comments and videos as below but I don't know why the error message "Type 'SwiftUIWebView' does not conform to protocol 'UIViewRepresentable'. Do you want to add protocol stubs?"
import SwiftUI
import WebKit
struct SwiftUIWebView: UIViewRepresentable {
let url: URL?
func makeUIView(Context: Context) -> WKWebView {
let prefs = WKWebpagePreferences()
prefs.allowsContentJavaScript=true
let config = WKWebViewConfiguration()
config.defaultWebpagePreferences = prefs
return WKWebView(
frame: .zero,
configuration: config
)
}
func updateIUView(_ uiView: WKWebView, context: Context) {
guard let myURL = url else {
return
}
let request = URLRequest(url: myURL)
uiView.load( request )
}
}
Can you point out where is the problem and how to solve this?

How to use fetch data in Intent Handler for editing Widget iOS 14?

I'm currently developing an application using SwiftUI.
I'm trying to make a widget user can edit some data using IntentConfiguration
I want to use some fetch data from CoreData in IntentHandler class for editing the widget like the image below.
I tried to make some codes but They don't work...
How could I solve my codes?
Here are the codes:
IntentHandler.swift
import WidgetKit
import SwiftUI
import CoreData
import Intents
class IntentHandler: INExtension, ConfigurationIntentHandling {
var moc = PersistenceController.shared.managedObjectContext
var timerEntity_0:TimerEntity?
var timerEntity_1:TimerEntity?
var timerEntity_2:TimerEntity?
init(context : NSManagedObjectContext) {
self.moc = context
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
do{
let result = try moc.fetch(request)
timerEntity_0 = result[0]
timerEntity_1 = result[1]
timerEntity_2 = result[2]
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
}
func provideNameOptionsCollection(for intent: ConfigurationIntent, searchTerm: String?, with completion: #escaping (INObjectCollection<NSString>?, Error?) -> Void) {
let nameIdentifiers:[NSString] = [
NSString(string: timerEntity_0?.task ?? "default"),
NSString(string: timerEntity_1?.task ?? "default"),
NSString(string: timerEntity_2?.task ?? "default")
// "meeting",
// "cooking",
// "shoping"
]
let allNameIdentifiers = INObjectCollection(items: nameIdentifiers)
completion(allNameIdentifiers,nil)
}
override func handler(for intent: INIntent) -> Any {
return self
}
}
Widget.swift
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider {
typealias Intent = ConfigurationIntent
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), configuration: ConfigurationIntent(), name: "")
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: #escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), configuration: configuration, name: "")
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, configuration: configuration, name: configuration.Name ?? "")
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
var name:String
}
struct TimerIntentWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
Text(entry.name)
.font(.title)
Text(entry.date, style: .time)
}
}
#main
struct TimerIntentWidget: Widget {
let kind: String = "TimerIntentWidget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
TimerIntentWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
tWidget.intentdefinition
Xcode: Version 12.0.1
iOS: 14.0
Life Cycle: SwiftUI App
I could display a list in the widget using fetch data from CoreData like the code below:
IntentHandler.swift
import WidgetKit
import SwiftUI
import CoreData
import Intents
class IntentHandler: INExtension, ConfigurationIntentHandling {
var moc = PersistenceController.shared.managedObjectContext
var timerEntity_0:TimerEntity?
var timerEntity_1:TimerEntity?
var timerEntity_2:TimerEntity?
func provideNameOptionsCollection(for intent: ConfigurationIntent, searchTerm: String?, with completion: #escaping (INObjectCollection<NSString>?, Error?) -> Void) {
let request = NSFetchRequest<TimerEntity>(entityName: "TimerEntity")
do{
let result = try moc.fetch(request)
timerEntity_0 = result[0]
timerEntity_1 = result[1]
timerEntity_2 = result[2]
}
catch let error as NSError{
print("Could not fetch.\(error.userInfo)")
}
let nameIdentifiers:[NSString] = [
NSString(string: timerEntity_0?.task ?? "default"),
NSString(string: timerEntity_1?.task ?? "default"),
NSString(string: timerEntity_2?.task ?? "default")
// "meeting",
// "cooking",
// "shoping"
]
let allNameIdentifiers = INObjectCollection(items: nameIdentifiers)
completion(allNameIdentifiers,nil)
}
override func handler(for intent: INIntent) -> Any {
return self
}
}

Nesting URLSession.shared.dataTask in Swift 4

I am trying to fetch data from an api where the JSON returned has URLs to other pieces of information that I need, such as
"value1" : "data",
"value2": {
"url": "https://example.com/stuff",
}
My logic is as follows:
func(completion: #escaping ([Data]) -> ()) {
var classArray = [myClass]()
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
do {
guard let resultArray = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return }
let myObject = myClass(value1: resultArray["value1"]! as! String)
guard let valueUrl = URL(string: resultArray["value2"]! as! String) else { return }
URLSession.shared.dataTask(with: valueUrl) { (data, _, _) in
myObject.value2 = data
classArray.append(myObject)
}.resume()
} catch let error {
print("Failed to create json with error: ", error.localizedDescription)
}
completion(classArray)
}.resume()
}
}
Is this a valid approach or are there better implementations? Trying to avoid a future Pyramid of Doom situation. I have tried putting the inner URLSession call in a separate private function but still receive an empty classArray in the end.

Convert Data to String in Swift 3

I am very new to Swift.
I want to create something like API on Swift for my educational app.
I have this code:
static func getFilm(filmID: Int) -> String {
print("getFilm")
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
var request = URLRequest(url: url)
var returnData: String = ""
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if var responseVar = response, var dataVar = data {
print(responseVar)
returnData = String(data: dataVar, encoding: .utf8)
} else {
print(error)
}
}
task.resume()
return returnData
}
And I try to convert Data to String in this line: returnData = String(data: dataVar, encoding: .utf8)
Swift compiler gives me an error, and change this line to
returnData = String(data: dataVar, encoding: .utf8)!
, when I execute this line I get empty returnData variable.
If I use basic example line
print(String(data: data, encoding: .utf8))
everything will be OK and I can see data in XCode console.
So, how I can convert Data to String?
This is an example using a completion handler:
class func getFilm(filmID: Int, completion: #escaping (String) -> ()) {
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
URLSession.shared.dataTask(with:url) { (data, response, error) in
if error != nil {
print(error!)
completion("")
} else {
if let returnData = String(data: data!, encoding: .utf8) {
completion(returnData)
} else {
completion("")
}
}
}.resume()
}
And you call it
MyClass.getFilm(filmID:12345) { result in
print(result)
}
In case of an error the completion handler returns an empty string.
MyClass is the enclosing class of getFilm method. Most likely the web service will return JSON, so you might need to deserialize the JSON to an array or dictionary.
In a more sophisticated version create an enum with two cases and associated values
enum ConnectionResult {
case success(String), failure(Error)
}
With a little more effort demonstrating the subtle power of Swift you can return either the converted string on success of the error on failure in a single object.
class func getFilm(filmID: Int, completion: #escaping (ConnectionResult) -> ()) {
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
URLSession.shared.dataTask(with:url) { (data, response, error) in
if error != nil {
completion(.failure(error!))
} else {
if let returnData = String(data: data!, encoding: .utf8) {
completion(.success(returnData))
} else {
completion(.failure(NSError(domain: "myDomain", code: 9999, userInfo: [NSLocalizedDescriptionKey : "The data is not converible to 'String'"])))
}
}
}.resume()
}
On the caller side a switch statement separates the cases.
MyClass.getFilm(filmID:12345) { result in
switch result {
case .success(let string) : print(string)
case .failure(let error) : print(error)
}
}
I had this problem, you can't use encoding: .utf8 for unpredictable data. It will return nil every time.
Use this instead:
String(decoding: data, as: UTF8.self)
For anyone coming in future (which are probably not interested in OP's film code?!);
Simply, try something like:
extension Data {
public func toString() -> String {
return String(data: self, encoding: .utf8) ?? "";
}
}
See also my toHex related answer

Why does localizedDescription of NSError say Optional("description")?

Whenever I do println(error.localizedDescription) I get something that says :
Optional("description of the error here")
Rather than just:
"description of the error here"
How do I get rid of the Optional() part of the description?
I tried the below method which results in a compiler error saying that it's not an optional.
func performLoginRequestWithURL(url: NSURL, email: String, password: String) {
let bodyData = "email=\(email)&password=\(password)"
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
response, data, error in
if let error = error {
let errString = error.localizedDescription
NSNotificationCenter.defaultCenter().postNotificationName(self.lh, object: nil, userInfo: ["Result": errString])
} else if data != nil {
let json = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
if let dictionary = JSON().parseJSON(json) as [String: AnyObject]? {
let accesstoken = dictionary["id"] as! String
let id = dictionary["userId"] as! Int
var results = [String: AnyObject]()
results = ["at": accesstoken, "id": id]
// MARK: - Store UID & AccessToken
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "userLoggedIn")
NSUserDefaults.standardUserDefaults().setInteger(id, forKey: "userId")
NSUserDefaults.standardUserDefaults().setObject(accesstoken, forKey: "accessToken")
NSUserDefaults.standardUserDefaults().synchronize()
NSNotificationCenter.defaultCenter().postNotificationName(self.lh, object: nil, userInfo: ["Result": "Success"])
}
}
}
}
The compiler is correct - error is an optional but error.localizedDescription is not. You either need to unwrap the error first, safely by
if let unwrappedError = error {
println(unwrappedError.localizedDescription)
}
or: Use optional chaining - https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html
let description = error?.localizedDescription
which will either cause 'description' to be the string of localizedDescription, if the error optional can be unwrapped successfully, or possibly 'nil' if not.
Code taken from example added to question. Seems to work as ok, below code can be copied into playground to show error and data string printout - just add valid http:// address into see valid return or try with just 'http:' to get an error.
import UIKit
import XCPlayground
func performLoginRequestWithURL(url: NSURL) {
let request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
response, data, error in
if let error = error {
let errString = error.localizedDescription
print(errString)
} else if data != nil {
let json = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
print(json)
}
}
}
performLoginRequestWithURL(NSURL(string:"http:")!)
XCPSetExecutionShouldContinueIndefinitely()
You have to unwrap error using ?:
if let errString = error?.localizedDescription {
println(errString)
}
If you already know from an earlier check that error isn't nil, you can force unwrap:
println(error!.localizedDescription)
This will crash if error is nil, so use the ! syntax with caution.

Resources