Could some one help me to understand why i'm not receiving sound from remote notifications in IOS 10 Swift 3.
Here is my code:
AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]?) -> Bool {
let settings = UNUserNotificationCenter.current()
settings.requestAuthorization(options: [.badge, .alert, .sound]) {(granted, error) in
}
return true
}
// Override point for customization after application launch.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
let viewController = self.window?.rootViewController as! LoadingCloudDataViewController
let notification: CKNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String: NSObject])
if(notification.notificationType == CKNotificationType.query) {
let queryNotification = notification as! CKQueryNotification
let recordID = queryNotification.recordID
viewController.fetchRecord(recordID: recordID!)
}
}
And here is the notification subscription code:
func subscriptionNotificationsKcal() {
publicDatabase = container.publicCloudDatabase
let predicate = NSPredicate(format: "TRUEPREDICATE")
let subscription = CKQuerySubscription(recordType: "Kcal", predicate: predicate, options: .firesOnRecordCreation)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertBody = "New Calories Item was Added"
notificationInfo.shouldBadge = true
subscription.notificationInfo = notificationInfo
publicDatabase?.save(subscription, completionHandler: ({returnRecord, error in
if let err = error {
print("subscription failed %#", err.localizedDescription)
}else{
print("sub success")
}
}))
}
By default the sound is muted. To activate it, in the case of this example, use the following code:
notificattionInfo.soundName = "default"
Related
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
{
switch challenge.protectionSpace.authenticationMethod {
case NSURLAuthenticationMethodClientCertificate:
let query: [String:Any] = [kSecAttrAccessGroup as String: kSecAttrAccessGroupToken,
kSecAttrKeyClass as String : kSecAttrKeyClassPrivate,
kSecClass as String : kSecClassIdentity,
kSecReturnAttributes as String : kCFBooleanTrue as Any,
kSecReturnRef as String: kCFBooleanTrue as Any,
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnPersistentRef as String: kCFBooleanTrue as Any
]
var result : AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess, let allItems = result as? [[String: Any]] else {
let errorDescription = SecCopyErrorMessageString(status, nil)
print(errorDescription as Any)
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let items = allItems.filter { item in
return (item["tkid"] as? String)?.starts(with: "Bundle ID") ?? false
// return true
}
// **items are the certificates which is already added in device**
// Let user select which cert to use, handle pin entry and call the completion handler.
let alert = UIAlertController.selectCertAndPinEntryAlert(certs: items, completionHandler: completionHandler)
alert.show()
case NSURLAuthenticationMethodServerTrust:
let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
completionHandler(URLSession.AuthChallengeDisposition.useCredential, credential);
case NSURLAuthenticationMethodHTTPBasic:
print("Basic auth")
default:
completionHandler(.useCredential, nil)
}
}
selectCertAndPinEntryAlert : this method allows user to choose the certificate which is already added in Keychain.
static func selectCertAndPinEntryAlert(certs: [[String : Any]], completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> UIAlertController {
// Select cert UIAlertController.
let alert = UIAlertController(title: "Select certificate", message: nil, preferredStyle: .actionSheet)
certs.forEach { item in
guard let certData = item["certdata"] as? Data else { return }
guard let certificate = SecCertificateCreateWithData(nil, certData as CFData) else { return }
alert.addAction(UIAlertAction(title: certificate.commonName,
style: .default,
handler: { action in
// Pin entry UIAlertController created after the user selected one of the certs.
let passwordAlert = UIAlertController(title: "Enter pin", message: nil, preferredStyle: .alert)
passwordAlert.addTextField { textField in
textField.isSecureTextEntry = true
}
passwordAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
completionHandler(.cancelAuthenticationChallenge, nil)
})
passwordAlert.addAction(UIAlertAction(title: "Ok",
style: .default,
handler: { action in
guard let pin = passwordAlert.textFields?.first?.text else { completionHandler(.cancelAuthenticationChallenge, nil); return }
** let secIdentity = item["v_Ref"] as! SecIdentity
let urlCredential = URLCredential(identity: secIdentity, certificates: nil, persistence: .forSession)**
// If this doesn't return a UserDefaults something is broken in the project and we might as well crash.
UserDefaults(suiteName: "App Group which used by app")!.writePin(pin)
**completionHandler(.useCredential, urlCredential)**
}))
passwordAlert.show()
}))
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
completionHandler(.cancelAuthenticationChallenge, nil)
})
return alert
}
Please provide the suggestion what i am doing wrong. What code i need to change?
Note : I am using Yubikey for fetching certificates and storing it into the keychain. Later All the added certificate popup i ll get to choose one.
I have tried this code but when i am entring the key to access the certificate, WKWebView is not taking this certificate as further process and i am getting URL Credentials as null in response.
I'm having a scenario like :
1) I want to create Http POST request and for this I'm having the data, please see this image:
2) As you can see in the above image, I have to create post a request with the mentioned body and also I'm getting response named: token. How to create post request and fetch this token response?.
3) That token response will allow me to login into myapp.
I'm newbie to this scenario. I have tried some code by my own but still getting confuse in how to combine my app delegate code with this POST Request Code.
Code
#IBAction func signinaction(_ sender: Any) {
self.username.resignFirstResponder()
self.password.resignFirstResponder()
if (self.username.text == "" || self.password.text == "") {
let alertView = UIAlertController(title: "Login failed",
message: "Wrong username or password." as String, preferredStyle:.alert)
let okAction = UIAlertAction(title: "Try Again!", style: .default, handler: nil)
alertView.addAction(okAction)
self.present(alertView, animated: true, completion: nil)
return
}
// Check if the user entered an email
if let actualUsername = self.username.text {
// Check if the user entered a password
if let actualPassword = self.password.text {
// Build the body message to request the token to the web app
self.bodyStr = "username=8870417698&password=1234&grant_type=password" + actualUsername + "&password=" + actualPassword
// Setup the request
let myURL = NSURL(string: "http://ezschoolportalapi.azurewebsites.net/token")!
let request = NSMutableURLRequest(url: myURL as URL)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.httpBody = bodyStr.data(using: String.Encoding.utf8)!
let task = URLSession.shared.dataTask(with: request as URLRequest) {
(data, response, error) -> Void in
if data?.count != 0
{
do {
let tokenDictionary:NSDictionary = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! NSDictionary
print(tokenDictionary)
// Get the token
let token:String = tokenDictionary["access_token"] as! String
// Keep record of the token
let userdefaults = UserDefaults()
let saveToken = userdefaults.set(token, forKey: "access_token")
userdefaults.synchronize()
// Dismiss login view and go to the home view controller
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
}
}
catch {
// Wrong credentials
// Reset the text fields
self.username.text = ""
self.password.text = ""
// Setup the alert
let alertView = UIAlertController(title: "Login failed",
message: "Wrong username or password." as String, preferredStyle:.alert)
let okAction = UIAlertAction(title: "Try Again!", style:.default, handler: nil)
alertView.addAction(okAction)
self.present(alertView, animated: true, completion: nil)
return
}
}
}
task.resume()
}
}
}
Question is how to combine this code with my above code :
let appDelegate = UIApplication.shared.delegate as! AppDelegate appDelegate.gotoMainvc()
if I use directly this code then in any of the case I'm able to switch over to my home screen it doesn't matter whether m using this POST Request code or not. Please Help.
You are hard coding the username and password in bodyStr update it to
self.bodyStr = "username=" + actualUsername + "&password=" + actualPassword + "&grant_type=password"
Update the statements inside do with
do {
let tokenDictionary:NSDictionary = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! NSDictionary
print(tokenDictionary)
// Get the token
if let authToken = tokenDictionary["access_token"] as? String{
self.token = authToken
UserDefaults.standard.set(accessToken, forKey: "access_token")
UserDefaults.standard.synchronize()
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
}
}
}
You can implement a function like isUserLoggedIn which will return true if token is saved in userDefaults. You need to check whether the user has logged in in the appDelegate's applicationdidFinishLaunchingWithOptions.Like
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if isUserLoggedIn(){
//showHomeViewController
} else{
//showLoginViewController
}
return true
}
func isUserLoggedIn() -> Bool{
if let accessToken = UserDefaults.standard.object(forKey: "access_token") as? String {
if (accessToken.characters.count)! > 0{
return true
} else {
return false
}
}
else {
return false
}
}
For a recent project I tried to pull some data from a server in the SOAP and oData format respectively, that is protected with a Microsoft NTLM authentication, and it has been a nightmare figuring out how to do it, none of the online examples really worked.
So here is my solution; I had to adapt, expand and combine a few different sources. I hope this helps someone in the future.
You might have to allow arbitrary loads!!
Adapted from:
https://gist.github.com/stevenschobert/f374c999e5cba6ccf09653b846967c83
https://blogs.msdn.microsoft.com/chiranth/2013/09/20/ntlm-want-to-know-how-it-works/
import UIKit
class ViewController: UIViewController {
var username: String? = nil
var password: String? = nil
lazy var conn: URLSession = {
let config = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
return session
}()
override func viewDidLoad() {
super.viewDidLoad()
username = "<username>"
password = "<password>"
ntlm()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func ntlm() {
let urlString = "<url>"
let url = URL(string: urlString)
let request = NSMutableURLRequest(url: url!, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 60000)
request.httpMethod = "GET"
let task = conn.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
print(response)
print(error)
print(String(data: data!, encoding: .utf8))
})
task.resume()
}
func doesHaveCredentials() -> Bool {
guard let _ = self.username else { return false }
guard let _ = self.password else { return false }
return true
}
}
extension ViewController: URLSessionDelegate {
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print("got challenge")
guard challenge.previousFailureCount == 0 else {
print("too many failures")
challenge.sender?.cancel(challenge)
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodNTLM else {
print("unknown authentication method \(challenge.protectionSpace.authenticationMethod)")
challenge.sender?.cancel(challenge)
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
guard self.doesHaveCredentials() else {
challenge.sender?.cancel(challenge)
completionHandler(.cancelAuthenticationChallenge, nil)
DispatchQueue.main.async {
print("Userdata not set")
};
return
}
let credentials = URLCredential(user: self.username!, password: self.password!, persistence: .forSession)
challenge.sender?.use(credentials, for: challenge)
completionHandler(.useCredential, credentials)
}
}
I am implementing UserNotification in my app. When the notification gets fired it shows two action, in one i want to add snooze effect, it must snooze after 5 mins again. How to handle it ? thanks for all ! help if any one do have idea
Well to snooze notification you can create another notification with same details of current notification and increase the fire date by 5 mins.
Here is the code I used :
func snoozeScheduledNotification(notification:UILocalNotification) -> Void {
// Snooze for 10 mins
let localNotification = UILocalNotification()
localNotification.fireDate = notification.fireDate?.addingTimeInterval(60*10)
localNotification.repeatInterval = NSCalendar.Unit(rawValue: 0) // 0 = No Repeat
localNotification.alertBody = notification.alertBody
localNotification.soundName = notification.soundName
localNotification.userInfo = notification.userInfo
localNotification.category = notification.category
UIApplication.shared.scheduleLocalNotification(localNotification)
}
Hope it helps you.
The shortest and simplest code I found about it
For Swift 3/4
extension UNNotification {
func snoozeNotification(for hours: Int, minutes: Int, seconds: Int) {
let content = UNMutableNotificationContent()
content.title = "Another Alert"
content.body = "Your message"
content.sound = .default()
let identifier = self.request.identifier
guard let oldTrigger = self.request.trigger as? UNCalendarNotificationTrigger else {
debugPrint("Cannot reschedule notification without calendar trigger.")
return
}
var components = oldTrigger.dateComponents
components.hour = (components.hour ?? 0) + hours
components.minute = (components.minute ?? 0) + minutes
components.second = (components.second ?? 0) + seconds
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
debugPrint("Rescheduling failed", error.localizedDescription)
} else {
debugPrint("rescheduled success")
}
}
}
}
You just need to call it this way :
response.notification.snoozeNotification(for: 0, minutes: 0, seconds: 30)
Credit goes to Simon Ljungberg : https://gist.github.com/simme/96264d5ceee394083d18e2c64f42a3a9
For iOS10, use this code.
Use this code in AppDelegate.swift file.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
let center = UNUserNotificationCenter.current()
let category = UNNotificationCategory(identifier: "identifier", actions: [], intentIdentifiers: [])
center.setNotificationCategories([category])
center.requestAuthorization(options: [.badge, .alert , .sound]) { (greanted, error) in
print(error)
}
return true
}
You can put this code in any view controller.
let content = UNMutableNotificationContent.init()
content.title = "Notification Title"
content.subtitle = "Notification Sub-Title"
content.body = "Notification Body"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "identifier", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
UNUserNotificationCenter.current().delegate = self
if (error != nil){
//handle here
}
}
You can handle notification using following method:
extension UIViewController: UNUserNotificationCenterDelegate {
public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Swift.Void) {
completionHandler( [.alert, .badge, .sound])
}
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Swift.Void) {
print("Tapped in notification")
}
}
You can use this Blog as reference and Example.
I need to write a downloader for my app, and It can pause, continue and cancel the downloads. Also it must support to pause download, kill the app, and reopen the app and continue from where it paused.
How can i keep the downloaded data and how can I continue it?
import UIKit
import Foundation
import Alamofire
class DownloaderViewController: UIViewController {
#IBOutlet weak var label: UILabel!
let progressIndicatorView = UIProgressView()
var request: Alamofire.Request?
override func viewDidLoad() {
super.viewDidLoad()
}
}
#IBAction func cancelBtn(sender: AnyObject) {
self.request?.cancel()
self.label.text = "% 0.0"
}
#IBAction func pauseBtn(sender: AnyObject) {
self.request?.suspend()
}
#IBAction func continueBtn(sender: AnyObject) {
self.request?.resume()
}
#IBAction func startBtn(sender: AnyObject) {
var localPath: NSURL?
self.request = Alamofire.download(.GET, "https://dl.dropboxusercontent.com/u/11563257/3.%20Interactive_iPad_test_for_PDF_EXPERT.pdf", destination: { (temporaryURL, response) in
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let pathComponent = response.suggestedFilename
localPath = directoryURL.URLByAppendingPathComponent(pathComponent!)
return localPath!
}).progress() {
(_, totalBytesRead, totalBytesExpectedToRead) in
dispatch_async(dispatch_get_main_queue()) {
self.progressIndicatorView.setProgress(Float(totalBytesRead) / Float(totalBytesExpectedToRead), animated: true)
self.updateProgress(self.progressIndicatorView)
if totalBytesRead == totalBytesExpectedToRead {
self.progressIndicatorView.removeFromSuperview()
}
}
}
func updateProgress(prg:UIProgressView) {
let stepSize:Float = 0.1
prg.setProgress(prg.progress + stepSize, animated: true)
self.label.text = "% " + String(format: "%.2f", prg.progress*100)
}
}
This works while the app is running. But I need to save the data when the app is terminated and continue it when the app started. I have no i idea how to keep the downloaded data and how to continue it. Any help will be appriciated.
I find this in Alamofire documentation :
Alamofire.download(.GET, "https://httpbin.org/stream/100", destination: destination)
.response { _, _, data, _ in
if let
data = data,
resumeDataString = NSString(data: data, encoding: NSUTF8StringEncoding)
{
print("Resume Data: \(resumeDataString)")
} else {
print("Resume Data was empty")
}
}
But I get "Resume Data was empty" everytime. I give the same destination. But It can't catct the Resume Data. And I couldn't find an example with Alamofire.