I want to show notification when app close on swift 3. is it possible,
how to do it? could you advise me?
//get Data
func getData(){
let url=URL(string:"http://MyGPS/service.php")
do {
let allContactsData = try Data(contentsOf: url!)
let allContacts = try JSONSerialization.jsonObject(with: allContactsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : NSArray]
if let arrJSON = allContacts["mygps"] {
for index in 0...arrJSON.count-1 {
vibration.append(aObject["vibration"] as! String)
}
}
}
catch {
}
}
//show notification
func notification(){
let content = UNMutableNotificationContent()
content.title = "GPS alert message"
// content.subtitle = "Do you know?"
content.body = "Your GPS detect vibration!!"
content.badge = 1
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "timerDone", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
// call function
func someBackgroundTask(timer:Timer) {
DispatchQueue.global(qos: DispatchQoS.background.qosClass).async {
//print("do some background task")
self.getData()
DispatchQueue.main.async {
//self.updatMarker()
}
}
}
// Use Timer
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) {
timer in
self.someBackgroundTask(timer: timer)
}
Related
Here is my async function class:
class MoviesViewModel: ObservableObject {
#Published var topRated: [Movie] = []
#Published var popular: [Movie] = []
#Published var upcoming: [Movie] = []
func getUpcomingMovies() {
if let movies = getMovies(path: "upcoming") {
DispatchQueue.main.async {
self.upcoming = movies
}
}
}
func getPopularMovies() {
if let movies = getMovies(path: "popular") {
DispatchQueue.main.async {
self.popular = movies
}
}
}
func getTopRatedMovies() {
DispatchQueue.main.async {
if let movies = self.getMovies(path: "top_rated") {
self.topRated = movies
}
}
}
func getMovies(path: String) -> [Movie]? {
var movies: [Movie]?
let urlString = "https://api.themoviedb.org/3/movie/\(path)?api_key=\(apiKey)&language=en-US&page=1"
guard let url = URL(string: urlString) else { return [] }
let session = URLSession.shared
let dataTask = session.dataTask(with: url, completionHandler: { data, _, error in
if error != nil {
print(error)
}
do {
if let safeData = data {
let decodedData = try JSONDecoder().decode(NowPlaying.self, from: safeData)
DispatchQueue.main.async {
movies = decodedData.results
}
}
}
catch {
print(error)
}
})
dataTask.resume()
return movies
}
}
When I printed the movies in getMovies function, I can get movies from api without problem. However, UI does not update itself. I used DispatchQueue.main.async function but it did not solve my problem. What can I do in this situation?
dataTask works asynchronously. Your code returns nil even before the asynchronous task is going to start. You have to use a completion handler as described in Returning data from async call in Swift function.
I highly recommend to use async/await in this case. You get rid of a lot of boilerplate code and you don't need to care about dispatching threads.
#MainActor
class MoviesViewModel: ObservableObject {
#Published var topRated: [Movie] = []
#Published var popular: [Movie] = []
#Published var upcoming: [Movie] = []
func getUpcomingMovies() async throws {
self.upcoming = try await getMovies(path: "upcoming")
}
func getPopularMovies() async throws {
self.popular = try await getMovies(path: "popular")
}
func getTopRatedMovies() async throws {
self.topRated = try await getMovies(path: "top_rated")
}
func getMovies(path: String) async throws -> [Movie] {
let urlString = "https://api.themoviedb.org/3/movie/\(path)?api_key=\(apiKey)&language=en-US&page=1"
guard let url = URL(string: urlString) else { throw URLError(.badURL) }
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(NowPlaying.self, from: data).results
}
}
I am trying to make a Graph API call after getting the access token but not able to make the graph API call function.
Facing the error in calling the function, actually I don't have an idea about the HTTP response but I need a user credentials for login and the logout functions, if suppose I got a user credentials means I have to send the data's from login function to logout function.
struct FieldUIViewRepresentable : UIViewControllerRepresentable{
func makeUIViewController(context: Context) -> some UIViewController {
let log = loginviewcontroller()
return log
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
class loginviewcontroller : UIViewController,UIWindowSceneDelegate{
var lableText : String = "default Login"
var account : MSALAccount? = nil
let kAuthority = "https://login.microsoftonline.com/common"
let kGraphEndpoint = "https://graph.microsoft.com/"
var accessToken : String = ""
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
let button = UIButton(frame: CGRect(x: 100, y: 100, width: 200, height: 60))
button.setTitle("Login click", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .blue
button.addTarget(self, action: #selector(login), for: .touchUpInside)
self.view.addSubview(button)
let button2 = UIButton(frame: CGRect(x: 100, y: 180, width: 200, height: 60))
button2.setTitle("Logout", for: .normal)
button2.setTitleColor(.white, for: .normal)
button2.backgroundColor = .red
button2.addTarget(self, action: #selector(logout), for: .touchUpInside)
self.view.addSubview(button2)
}
#objc func login(){
let config = MSALPublicClientApplicationConfig(clientId: "0f166c0f-55e1-4de1-b6fe-e0c35c331f4b")
let scopes = [""]
if let application = try? MSALPublicClientApplication(configuration: config) {
let viewController = self
let webviewParameters = MSALWebviewParameters(authPresentationViewController: viewController)
let interactiveParameters = MSALInteractiveTokenParameters(scopes: scopes, webviewParameters: webviewParameters)
application.acquireToken(with: interactiveParameters, completionBlock: { (result, error) in
guard let authResult = result, error == nil else {
print("The auth result is \(error!)")
print("Auth result localized error \(error!.localizedDescription)")
return
}
print("Auth Result is \(authResult.account)")
self.account = authResult.account
let accessToken = authResult.accessToken
self.accessToken = accessToken
self.getContentWithToken()
print("The access token is \(accessToken)")
let accountIdentifier = authResult.account.identifier
print("Account Identifier \(String(describing: accountIdentifier))")
})
}
else {
print("Unable to create application.")
}
}
func getContentWithToken() {
print("get content with token function invokes")
// Specify the Graph API endpoint
let graphURI = getGraphEndpoint()
let url = URL(string: graphURI)
var request = URLRequest(url: url!)
request.setValue("Bearer \(self.accessToken)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
return
}
guard let result = try? JSONSerialization.jsonObject(with: data!, options: []) else {
return
}
}.resume()
}
func getGraphEndpoint() -> String {
return kGraphEndpoint.hasSuffix("/") ? (kGraphEndpoint + "v1.0/me/") : (kGraphEndpoint + "/v1.0/me/");
}
#objc func logout(){
let account = self.account ?? nil //* account retrieved above
let config = MSALPublicClientApplicationConfig(clientId: "0f166c0f-55e1-4de1-b6fe-e0c35c331f4b")
let scopes = [""]
let application = try? MSALPublicClientApplication(configuration: config)
let viewController = self
let webviewParameters = MSALWebviewParameters(authPresentationViewController: viewController)
let signoutParameters = MSALSignoutParameters(webviewParameters: webviewParameters)
signoutParameters.signoutFromBrowser = false
application?.signout(with: account!, signoutParameters: signoutParameters, completionBlock: {(success, error) in
if let error = error {
// Signout failed
return
}
print("Signout is Completed")
// Sign out completed successfully
})
}
}
I'm trying to get contacts (swift 3) in ios 9
func getInnerContacts()-> Observable<[CNContact]>{
return Observable<[CNContact]>.create({ (observer) -> Disposable in
var contacts = [CNContact]()
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactPhoneNumbersKey] as [Any]
self.contactStore.requestAccess(for: .contacts, completionHandler: { (granted, error) -> Void in
if granted {
let predicate = CNContact.predicateForContactsInContainer(withIdentifier: self.contactStore.defaultContainerIdentifier())
do {
contacts = try self.contactStore.unifiedContacts(matching: predicate, keysToFetch: keysToFetch as! [CNKeyDescriptor])
contacts = newContacts.filter({ (contact) -> Bool in
return !contact.phoneNumbers.isEmpty && contact.givenName != "" || !contact.phoneNumbers.isEmpty && contact.familyName != ""
})
observer.onNext(contacts)
observer.onCompleted()
}catch {
observer.onError(error)
}
}
})
return Disposables.create()
})
}
but it doesn't work, moreover, I can't see permission alert.
Before executing i first check the iOS version, like this:
if #available(iOS 10 , *){
}else if #available(iOS 9, *){
}
then i added permission tag Privacy - Contacts Usage Description into Info.plist file. This works fine in ios 10, any ideas how can I achieve this in ios 9
It will work fine in Swift 3.
var store = CNContactStore()
var contacts = [CNContact]()
Try this in viewDidLoad():
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactEmailAddressesKey, CNContactOrganizationNameKey, CNContactImageDataKey]
let request = CNContactFetchRequest(keysToFetch: keysToFetch as [CNKeyDescriptor])
do {
try self.store.enumerateContacts(with: request) { contact, stop in
self.contacts.append(contact)
}
DispatchQueue.main.async(execute: {
for contact in self.contacts {
print("contact:\(contact)")
let firstName=String(format:"%#",contact.givenName)
// print("first:\(firstName)")
self.givenNameArray.append(firstName)
let lastName=String(format:"%#",contact.familyName)
// print("last:\(lastName)")
self.familyNameArray.append(lastName)
let comapny=String(format:"%#",contact.organizationName)
// print("company:\(comapny)")
self.organizationNameArray.append(comapny)
//get all phone numbers
// for ContctNumVar: CNLabeledValue in contact.phoneNumbers
// {
// let MobNumVar = (ContctNumVar.value ).value(forKey: "digits") as? String
// print("ph no:\(MobNumVar!)")
// get one phone number
let MobNumVar = (contact.phoneNumbers[0].value ).value(forKey: "digits") as! String
// print("mob no:\(MobNumVar)")
self.phonenosArray.append(MobNumVar)
// get all emails
// for ContctNumVar1: CNLabeledValue in contact.emailAddresses
// {
// print("email \(ContctNumVar1.value(forKey: "value") as! String)")
// }
// get one email
let email = (contact.emailAddresses[0]).value(forKey: "value") as! String
// print("email:\(email)")
self.emailsArray.append(email)
let imagedat=contact.imageData
// print("image data:\(imagedat)")
let image=UIImage(data: imagedat!)
// print("image:\(image)")
self.imagesArray.append(image!)
}
})
} catch {
print(error)
}
I am trying to use youtube api in ios swift, and following this tutorial
http://www.appcoda.com/youtube-api-ios-tutorial/
HTTP Status Code = 403
Error while loading channel details: nil
I'm using swift 3
var urlString = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=\(textField.text)&type=\(type)&key=\(apiKey)"
urlString = urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
// Create a NSURL object based on the above string.
let targetURL = URL(string: urlString)
// Get the results.
performGetRequest(targetURL, completion: { (data, HTTPStatusCode, error) -> Void in
if HTTPStatusCode == 200 && error == nil {
// Convert the JSON data to a dictionary object.
do {
let resultsDict = try JSONSerialization.jsonObject(with: data!, options: []) as! Dictionary<String, AnyObject>
// Get all search result items ("items" array).
let items: Array<Dictionary<String, AnyObject>> = resultsDict["items"] as! Array<Dictionary<String, AnyObject>>
// Loop through all search results and keep just the necessary data.
for i in 0 ..< items.count {
let snippetDict = items[i]["snippet"] as! Dictionary<String, AnyObject>
// Gather the proper data depending on whether we're searching for channels or for videos.
if self.segDisplayedContent.selectedSegmentIndex == 0 {
// Keep the channel ID.
self.desiredChannelsArray.append(snippetDict["channelId"] as! String)
}
else {
// Create a new dictionary to store the video details.
var videoDetailsDict = Dictionary<String, AnyObject>()
videoDetailsDict["title"] = snippetDict["title"]
videoDetailsDict["thumbnail"] = ((snippetDict["thumbnails"] as! Dictionary<String, AnyObject>)["default"] as! Dictionary<String, AnyObject>)["url"]
videoDetailsDict["videoID"] = (items[i]["id"] as! Dictionary<String, AnyObject>)["videoId"]
// Append the desiredPlaylistItemDataDict dictionary to the videos array.
self.videosArray.append(videoDetailsDict)
// Reload the tableview.
self.tblVideos.reloadData()
}
}
} catch {
print(error)
}
// Call the getChannelDetails(…) function to fetch the channels.
if self.segDisplayedContent.selectedSegmentIndex == 0 {
self.getChannelDetails(true)
}
}
else {
print("HTTP Status Code = \(HTTPStatusCode)")
print("Error while loading channel videos: \(error)")
}
// Hide the activity indicator.
self.viewWait.isHidden = true
})
return true
}
// MARK: Custom method implementation
func performGetRequest(_ targetURL: URL!, completion: #escaping (_ data: Data?, _ HTTPStatusCode: Int, _ error: NSError?) -> Void) {
// let request = NSMutableURLRequest(url: targetURL)
// request.httpMethod = "GET"
var request = URLRequest(url: targetURL)
request.httpMethod = "GET"
let sessionConfiguration = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfiguration)
/* let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: NSError?) -> Void in
DispatchQueue.main.async(execute: { () -> Void in
completion(data, (response as! HTTPURLResponse).statusCode, error)
})
} as! (Data?, URLResponse?, Error?) -> Void)*/
/* let task = session.dataTask(with: request, completionHandler: ({ (data: Data?, response: URLResponse?, error: NSError?) -> Void in
DispatchQueue.main.async(execute: { () -> Void in
completion(data as Data?, (response as! HTTPURLResponse).statusCode, error)
})
} as! (Data?, URLResponse?, Error?) -> Void))*/
let task = session.dataTask(with: request) { data, response, error in DispatchQueue.main.async { completion(data, (response as! HTTPURLResponse).statusCode, error as? NSError) } }
task.resume()
}
First of all the JSON dictionary representation in Swift 3 is [String:Any] (aka Dictionary<String,Any>)
Second of all in Swift 3 all parameter labels in closures have been removed
func performGetRequest(_ targetURL: URL, completion: #escaping (Data?, Int, NSError?) -> Void) {
Do not use implicit unwrapped optionals for method parameter types. Either use regular optional (?) or non-optional.
Error 403 means Forbidden Access. Make sure you have the correct apiKey from google/youtube developer.
I also used the appcoda youtube api tutorial (which is in Swift 2 I think) and this is a working version of mine for swift 3.
func getVideosForChannelAtIndex() {
let urlString = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=\(playlistID)&maxResults=\(maxResults)&key=\(apiKey)"
// Create a NSURL object based on the above string.
let targetURL = URL(string: urlString)
// Fetch the playlist from Google.
performGetRequest(targetURL!) { (data, HTTPStatusCode, error) -> Void in
if HTTPStatusCode == 200 && error == nil {
do {
self.videos = []
// Convert the JSON data into a dictionary.
let resultsDict = try JSONSerialization.jsonObject(with: data!, options: []) as! Dictionary<AnyHashable, Any>
// Get all playlist items ("items" array).
let items:Array<Dictionary<AnyHashable, Any>> = resultsDict["items"] as! Array<Dictionary<AnyHashable, Any>>
// Use a loop to go through all video items.
// for var i=0; i<items.count; ++i
for i in 0 ..< items.count {
let playlistSnippetDict = (items[i] as Dictionary<AnyHashable, Any>)["snippet"] as! Dictionary<AnyHashable, Any>
let video = Video()
video.title = playlistSnippetDict["title"] as? String
// video.thumbnail =
video.videoId = (playlistSnippetDict["resourceId"] as? Dictionary<AnyHashable, Any>)?["videoId"] as? String
guard let thumbnail = ((playlistSnippetDict["thumbnails"] as? Dictionary<AnyHashable, Any>)?["high"] as? Dictionary<AnyHashable, Any>)?["url"] as? String else {
video.thumbnail = UIImage(named: "Icon1024x1024")
return
}
guard let url:URL? = URL(string: thumbnail), let data:Data? = try? Data(contentsOf: url!) else {
video.thumbnail = UIImage(named: "Icon1024x1024")
return
}
if let dataImage = data {
video.thumbnail = UIImage(data: dataImage)
} else {
video.thumbnail = UIImage(named: "Icon1024x1024")
}
self.videos.append(video)
// Reload the tableview.
self.tblVideos.reloadData()
}
} catch {
print("json error: \(error)")
}
} else {
print("")
print("HTTP Status Code = \(HTTPStatusCode)")
print("")
//Show alertDialog here with Error
print("Error while loading videos: \(error?.localizedDescription)")
let alert = UIAlertView(title: "Oops!", message: error?.localizedDescription, delegate: self, cancelButtonTitle: "OK")
alert.show()
}
// Hide the activity indicator.
self.viewWait.isHidden = true
}
}
This is for the performGetRequest
func performGetRequest(_ targetURL: URL, completion: #escaping (_ data: Data?, _ HTTPStatusCode: Int?, _ error: Error?) -> Void) {
var request = URLRequest(url: targetURL)
request.httpMethod = "GET"
let sessionConfiguration = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfiguration)
let task = session.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
completion(data, (response as? HTTPURLResponse)?.statusCode, error)
})
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
askForPermission()
}
#IBAction func addLocalNotification(_ sender: AnyObject) {
addLocalNotification()
}
func addLocalNotification() {
let content = UNMutableNotificationContent()
content.title = "iOS10.0"
content.body = "Hello Buddy"
content.sound = UNNotificationSound.default()
// Deliver the notification in five seconds.
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5, repeats: false)
let request = UNNotificationRequest.init(identifier: "FiveSecond", content: content, trigger: trigger)
// Schedule the notification.
let center = UNUserNotificationCenter.current()
center.add(request) { (error) in
print(error)
}
print("should have been added")
}
func askForPermission() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (granted, error) in
}
}
You have to implement a delegate to UNUserNotificationCenter to tell the system you want to display the notification while the app is running. See the sample here: https://github.com/jerbeers/DemoLocalNotification