Is there a better way in Swift to check if a specific flag is set on application.currentUserNotificationSettings().types?
For example how would you check if the application is allow to update it's badge?
Below is the current method I'm using, but I thought there might be a better way in Swift, like some operator I don't know about.
func printUserNotificationSettings() {
println("Notification Settings:")
let notificationSettingsTypes = UIApplication.sharedApplication().currentUserNotificationSettings().types
let badgeOn: Bool = (notificationSettingsTypes & UIUserNotificationType.Badge) == UIUserNotificationType.Badge
let soundOn: Bool = (notificationSettingsTypes & UIUserNotificationType.Sound) == UIUserNotificationType.Sound
let alertOn: Bool = (notificationSettingsTypes & UIUserNotificationType.Alert) == UIUserNotificationType.Alert
println("\tBadge? \(badgeOn)")
println("\tSound? \(soundOn)")
println("\tAlert? \(alertOn)")
}
Looks like the only thing you can do to improve the code is make it more concise.
func printUserNotificationSettings() {
println("Notification Settings:")
let notificationSettingsTypes = UIApplication.sharedApplication().currentUserNotificationSettings().types
let badgeOn = (notificationSettingsTypes & .Badge) != nil
let soundOn = (notificationSettingsTypes & .Sound) != nil
let alertOn = (notificationSettingsTypes & .Alert) != nil
println("\tBadge? \(badgeOn)")
println("\tSound? \(soundOn)")
println("\tAlert? \(alertOn)")
}
UIUserNotificationType implements RawOptionSetType which is the swift mapping from NS_OPTIONS in Objective C code. In earlier betas of Xcode, these objects also implemented BooleanType, which would have allowed you to write this code a little more concisely, but that seems to have been removed prior to release.
Also, searching around, the most common way to do the check is != nil so I have included that modification as well, it seems to improve the readability a bit.
Here is a pretty robust StackOverflow post on the topic: Switch statement for imported NS_OPTIONS (RawOptionSetType) in Swift?
And another great article on the background of RawOptionSetType: http://nshipster.com/rawoptionsettype/
Related
I need to build a quiz type application using Siri.
Here, let us consider, my application have only one question along with multiple choices as answers.
Now with the Siri voice commands, I need to load that question along with choices onto the Label/ textview.
Once that question gets loaded onto the Lable/textview, this question should be spoken by Siri.
Now the user can choose his/her answer either a or b or c or d using their voice command.
Then Siri should validate the user's input with the correct answer.
If its correct, Siri should say”correct answer”. Otherwise It should say “wrong answer. The correct answer is and so on..”
I have implemented half part of my requirements, Using Siri shortcuts, I can able to load the question onto a Label and Siri can speak that question.
for your reference, please find my code below.
In ViewController.swift file I have implemented below code,
Public fund createShortcutForloadingQuestion(){
let userAct = NSUserActivity(activityType: "com.organization.QuizSpeakingApp.loadQuestion")
userAct.title = "get the text from document"
userAct.userInfo = ["question" : "what is the capital of India? \n a. Kolkata \n b. Mumbai, \n c. Bengaluru,\n d. New Delhi"]
userAct.isEligibleForSearch = true
userAct.isEligibleForPrediction = true
userAct.persistentIdentifier = NSUserActivityPersistentIdentifier("com.organization.QuizSpeakingApp.loadQuestion")
textLbl.userActivity = userAct
userAct.becomeCurrent()
displayTextOnLabel()
}
public func displayTextOnLabel() {
textLbl.text = “what is the capital of India? \n a. Kolkata \n b. Mumbai, \n c. Bengaluru,\n d. New Delhi.”
}
To speak the loaded question I have implemented the code like below,
public func speakTheQuestion(){
let speechUtterance = AVSpeechUtterance(string: textLbl.text ?? "Label Doesnt have any text")
speechUtterance.rate = 0.5
speechUtterance.pitchMultiplier = 1.0
speechUtterance.volume = 1.0
speechUtterance.postUtteranceDelay = 0.005
speechSynthesizer.speak(speechUtterance)
}
Now to create a shortcut for loading the question and to speak the loaded question,
I have implemented the below method in AppDelegate.swift.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
let viewController = window?.rootViewController as! ViewController
viewController.displayTextOnLabel()
viewController.speakTheQuestion()
return true
}
With the above implementation I can able to load the question and spoken it by Siri.
Now, Anyone can help me how to take user answer with voice command, and how to validate their answer with the correct answer and how to give update about his answer with Siri?
Thanks in andvance.
as of now Sirikit handle only a specific domains and intents.
More about SiriKit
For your use case I would suggest you to use SpeechKit. Speechkit take voice as input and convert it to text. It has accuracy and works amazingly. You can ask question to user and then starts speech recognition using speechKit and read user's reply.
recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
var isFinal = false
if result != nil {
let string = result?.bestTranscription.formattedString
self.textView.text = string
if((string?.elementsEqual("A"))! || (string?.elementsEqual("Option A"))!)
{
//user said option A is correct, perform your action
}
else if((string?.elementsEqual("B"))! || (string?.elementsEqual("Option B"))!)
{
//user said option B is correct, perform your action
}
//......
isFinal = (result?.isFinal)!
}
if error != nil || isFinal {
self.audioEngine.stop()
inputNode.removeTap(onBus: 0)
self.recognitionRequest = nil
self.recognitionTask = nil
self.microPhoneButton.isEnabled = true
}
})
more about SpeechKit in iOS
I'm working with the Photos framework, specifically I'd like to keep track of the current camera roll status, thus updating it every time assets are added, deleted or modified (mainly when a picture is edited by the user - e.g a filter is added, image is cropped).
My first implementation would look something like the following:
private var lastAssetFetchResult : PHFetchResult<PHAsset>?
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let fetchResult = lastAssetFetchResult,
let details = changeInstance.changeDetails(for: fetchResult) else {return}
let modified = details.changedObjects
let removed = details.removedObjects
let added = details.insertedObjects
// update fetch result
lastAssetFetchResult = details.fetchResultAfterChanges
// do stuff with modified, removed, added
}
However, I soon found out that details.changedObjects would not contain only the assets that have been modified by the user, so I moved to the following implementation:
let modified = modifiedAssets(changeInstance: changeInstance)
with:
func modifiedAssets(changeInstance: PHChange) -> [PHAsset] {
var modified : [PHAsset] = []
lastAssetFetchResult?.enumerateObjects({ (obj, _, _) in
if let detail = changeInstance.changeDetails(for: obj) {
if detail.assetContentChanged {
if let updatedObj = detail.objectAfterChanges {
modified.append(updatedObj)
}
}
}
})
return modified
}
So, relying on the PHObjectChangeDetails.assetContentChanged
property, which, as documentation states indicates whether the asset’s photo or video content has changed.
This brought the results closer to the ones I was expecting, but still, I'm not entirely understanding its behavior.
On some devices (e.g. iPad Mini 3) I get the expected result (assetContentChanged = true) in all the cases that I tested, whereas on others (e.g. iPhone 6s Plus, iPhone 7) it's hardly ever matching my expectation (assetContentChanged is false even for assets that I cropped or added filters to).
All the devices share the latest iOS 11.2 version.
Am I getting anything wrong?
Do you think I could achieve my goal some other way?
Thank you in advance.
Compiling on Swift 2.0, running IOS 9.3 working with Xcode 7.2.1 under 10.11.3
Trying to implement a code 39 bar code scanner, but unable to figure how to cast this without crashing my code in the process, unless I use this hack.
AVFoundation returns both AVMetadataFaceObject & AVMetadataMachineReadableCodeObject objects when scanning my bar code.
If I try and cast the wrong object into the wrong type it crashes, and for the life of I can not seem to find a way of figuring out which type of code it is looking at beyond this hack.
Tried guard statement; no crash. Tried do/catch; no crashes. Tried to test the type, but nothing seems to work.
if metadataObjects == nil || metadataObjects.count == 0 {
//print("No code is detected")
return
} else {
let A1 = String(metadataObjects[0])
if (A1.hasPrefix("<AVMetadataFaceObject:")) {
print("Face -> \(A1)")
}
if (A1.hasPrefix("<AVMetadataMachineReadableCodeObject:")) {
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
self.lblDataInfo.text = metadataObj.stringValue
self.lblDataType.text = metadataObj.type
print("Machine -> \(A1) ")
}
}
Ok, this works, but I don't think its good coding practice, I fear it is hack.
Recently, I have been working on an application. The goal of the application is to show you your last 10 photos taken in the Today view of the Notification Center, among other things. The application works perfectly in the iOS Simulator, but as soon as I put it on a real device for testing, it fails. It returns the error:
fatal error: Unexpected nil while unwrapping an Optional value
Typically this is very easy to fix since XCode highlights the code that returned nil and gives you the opportunity to fix it. In this case, none of my code gets highlighted. Instead, it highlights a line from Thread 1 (Is this the correct term? Thread 1?), as seen below:
Also note that above the highlited line is the line
; function signature specialization <Arg[0] = Exploded, Arg[1] = Exploded of Swift.(_fatalErrorMessage (Swift.StaticString, Swift.StaticString, Swift.StaticString, Swift.UInt) -> ()).(closure #2)
I included this line in the picture because of the "fatalErrorMessage" part of it. I suspect this could clue me in on the error, but I have no idea what this means. I'm not yet to the point of understanding that.
Also, after some thought, I placed a breakpoint at the viewDidLoad() function in the hopes of tracking any nil values, but the code appears to never even get to this point. It seems as if none of my code is being run.
Has anyone had problems like this/understands what that error code (If that's what it is) means? I'm pretty desperate now, hence me being here.
Thanks,
CodeIt
EDIT:
I placed a println line inside the viewDidLoad function to double check if it was being run. The println function runs and outputs correctly, so I think I may just have messed up my breakpoint somehow. Anyway - the code runs, but it still doesn't highlight any of my code causing the nil value.
EDIT 2:
As requested, I have inserted parts of my code. Please keep in mind that this code is a "first draft", if you will, and I have not yet gotten around to cleaning it up. I'm just trying to get it to work:
#IBOutlet weak var currentPosLabel: UILabel!
var currentImgPos = 0
#IBOutlet weak var imageView: UIImageView!
var images: NSMutableArray!
var totalImageCountNeeded: Int!
func fetchPhotos() {
images = NSMutableArray()
totalImageCountNeeded = 10
self.fetchPhotoAtIndexFromEnd(0)
}
func fetchPhotoAtIndexFromEnd(index: Int) {
let imgManager = PHImageManager.defaultManager()
var requestOptions = PHImageRequestOptions()
requestOptions.synchronous = true
var fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions) {
if fetchResult.count > 0 {
imgManager.requestImageForAsset(fetchResult.objectAtIndex(fetchResult.count - 1 - index) as? PHAsset, targetSize: view.frame.size, contentMode: PHImageContentMode.AspectFill, options: requestOptions, resultHandler: { (image, _) in
self.images.addObject(image)
if index + 1 < fetchResult.count && self.images.count < self.totalImageCountNeeded {
self.fetchPhotoAtIndexFromEnd(index + 1)
} else {
println("Completed array: \(self.images)")
}
})
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
// NOTE: I am calling the fetchPhotos() function here since earlier in my debugging I thought the problem may be that I was calling it too early in the viewDidLoad function. It an incorrect theory, but it was worth a try.
fetchPhotos()
if images.count > 0 {
imageView.image = images[1] as? UIImage
}
}
I removed some parts of the code that I know have no reasons to cause my error, such as #IBActions, didReceiveMemoryWarning(), etc..
Even it's old question. I found my issue by deleting derived data from xcode.
Xcode -> window->Projects then select and delete your project derived data.
I don't think it is possible to determine exactly where the problem is without seeing your code.
Somewhere in your code you may have forced downcast a variable as!. If you change this to:
if let variable = optionalVariable as? SomeClass {
//Insert your code
}
Then this should fix your problem. Read this to learn more about casting in swift:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html
You should run the same device on simulator and real device.As an example if you run on 5s on real device then try the same device on simulator.By running simulator, it will definitely show the error.My error was the not connecting the #IBActions. Hope that this tip will help you.
I received the same error when trying to set a non-optional value to the value of an implicitly-unwrapped optional that was nil.
Ex. someGlobalFunction() returns an ImplicitlyUnwrappedOptional with a nil value then you try to set that to a regular value.
func someGlobalFunction() -> String! { return nil }
class MyClass {
let aVariable: String
init() {
aVariable = someGlobalFunction()
}
}
i don't understand why this code doesn't work, the detector is always nil with the CIDetectorTypeQRCode constant, everything work with CIDetectorTypeFace.
I Supect a bug from API of Apple. This a the official doc : Apple documentation
#IBAction func analyseTag(sender: AnyObject) {
var detector:CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context:nil, options:[CIDetectorAccuracy: CIDetectorAccuracyHigh])
var decode = ""
var ciImage:CIImage = CIImage(image: ImgChoosed.image)
var message:String = "";
let features = detector.featuresInImage(ciImage)
for feature in features as [CIQRCodeFeature] {
message += feature.messageString
}
if(message == "") {
println("nothing")
} else {
println("\(message)")
}
}
Have you a solution?
Thank in advance guy's
The code you provided can't have a nil detector because it's not an optional and the compiler would complain about several places in your code if it was.
If features is empty then you know it didn't find a QR code in your image. Try providing a better image or turning down the CIDetectorAccuracy.
If features isn't empty then your cast is failing.
Edit:
You can't pass a nil context in the constructor.
This happened to us as well. iPhone 4s doesn't return a CIDetector of type QRCode. The other types (rectangle, face) work though…
The same code works as expected on the iPhone 6. Haven't tested on a 5 or 5s yet.
But two weeks ago it was still working on the 4s, I believe. It was still on iOS 8 back then, I guess.
Make sure your ImgChoosed.image is not nil.
Change another input image for testing.
Try for feature in features as! [CIQRCodeFeature].
I found that using on a device resolved this issue. The simulator seemed to always return nil for me.