CMMotionactivitymanager authorizationstatus - ios

I want to get the authorization status for CMMotionActivityManager. For other services like calendar and location we have some property in the API that gives us the user authorization status for these classes. How i can get the authorization status for CMMotionActivityManager class?

CMMotionActivityManager does not currently offer a way to check authorisation status directly like other frameworks.
iOS - is Motion Activity Enabled in Settings > Privacy > Motion Activity
However, as the comments in the above question mention, if you attempt a query using
queryActivityStartingFromDate:toDate:toQueue:withHandler
and the user has not authorised your application, the handler (CMMotionActivityQueryHandler) will return this error.
CMErrorMotionActivityNotAuthorized

With introduction of IOS 11.* there is the possibility to call CMMotionActivityManager.authorizationStatus() which gives you a detailed status.

Here's how I'm doing it :
manager = CMMotionActivityManager()
let today = NSDate()
manager.queryActivityStartingFromDate(today, toDate: today, toQueue: NSOperationQueue.mainQueue(),
withHandler: { (activities: [CMMotionActivity]?, error: NSError?) -> Void in
if let error = error where error.code == Int(CMErrorMotionActivityNotAuthorized.rawValue){
print("NotAuthorized")
}else {
print("Authorized")
}
})

I had to adjust Zakaria's answer a bit for Swift 3.0 and also the new Error made problems, so I had to convert it back to NSError to get the code but this is how my function looks now. Thanks!
func triggerActivityPermissionRequest() {
let manager = CMMotionActivityManager()
let today = Date()
manager.queryActivityStarting(from: today, to: today, to: OperationQueue.main, withHandler: { (activities: [CMMotionActivity]?, error: Error?) -> () in
if error != nil {
let errorCode = (error! as NSError).code
if errorCode == Int(CMErrorMotionActivityNotAuthorized.rawValue) {
print("NotAuthorized")
}
} else {
print("Authorized")
}
manager.stopActivityUpdates()
})
}

Related

NEHotspotConfigurationManager getting this alert:"Unable to join the network<name of network>" while error is nil

So i am trying to monitor the connection status by closers :
func reconnect(success: #escaping () -> Void, failure: #escaping () -> Void) {
let manager = NEHotspotConfigurationManager.shared
let ssid = CameraManager.camera.uuid
let password = "password"
let isWEP = false
let hotspotConfiguration = NEHotspotConfiguration(ssid: ssid, passphrase: password, isWEP: isWEP)
hotspotConfiguration.joinOnce = true
manager.apply(hotspotConfiguration) { (error) in
if (error != nil) {
if let error = error {
switch error._code {
case 8:
print("internal error")
failure()
case 7:
NotificationCenter.default.post(name: Notification.Name(rawValue: "cancelFromHotSpot"), object: nil)
failure()
self.stopSession()
case 13:
success()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.startSession()
}
default:
break
}
}
if error == nil {
print("success connecting wifi")
success()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.startSession()
}
}
}
}
Yet there is a scenario that i am getting this alert "Unable to join the network" while error is nil, any ideas?
I think this behavior is a iOS bug and we cannot avoid.
This problem was also discussed in Apple Developer Forum and answer of Apple staff was below
"I’ve got nothing to say here beyond what I said on 13 Feb. The fact that errors from the Wi-Fi subsystem don’t get reported via the completion handler is expected behaviour. If you don’t like that behavior — and, to be clear, I personally agree with you about that — the best way forward is to file a bug report requesting that it be changed. Please post your bug number, just for the record."
This was discussed here
So I do not have great ideas, unfortunately. All ideas I have are two below (these do not solve this problem perfectly.)
Wait for a bug fix in the future release.
Separate "Applying Configuration" code & Communication code like below.
#IBAction func setConfigurationButtonTapped(_ sender : Any) {
manager.apply(hotspotConfiguration) { (error) in
if(error != nil){
// Do error handling
}else{
// Wait a few seconds for the case of showing "Unable to join the..." dialog.
// Check reachability to the device because "error == nil" does not means success.
}
}
#IBAction func sendButtonTapped(_ sender : Any) {
self.startSession()
}
iOS 13 - Patch
I had the same problem, but I solved it by deleting first all the existing configuration entries:
NEHotspotConfigurationManager.shared.getConfiguredSSIDs { (wifiList) in
wifiList.forEach { NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: $0) }
// ... from here you can use your usual approach to autoconnect to your network
}
Maybe it's not always a possible solution since it's a bit drastic, but for me worked like a charm.
PS: I use this in an app that runs iOS 13. As far as I know should work also on iOS 11 and 12, but I didn't test it.
Remove hotspotConfiguration.joinOnce = true work for me

CloudKit: Get users firstname/surname

I'm trying to get the users first name using cloud kit however the following code is not getting the users first name and is leaving firstNameFromFunction variable empty. Does anyone know how to achieve this in iOS 10?
let container = CKContainer.default()
container.fetchUserRecordID { (recordId, error) in
if error != nil {
print("Handle error)")
}else{
self.container.discoverUserInfo(
withUserRecordID: recordId!, completionHandler: { (userInfo, error) in
if error != nil {
print("Handle error")
}else{
if let userInfo = userInfo {
print("givenName = \(userInfo.displayContact?.givenName)")
print("familyName = \(userInfo.displayContact?.familyName)")
firstNameFromFunction = userInfo.displayContact?.givenName
}else{
print("no user info")
}
}
})
}
}
the permission screen that comes up when asking for the first time, IMO, is very poorly worded. They need to change that. It says "Allow people using 'your app' to look you up by email? People who know your email address will be able to see that you use this app." This make NO sense. This has nothing to do with asking the user to get their iCloud first name, last name, email address.
Speaking of email address - this and the phone number from the lookupInfo property is missing - i.e. set to nil, even though those values are legit and correct. Filing a bug tonight.
First, you will need to request permission to access the user's information.
Then, you can use a CKDiscoverUserIdentitiesOperation. This is just like any other CKOperation (eg. the modify record operation). You just need to create a new operation with the useridentitylookupinfo. Then you will also need to create a completion block to handle the results.
Here is an example function I created:
func getUserName(withRecordID recordID: CKRecordID,
completion: #escaping (String) -> ()) {
if #available(iOS 10.0, *) {
let userInfo = CKUserIdentityLookupInfo(userRecordID: recordID)
let discoverOperation = CKDiscoverUserIdentitiesOperation(userIdentityLookupInfos: [userInfo])
discoverOperation.userIdentityDiscoveredBlock = { (userIdentity, userIdentityLookupInfo) in
let userName = "\((userIdentity.nameComponents?.givenName ?? "")) \((userIdentity.nameComponents?.familyName ?? ""))"
completion(userName)
}
discoverOperation.completionBlock = {
completion("")
}
CKContainer.default().add(discoverOperation)
} else {
// iOS 10 and below version of the code above,
// no longer works. So, we just return an empty string.
completion("")
}
}
First you need to ask the user for permission to be discovered.
Use CKContainer.default().requestApplicationPermission method passing .userDiscoverability on applicationPermission parameter.
The CKContainer.default().discoverUserInfo method is deprecated on iOS 10. Instead use CKContainer.default().discoverUserIdentity method.
Do something like:
CKContainer.default().requestApplicationPermission(.userDiscoverability) { (status, error) in
CKContainer.default().fetchUserRecordID { (record, error) in
CKContainer.default().discoverUserIdentity(withUserRecordID: record!, completionHandler: { (userIdentity, error) in
print("\(userIdentity?.nameComponents?.givenName)")
print("\(userIdentity?.nameComponents?.familyName)")
})
}
}

CMMotionActivityManager authorization status

I'm trying to figure out a way to handle the authorization statuses for Motion activity
Here's what I came up with so far :
manager = CMMotionActivityManager()
manager.queryActivityStartingFromDate(now, toDate: now, toQueue: NSOperationQueue.mainQueue(),
withHandler: { (activities: [CMMotionActivity]?, error: NSError?) -> Void in
if(error != nil){
if(error!.code != Int(CMErrorMotionActivityNotAuthorized.rawValue)){
print("CMErrorMotionActivityNotAuthorized")
}else if(error!.code != Int(CMErrorMotionActivityNotEntitled.rawValue)){
print("CMErrorMotionActivityNotEntitled")
}else if(error!.code != Int(CMErrorMotionActivityNotAvailable.rawValue)){
print("CMErrorMotionActivityNotAvailable")
}
}
})
One problem though :
When I deny the app permission to motion activity (via settings), I get CMErrorMotionActivityNotEntitled
(I believe I should be getting CMErrorMotionActivityNotAuthorized instead)
Any ideas why ? or at least what's the proper way of doing this ?
Perhaps you are getting CMErrorMotionActivityNotAuthorized. You'll never know, with your code, because your code does not ask what code you are getting. It asks what code you are not getting:
if(error!.code != Int(CMErrorMotionActivityNotAuthorized.rawValue)){
print("CMErrorMotionActivityNotAuthorized")
}else if(error!.code != Int(CMErrorMotionActivityNotEntitled.rawValue)){
print("CMErrorMotionActivityNotEntitled")
}else if(error!.code != Int(CMErrorMotionActivityNotAvailable.rawValue)){
print("CMErrorMotionActivityNotAvailable")
}
The != operator means is not. So you are doing a series of checks about what the code is not. It's hard to see how you can get any useful information by asking that question. It might make more sense to ask what the code is, which would involve using the == operator.

Error getting new calendar for new reminders

I'm trying to add a feature in my application so the user can get a list of reminders using the following method.
The following method is the main method I'm using for retrieving the reminders:
func getReminders(){
var eventStore : EKEventStore = EKEventStore()
// This lists every reminder
var calender = getCalender(eventStore)
let calendars = eventStore.calendarsForEntityType(EKEntityTypeReminder)
as! [EKCalendar]
//cals.append(calender)
var predicate = eventStore.predicateForRemindersInCalendars([calender])
eventStore.fetchRemindersMatchingPredicate(predicate) { reminders in
for reminder in reminders {
println(reminder.title)
self.remindersTitles.append(reminder.title!!)
}}
var startDate=NSDate().dateByAddingTimeInterval(-60*60*24)
var endDate=NSDate().dateByAddingTimeInterval(60*60*24*3)
var predicate2 = eventStore.predicateForEventsWithStartDate(startDate, endDate: endDate, calendars: nil)
println("startDate:\(startDate) endDate:\(endDate)")
var eV = eventStore.eventsMatchingPredicate(predicate2) as! [EKEvent]!
if eV != nil {
for i in eV {
println("Title \(i.title!)" )
println("stareDate: \(i.startDate)" )
println("endDate: \(i.endDate)" )
}
}
}
As you notice I'm creating a calendar and assign it the return value of a method called 'getCalender':
func getCalender(store: EKEventStore) -> EKCalendar {
let defaults = NSUserDefaults.standardUserDefaults()
if let id = defaults.stringForKey("GSCalender") {
return store.calendarWithIdentifier(id)
} else {
var calender = EKCalendar(forEntityType: EKEntityTypeReminder, eventStore: store)
calender.title = "Genie Sugar Calender!"
calender.CGColor = UIColor.redColor().CGColor
calender.source = store.defaultCalendarForNewReminders().source!
var error: NSError?
store.saveCalendar(calender, commit: true, error: &error)
if error == nil {
defaults.setObject(calender.calendarIdentifier, forKey: "GSCalender")
}
if calender == nil {
println("nothing here")
}
return calender
}
}
But the issue is that the application is stuck at this line of the second method:
calender.source = store.defaultCalendarForNewReminders().source!
And returns me this error:
Error getting default calendar for new reminders: Error Domain=EKCADErrorDomain Code=1013 "The operation couldn’t be completed.
Any ideas please to overcome this problem? with my advanced thanks
I noticed the iPhone simulator, after it being reset - returns nil for store.defaultCalendarForNewReminders(). I believe it is a simulator bug.
By doing lots of tests, I came up with an observation that the simulators can fetch defaultCalendarForNewReminders() smoothly (with calendar usage permission)....but with the testing on a real device with iOS 14.6 it is returning nil, even an iPhone has the default calendar for events already!
I had also tried with EKCalendarChooser in order to select other calendars, but still it not worked!
So I think it is an iOS bug.

How to identify iCloud logged in user in airplane mode?

I try to get userRecordID in airplane mode, but I get an error, any other way?
class func asdf() {
var defaultContainer = CKContainer.defaultContainer()
var publicDatabase = defaultContainer.publicCloudDatabase
defaultContainer.fetchUserRecordIDWithCompletionHandler({ userRecordID, error in
if error == nil {
println("userRecordID.recordName : \(userRecordID.recordName)")
} else {
println("\(error.localizedDescription)")
}
})
}
Terminal: Couldn't renew our secure session
I put an accountStatusWithCompletionHandler call outside of fetchUserRecordIDWithCompletionHandler, that returned CKAccountStatus.Available.
You cannot detect internet connectivity with CloudKit. It will only give you an error when there is no connectivity. If you do want to test for internet connectivity, then you could use the famous Reachability class like this: How to check for an active Internet connection on iOS or OSX?
If you want to detect changes to the iCloud account, then you can add the following code to your AppDelegate application didFinishLaunchingWithOptions:
var localeChangeObserver = NSNotificationCenter.defaultCenter().addObserverForName(NSUbiquityIdentityDidChangeNotification, object: nil, queue: NSOperationQueue.mainQueue()) { _ in
println("The user’s iCloud login changed: should refresh all user data.")
}
If you then want to fetch the user id, you have to do a container.requestApplicationPermission to see if you are allowed to query an then a container.fetchUserRecordIDWithCompletionHandler. Bit this requires internet connection. You could cache it on the device together with the detection code above to get the correct status.
I came across to this code, comparing recently and previous logged in user's token, and if the same, use the previously downloaded userRecordID. The only problem that in some cases on my iPad ubiquityIdentityToken method returns nil even dow I am logged in, strange.
class func checkUser() {
let ubiquityIdentityToken = NSFileManager.defaultManager().ubiquityIdentityToken
let status = Utility.status()
let prevUbiquityIdentityToken = status.objectForKey("ubiquityIdentityToken")
if ubiquityIdentityToken != nil && ubiquityIdentityToken!.isEqual(prevUbiquityIdentityToken) {
} else if ubiquityIdentityToken != nil && !ubiquityIdentityToken!.isEqual(prevUbiquityIdentityToken) {
status.setObject(ubiquityIdentityToken!, forKey: "ubiquityIdentityToken")
Utility.saveStatus(status)
let defaultContainer = CKContainer.defaultContainer()
let publicDatabase = defaultContainer.publicCloudDatabase
defaultContainer.fetchUserRecordIDWithCompletionHandler({ userRecordID, error in
if error == nil {
//do some stuff
})
} else {
println("\(error.localizedDescription)")
}
})
} else {
//do some stuff
status.removeObjectForKey("ubiquityIdentityToken")
Utility.saveStatus(status)
}
}

Resources