I'm looking to display a countdown timer Label is Swift 4.0 using the time from an online database. I do not want to use the phone's time.
I originally wanted to retrieve the time from Firebase but had no luck but then I found timezonedb and made a little more progress.
I have my API key and want to know how to implement the query.
I have set my url variable to the query with my API key and used components to extract the info.
let url = URL(string: "http://api.timezonedb.com/v2.1/get-time-zone?key=YOUR_API_KEY&format=json&by=zone&zone=America/Chicago") // I inserted my API key before but left it blank here
let components = URLComponents(url: url!, resolvingAgainstBaseURL: false)
if let components = components {
components.host
components.query
components.percentEncodedQuery
if let queryItems = components.queryItems {
for queryItem in queryItems {
print("\(queryItem.name): \(queryItem.value)")
}
}
}
Looking at getting a time in hh : mm : ss but instead get this:
key: Optional("// My key")
format: Optional("json")
by: Optional("zone")
zone: Optional("America/Chicago")
Related
How to get bit rate information from the TVMLKit JS player object?
In apple documentation(https://developer.apple.com/documentation/tvmljs/player), I am not able to identify any attribute/method that returns this particular information. Is this possible to get this information?
Doesn't seem like there is any way to read the player item's access log from JS. At least none that I have been able to find either.
However, if you have access to the native code, as a workaround, you can listen for the AVPlayerItemNewAccessLogEntry notification.
Not the perfect solution, but perhaps it's enough for your use-case.
// Add observer somewhere.
NotificationCenter.default.addObserver(self, selector: #selector(accessLog(notification:)), name: .AVPlayerItemNewAccessLogEntry, object: nil)
#objc func accessLog(notification: Notification) {
guard let playerItem = notification.object as? AVPlayerItem,
let accessLogEvent = playerItem.accessLog()?.events.last
else { return }
_ = accessLogEvent.observedBitrate
}
I am trying to do webhook fulfillment for my dialogflow agent. However there are four specific intents that should all have different JSON responses based on what specific intent is called. Right now I am creating a switch case based on the called intent's displayName. However that is not working. Should I be using a different parameter to check what intent is called other than displayName?
HERE IS MY CODE THAT ONLY OUTPUTS "test"
server.post("/get-bill-details", function(req, res) {
let intentName = req.body.queryResult.intent.displayName;
let ret = "test";
if(intentName == "1 - Bill"){
ret = "your billing amount is $120.";
}
return res.json({
fulfillmentText: ret,
source: "get-bill-details"
});
});
I would suggest you use client libraries as they will ease out the process of parsing the JSON and reduce your development time. You can use NodeJS or Python clients for Dialogflow. Also, if you need Google Assistant, you can also use following NodeJS library to build webhook. They all have documentation on how to build webhooks on cloud or by using Express and other frameworks.
Instead of matching with intent name give your intent an action name( try not to give any spaces e.g input.welcome ).
Then get the action parameter using
let action = req.body.queryResult.action;
switch(action) {
your logic..
}
Also as abhinav said you can use this library to ease your development time and better readability of your code that also help cross platform response for Cards, Image and Suggestions.
const { WebhookClient } = require('dialogflow-fulfillment');
server.post('/', function (request, response, next) {
const agent = new WebhookClient({ request, response });
const welcome = () => {
agent.add('Hello Welcome To My bot');
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
agent.handleRequest(intentMap);
}
obviously I am new to RxSwift and though I consumed a lot of documentations and speeches, I think I am missing some fundamental concepts.
In my app I have a RESTful web service to load various resources but the base url of the web service is unknown at build/start time. Instead I have a "URL resolver" web service which I can call with my apps bundle, version and possible environment ("production", "debug" or any custom string entered in the apps debug settings) to obtain the base url I then use for the actual service.
My thinking was that I would create 2 services, one for the URL resolver and one for the actual web service which gives me my resources. The URL resolver would have a Variable and a Observable. I use the variable to signal the need to refresh the base url via a web service call to the URL resolver. I do this by observing the variable and filter only for true values. A function in the service class set the variables value to true (initially it is false) and inside an observer of the filtered variable, I make the web service call in another Observable (this example uses a dummy JSON web service):
import Foundation
import RxSwift
import Alamofire
struct BaseURL: Codable {
let title: String
}
struct URLService {
private static var counter = 0
private static let urlVariable: Variable<Bool> = Variable(false)
static let urlObservable: Observable<BaseURL> = urlVariable.asObservable()
.filter { counter += 1; return $0 }
.flatMap { _ in
return Observable.create { observer in
let url = counter < 5 ? "https://jsonplaceholder.typicode.com/posts" : ""
let requestReference = Alamofire.request(url).responseJSON { response in
do {
let items = try JSONDecoder().decode([BaseURL].self, from: response.data!)
observer.onNext(items[0])
} catch {
observer.onError(error)
}
}
return Disposables.create() {
requestReference.cancel()
}
}
}
static func getBaseUrl() {
urlVariable.value = true;
}
static func reset() {
counter = 0;
}
}
Now the problem is that sometimes it can happen that a web service call fails and I would need to show the error to the user so a retry can be made. I thought that the onError was useful for this but it seems to kills all the subscribers forever.
I could put the subscribing in its own function and inside the error handler of the Observer, I could show a alert and then call the subscribe function again like so:
func subscribe() {
URLService.urlObservable.subscribe(onNext: { (baseURL) in
let alert = UIAlertController(title: "Success in Web Service", message: "Base URL is \(baseURL.title)", preferredStyle: .alert)
let actionYes = UIAlertAction(title: "Try again!", style: .default, handler: { action in
URLService.getBaseUrl()
})
alert.addAction(actionYes)
DispatchQueue.main.async {
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alert, animated: true, completion: nil)
}
}, onError: { error in
let alert = UIAlertController(title: "Error in Web Service", message: "Something went wrong: \(error.localizedDescription)", preferredStyle: .alert)
let actionYes = UIAlertAction(title: "Yes", style: .default, handler: { action in
URLService.reset()
self.subscribe()
})
alert.addAction(actionYes)
DispatchQueue.main.async {
VesselService.reset()
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alert, animated: true, completion: nil)
}
}).disposed(by: disposeBag)
}
Then in my AppDelegate I would call
subscribe()
URLService.getBaseUrl()
The problem is that all other observers get killed on an error as well but since the the only other observer on the URLService.urlObservable is my other web service class, I guess I could implement the same style subscribe function in there as well.
I read that some people suggest to return a Result enum which has 2 cases: the actual result (.success(result: T)) or an error (.error(error: Error)).
So what is the better way of handling errors web service errors in Rx? I cant wrap my head around this problem and I'm trying for 2 days to understand it. Any ideas or suggestions?
Update
It just came to my mind that I could ignore errors from the web service calls completely and instead post any error to a global "error" variable which my app delegate could observe to show alerts. The "error" could reference the function which initially caused it so a retry could be made. I'm still confused and not sure what I should do. :/
Update 2
I think I might found a working solution. As I am still a beginner to Rx and RxSwift, I'm happy to take improvement suggestions. As I was writing the actual code, I splitted my call chain in two parts:
The part where I make the web service calls
The part where I click a button and process the result of the web service, whether it is an error or a success
In the part where I click the button and process the result, I use catchError and retry as suggested in the comments. The code looks like this:
let userObservable = URLService
.getBaseUrl(environment: UserDefaults.standard.environment) //Get base url from web service 1
.flatMap({ [unowned self] baseURL -> Observable<User> in
UserService.getUser(baseURL: baseURL,
email: self.usernameTextField.text!,
password: self.passwordTextField.text!) //Get user from web service 2 using the base url from webservice 1
})
signInButton
.rx
.tap
.throttle(0.5, scheduler: MainScheduler.instance)
.flatMap({ [unowned self] () -> Observable<()> in
Observable.create { observable in
let hud = MBProgressHUD.present(withTitle: "Signing in...");
self.hud = hud
observable.onNext(())
return Disposables.create {
hud?.dismiss()
}
}
})
.flatMap({ () -> Observable<User> in
return userObservable
})
.catchError({ [unowned self] error -> Observable<User> in
self.hud?.dismiss()
self.handleError(error)
return userObservable
})
.retry()
.subscribe(onNext: { [unowned self] (user) in
UserDefaults.standard.accessToken = user.accessToken
UserDefaults.standard.tokenType = user.tokenType
self.hud?.dismiss()
})
.disposed(by: disposeBag)
The trick was to move the call to the two web services out of the cain into their own variable so I can re-call it at any time. When I now return the "userObservable" and an error happens during the web service call, I can show the error in the catchError and return the same "userObservable" for the next retry.
At the moment this only properly handles errors when they occur in the web service call chain so I think I should make the button tap a driver.
Okay so for everyone who comes here, you probably have a lack of understanding or a misconception of how the Rx world is supposed to work. I still find it sometimes confusing but I found a way better solution than what I posted in my original question.
In Rx, a error "kills" or rather completes all observers in the chain and that is actually a good thing. If there are expected errors like API error in web service calls, you should either try to handle them where they occur or treat them like expected values.
For example, your observer could return a optional type and subscribers could filter for the existence of values. If an error in the API call occurs, return nil. Other "error handlers" could filter for nil values to display error messages to the user.
Also viable is to return a Result enum with two cases: .success(value: T) and .error(error: Error). You treat the error as a acceptable result and the observer is responsible for checking if it should display a error message or the success result value.
Yet another option, which surely is not the best as well but works it to simply nest the call which you expect to fail inside the subscriber of the call which must not be affected. In my case that is a button tap which causes a call to a web service.
The "Update 2" of my original post would become:
signInButton.rx.tap.throttle(0.5, scheduler: MainScheduler.instance)
.subscribe(onNext: { [unowned self] () in
log.debug("Trying to sign user in. Presenting HUD")
self.hud = MBProgressHUD.present(withTitle: "Signing in...");
self.viewModel.signIn()
.subscribe(onNext: { [unowned self] user in
log.debug("User signed in successfully. Dismissing HUD")
self.hud?.dismiss()
}, onError: { [unowned self] error in
log.error("Failed to sign user in. Dismissing HUD and presenting error: \(error)")
self.hud?.dismiss()
self.handleError(error)
}).disposed(by: self.disposeBag)
}).disposed(by: self.disposeBag)
The MVVM view model makes the calls to the web serivces like so:
func signIn() -> Observable<User> {
log.debug("HUD presented. Loading BaseURL to sign in User")
return URLService.getBaseUrl(environment: UserDefaults.standard.environment)
.flatMap { [unowned self] baseURL -> Observable<BaseURL> in
log.debug("BaseURL loaded. Checking if special env is used.")
if let specialEnv = baseURL.users[self.username.value] {
log.debug("Special env is used. Reloading BaseURL")
UserDefaults.standard.environment = specialEnv
return URLService.getBaseUrl(environment: specialEnv)
} else {
log.debug("Current env is used. Returning BaseURL")
return Observable.just(baseURL)
}
}
.flatMap { [unowned self] baseURL -> Observable<User> in
log.debug("BaseURL to use is: \(baseURL.url). Now signing in User.")
let getUser = UserService.getUser(baseURL: baseURL.url, email: self.username.value, password: self.password.value).share()
getUser.subscribe(onError: { error in
UserDefaults.standard.environment = nil
}).disposed(by: self.disposeBag)
return getUser
}
.map{ user in
UserDefaults.standard.accessToken = user.accessToken
UserDefaults.standard.tokenType = user.tokenType
return user
}
}
First I was thinking to only call the view models signIn() function when pressing the button but since there should be no UI code in the view model, I figured that presenting and dismissing the HUD is the responsibility of the ViewController.
I think this design is now pretty solid. The button observer never completes and can continue to send events forever. Earlier, if there was a second error, it might happen that the button observer died and my logs showed that the userObservable was executed twice, which must also not be happen.
I just wonder if there is a better way then nesting the subscribers.
I'm starting to write Google scripts to automatize certain tasks, and here I'm stuck on a problem I can't figure out by myself. I must say I'm neither an expert in app scripts (yet) nor in javascript.
Here is my problem. I make a call to a (private) REST API to retrieve some data. I get the result, parse it to get a Json object. Then I want to write some properties in a spreadsheet. For some reason, I can't get to manipulate nested objects.
Say I have a list of this json payload :
{
id: 2146904633,
status: "in_progress",
success_probability: 99,
amount: "0.0",
decision_maker: "Bob Mauranne",
business_contact: {
id: 2142664162,
nickname: "NIL",
}
}
EDIT : I made a mistake with the code I pasted (businessContact was not declared, instead a variable bc was declared).Thanks for the comment :) The code below is correct now, but still doesn't work.
I get it like with this (overly simplified) code :
var response = UrlFetchApp.fetch(url);
var dataAll = JSON.parse(response.getContentText());
var data, businessContact;
for (i = 0; i < dataAll.length; i++) {
data = dataAll[i];
businessContact = data.business_contact;
Logger.log(data.status);
Logger.log(businessContact);
Logger.log(businessContact.id);
}
My problem is that when I call businessContact.id I get the error "TypeError: unable to read property id from null object". And I don't understand since I can see the content from businessContact : either from the log call or from the debugger, it's definately not null.
It seems to happen only on nested objects, because on simple properties, I don't have any error. And I have the same problems on all nested objects, whatever json payload I've tried so far...
I searched on the internet for a solution but found none. It probably is very basic, but I can't get it to work.
Any idea ?
You never define "businessContact" that your using in the logger. You define "bc" but not "businessContact". If you changed it to Logger.log(bc.id) it should work.
Here is a trimmed down version of what your trying to do also.
function getJSON() {
var url = "your url";
var response = UrlFetchApp.fetch(url).getContentText();
var data = JSON.parse(response)
data.forEach(function(item) {
Logger.log(item.business_contact.id)
})
}
Heres an example pulling weather data.
function myFunction() {
var url = "https://www.aviationweather.gov/gis/scripts/TafJSON.php";
var response = UrlFetchApp.fetch(url).getContentText();
var data = JSON.parse(response)
data.features.forEach(function(feature) {
Logger.log(feature.properties.id)
})
}
I finally found the solution. This code is in a loop, sometimes the object business_contact is null and I hadn't seen it :|
Clearly I should stop working late in the evening when I learn a new technology ...
My bad, sorry for the noise, and thanks for the answers and comments guys.
I want to implement UISearchContoller that search from webservice JSON with swifty json, exactly like apple's appStore when you search for an app, it loads without load them into tableView
here is what I have done in updateSearchResultsForSearchController method:
func updateSearchResultsForSearchController(searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
func filterContentForSearchText(searchText: String) {
filteredContents = myStruct.filter{$0.name.rangeOfString(searchText) != nil
}
Posting more of your code would be nice, like what you are using to get the results from the web service, However, I will try to step you through it anyway.
I have done with before just using a UISearchBar and it's delegate method, one the user pressed the search button or enter, I would use NSURLSession to pass the user's search terms to the API, and parsed the response.
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
let searchText : String = searchBar.text
webService.getRecipe(ingredient: searchText, completionHandler: { (recipeArray) in
self.highProteinArray = recipeArray
dispatch_async(dispatch_get_main_queue(), {
self.collectionView.reloadData()
})
})
}
As you can see I used a callback to handle setting the newly parsed data to a variable for later use, and then reloaded the collectionView. This way your tableView/collectionView will load and set itself up while you are waiting for the response from the web service and then parsing it, once that is complete you just reload to show the new data.
To add a little extra you could even add a fading in animation in your cellForItemAtIndexPath or cellForRowAtIndexPath, whichever you are using.