I want get user location in app background. Works fine if I use timer 9 or less seconds if I use 10 and more seconds I can't get user location...
my code (AppDelegate.swift):
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var backgroundTaskIdentifier:UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
var myTimer: NSTimer?
var locationManager = CLLocationManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func isMultitaskingSupported()->Bool{
return UIDevice.currentDevice().multitaskingSupported
}
func timerMethod(sender: NSTimer){
let backgroundTimerRemainig = UIApplication.sharedApplication().backgroundTimeRemaining
self.locationManager.startUpdatingLocation()
if backgroundTimerRemainig == DBL_MAX{
println("test1");
}else{
println("test");
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
}
}
func applicationWillResignActive(application: UIApplication) {
}
func applicationDidEnterBackground(application: UIApplication){
if isMultitaskingSupported() == false{
return
}
myTimer = NSTimer.scheduledTimerWithTimeInterval(60.0,
target: self,
selector: "timerMethod:",
userInfo: nil,
repeats: true)
backgroundTaskIdentifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler { () -> Void in
UIApplication.sharedApplication().endBackgroundTask(self.backgroundTaskIdentifier)
}
}
func endBackgroundTask(){
let mainQueue = dispatch_get_main_queue()
dispatch_async(mainQueue, {[weak self] in
if let timer = self!.myTimer{
timer.invalidate()
self!.myTimer = nil
UIApplication.sharedApplication().endBackgroundTask(self!.backgroundTaskIdentifier)
self!.backgroundTaskIdentifier = UIBackgroundTaskInvalid
}
})
}
func applicationWillEnterForeground(application: UIApplication) {
if backgroundTaskIdentifier != UIBackgroundTaskInvalid{
endBackgroundTask()
}
}
func applicationDidBecomeActive(application: UIApplication) {
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func locationManager(manager: CLLocationManager!, didUpdateToLocation locations:[AnyObject]) {
var latValue = locationManager.location.coordinate.latitude
var lonValue = locationManager.location.coordinate.longitude
}
}
why I can't get user location when I use 10 and more seconds?
Related
I currently have an iOS app and want to embed a Unity application as a subview within my application. I'v been struggling with this over the last couple months on and off.
I was getting issues with building my application for some time, but now it is building and running properly (almost). The view I would like the Unity application to display within isn't displaying anything.
For reference I am using Unity 2019.1.12f1 and XCode 10.3
I am NOT using Vuforia, its just a very basic Unity app with a model and basic animation.
My AppDelegate file is:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var application: UIApplication?
#objc var currentUnityController: UnityAppController!
var isUnityRunning = false
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.application = application
unity_init(CommandLine.argc, CommandLine.unsafeArgv)
currentUnityController = UnityAppController()
currentUnityController.application(application, didFinishLaunchingWithOptions: launchOptions)
// first call to startUnity will do some init stuff, so just call it here and directly stop it again
startUnity()
stopUnity()
return true
}
func applicationWillResignActive(_ application: UIApplication) {f isUnityRunning {
currentUnityController.applicationWillResignActive(application)
}
}
func applicationDidEnterBackground(_ application: UIApplication) {
if isUnityRunning {
currentUnityController.applicationDidEnterBackground(application)
}
}
func applicationWillEnterForeground(_ application: UIApplication) {
if isUnityRunning {
currentUnityController.applicationWillEnterForeground(application)
}
}
func applicationDidBecomeActive(_ application: UIApplication) {
if isUnityRunning {
currentUnityController.applicationDidBecomeActive(application)
}
}
func applicationWillTerminate(_ application: UIApplication) {
if isUnityRunning {
currentUnityController.applicationWillTerminate(application)
}
}
func startUnity() {
if !isUnityRunning {
isUnityRunning = true
currentUnityController.applicationDidBecomeActive(application!)
}
}
func stopUnity() {
if isUnityRunning {
currentUnityController.applicationWillResignActive(application!)
isUnityRunning = false
}
}
I am then using this snippet of code to display the code Unity in the subview
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.startUnity()
self.unityView = UnityGetGLView()
I am absolutely baffled as to why this is not working. Any help would be much appreciated.
Hope this is enough information. Please let me know if there is anything else you need to know.
Your delegate looks all good, but your ViewController is missing some pieces. Try this code:
import UIKit
class UnityViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.startUnity()
showUnitySubView()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.stopUnity()
}
}
func showUnitySubView() {
if let unityView = UnityGetGLView() {
// insert subview at index 0 ensures unity view is behind current UI view
view?.insertSubview(unityView, at: 0)
unityView.translatesAutoresizingMaskIntoConstraints = false
let views = ["view": unityView]
let w = NSLayoutConstraint.constraints(withVisualFormat: "|-0-[view]-0-|", options: [], metrics: nil, views: views)
let h = NSLayoutConstraint.constraints(withVisualFormat: "V:|-64-[view]-49-|", options: [], metrics: nil, views: views)
view.addConstraints(w + h)
}
}
}
I need to detect user inactivity on all view controllers, by detecting all touches made. My current AppDelegate code is not detecting the touches made on UIButton and other UI controls. How do I detect all touches made, including UIButtons, UILabels and UITextfields? I have looked at many Stack Overflow articles but I cant seem to adapt it to my needs. If this is not possible how do I extend my original TimerUIApplication class to the other views to detect touches made. Thanks in advance.
AppDegegate Code:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate{
var window: UIWindow?
static let ApplicationDidTimoutNotification = "AppTimout"
// The timeout in seconds for when to fire the idle timer.
let timeoutInSeconds: TimeInterval = 5
var idleTimer: Timer?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
application.isIdleTimerDisabled = true
let userDefaults = UserDefaults.standard
let defaultValues = ["promotionIsEnabled_preference" : "YES",
"promotionDuration_preference" : "10"]
userDefaults.register(defaults: defaultValues)
userDefaults.synchronize()
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
print ("app started")
self.resetIdleTimer()
idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds, target: self, selector: #selector(AppDelegate.idleTimerExceeded), userInfo: nil, repeats: false)
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touched")
self.resetIdleTimer()
}
// Resent the timer because there was user interaction.
func resetIdleTimer() {
if let idleTimer = idleTimer {
idleTimer.invalidate()
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
self.resetIdleTimer()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds, target: self, selector: #selector(AppDelegate.idleTimerExceeded), userInfo: nil, repeats: false)
}
#objc func idleTimerExceeded() {
NotificationCenter.default.post(name: Notification.Name(rawValue: TimerUIApplication.ApplicationDidTimoutNotification), object: nil)
print ("Inactive User")
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewController(withIdentifier: "mainPromo") as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewControlleripad
self.window?.makeKeyAndVisible()
}
}
Original Code that I used in the AppDelegate.
import UIKit
import Foundation
class TimerUIApplication: UIApplication {
static let ApplicationDidTimoutNotification = "AppTimout"
// The timeout in seconds for when to fire the idle timer.
let timeoutInSeconds: TimeInterval = 5
var idleTimer: Timer?
// Resent the timer because there was user interaction.
func resetIdleTimer() {
if let idleTimer = idleTimer {
idleTimer.invalidate()
}
idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds, target: self, selector: #selector(TimerUIApplication.idleTimerExceeded), userInfo: nil, repeats: false)
}
// If the timer reaches the limit as defined in timeoutInSeconds, post this notification.
#objc func idleTimerExceeded() {
NotificationCenter.default.post(name: Notification.Name(rawValue: TimerUIApplication.ApplicationDidTimoutNotification), object: nil)
print ("out")
}
override func sendEvent(_ event: UIEvent) {
super.sendEvent(event)
if idleTimer != nil {
self.resetIdleTimer()
}
if let touches = event.allTouches {
for touch in touches {
if touch.phase == UITouchPhase.began {
self.resetIdleTimer()
}
}
}
}
}
Swift 4 - Create a CustomWindow in your AppDelegate.swift which goes over your Storyboard Views, that registers touches, but does not cancel them.
Code:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate{
var topWindow: CustomWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
topWindow = CustomWindow(frame: UIScreen.main.bounds)
topWindow?.rootViewController = UIViewController()
topWindow?.windowLevel = UIWindowLevelNormal + 1
topWindow?.isHidden = false
}
Create a CustomWindow.swift class that recieves your actions and handles them. Code:
import UIKit
class CustomWindow: UIWindow{
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// What you want to do in here.
return false
}
I need to do something if iPhone detect (beacon in background) with UUID: "3E06945C-F54A-4BE4-ABB2-79764DA6AC6F" so I am trying to do this, in AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.sound, .alert], categories: nil))
initBeaconManager()
//Fabric.with([Crashlytics.self])
//Fabric.with([Appsee.self])
return true
}
func applicationWillTerminate(_ application: UIApplication) {
print("Application will terminate")
}
func applicationDidEnterBackground(_ application: UIApplication) {
isInForeground = false
isInBackground = true
extendBackgroundTime()
}
func applicationDidBecomeActive(_ application: UIApplication) {
isInForeground = true
isInBackground = false
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "initCarPlaceNotification"), object: nil)
}
func extendBackgroundTime() {
if backgroundTask != UIBackgroundTaskInvalid {
// Still running
return
}
else {
backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "Ranging", expirationHandler: { () in
// Background task expired by iOS
UIApplication.shared.endBackgroundTask(self.backgroundTask)
self.backgroundTask = UIBackgroundTaskInvalid
});
DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { //
if !self.isInForeground {
while true {
Thread.sleep(forTimeInterval: 1)
}
}
}
}
func initBeaconManager() {
let beaconsUUIDs = ["1":"3E06945C-F54A-4BE4-ABB2-79764DA6AC6F"]
locationManager = CLLocationManager()
if (locationManager!.responds(to: #selector(CLLocationManager.requestAlwaysAuthorization))) {
locationManager!.requestWhenInUseAuthorization()
}
locationManager!.delegate = self
locationManager!.pausesLocationUpdatesAutomatically = true
for (key, value) in beaconsUUIDs {
let beaconUUID:UUID = UUID(uuidString: value)!
let beaconRegion = CLBeaconRegion(proximityUUID: beaconUUID, identifier: "reg-\(key)")
locationManager!.startMonitoring(for: beaconRegion)
locationManager!.startRangingBeacons(in: beaconRegion)
}
locationManager!.startUpdatingHeading()
locationManager!.startUpdatingLocation()
}
extension AppDelegate {
func rangeForAllBeacons(_ beacons:[CLBeacon]) {
if beacons[0].proximityUUID == UUID(uuidString: "3E06945C-F54A-4BE4-ABB2-79764DA6AC6F") {
displayNotification("work fine..")
}
}
extension AppDelegate:CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
if !isInForeground {
extendBackgroundTime()
}
print(beacons)
for beacon in beacons {
if beacon.rssi < -20 {
nearbyBeacons.append(beacon)
}
}
if firstRegionDetected == "" {
firstRegionDetected = region.identifier
} else if firstRegionDetected == region.identifier {
rangeForAllBeacons(nearbyBeacons)
nearbyBeacons = []
}
}
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
if isInBackground {
extendBackgroundTime()
}
}
But it doesn't work. On the info.plist I use
Privacy - Location Always Usage Description
and
Privacy - Location When In Use Usage Description
Here is my code:
#IBAction func sendSweet(sender: AnyObject) {
//delegate method
let newSweet = CKRecord(recordType: "Extra1")
newSweet["content"] = textField.text
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(newSweet, completionHandler: { (record:CKRecord?, error:NSError?) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.beginUpdates()
self.sweets.insert(newSweet, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Top)
self.tableView.endUpdates()
self.textField.text = ""
self.textField.resignFirstResponder()
})
}})
// Put the CloudKit private database in a constants
let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase
// Create subscription and set three of its properties (an id, a predicate, and options)
let friendsSubscription = CKSubscription(recordType: "Extra1",
predicate: NSPredicate(format: "TRUEPREDICATE"),
subscriptionID: "Extra1",
options: .FiresOnRecordCreation)
// Create a notification and set two of its properties (alertBody and shouldBadge)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertBody = "New message in Lms Chat"
notificationInfo.shouldBadge = false
// Attach the notification to the subscription
friendsSubscription.notificationInfo = notificationInfo
// Save the subscription in the private database
privateDatabase.saveSubscription(friendsSubscription) {recordReturned, error in
// On the main thread, display an error/success message in the textView
if error != nil {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = "Cloud error\n\(error!.localizedDescription)"
}
} else {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = ""
}
}
}
}
func textFieldShouldEndEditing(textField: UITextField!) -> Bool { //delegate method
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
It's a messaging app so people can message eachother but I also want them to recieve notifications. This is the code for notifications and I also have some code for notifications in App Delegate:
import UIKit
import CloudKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
UIApplication.sharedApplication().registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let cloudKitNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String:NSObject])
if cloudKitNotification.notificationType == CKNotificationType.Query {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
}
func resetBadge () {
let badgeReset = CKModifyBadgeOperation(badgeValue: 0)
badgeReset.modifyBadgeCompletionBlock = { (error) -> Void in
if error == nil {
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
}
}
CKContainer.defaultContainer().addOperation(badgeReset)
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
resetBadge()
}
func applicationWillEnterForeground(application: UIApplication) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
func applicationDidBecomeActive(application: UIApplication) {
resetBadge()
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
But notifications do not come in even though I have all of the code needed to make notifications every time someone sends a message. Am I missing something? Thanks!
You are adding data to the public database but you are creating your subscription on the private database. One of those two needs to be changed to match the other.
I want get a user's location in app background. It works fine if I use a timer of 9 or less seconds. If I use 10 and more seconds I can't get the user's location...
My AppDelegate.swift:
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var backgroundTaskIdentifier: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
var myTimer: NSTimer?
var locationManager = CLLocationManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func isMultitaskingSupported() -> Bool {
return UIDevice.currentDevice().multitaskingSupported
}
func timerMethod(sender: NSTimer) {
let backgroundTimerRemainig = UIApplication.sharedApplication().backgroundTimeRemaining
self.locationManager.startUpdatingLocation()
if backgroundTimerRemainig == DBL_MAX {
println("test1")
} else {
println("test")
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
}
}
func applicationWillResignActive(application: UIApplication) {
}
func applicationDidEnterBackground(application: UIApplication){
if !isMultitaskingSupported() {
return
}
myTimer = NSTimer.scheduledTimerWithTimeInterval(60.0,
target: self,
selector: "timerMethod:",
userInfo: nil,
repeats: true)
backgroundTaskIdentifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler {
() -> Void in
UIApplication.sharedApplication().endBackgroundTask(self.backgroundTaskIdentifier)
}
}
func endBackgroundTask() {
let mainQueue = dispatch_get_main_queue()
dispatch_async(mainQueue) {
[weak self] in
if let timer = self!.myTimer {
timer.invalidate()
self!.myTimer = nil
UIApplication.sharedApplication().endBackgroundTask(self!.backgroundTaskIdentifier)
self!.backgroundTaskIdentifier = UIBackgroundTaskInvalid
}
}
}
func applicationWillEnterForeground(application: UIApplication) {
if backgroundTaskIdentifier != UIBackgroundTaskInvalid {
endBackgroundTask()
}
}
func applicationDidBecomeActive(application: UIApplication) {
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func locationManager(manager: CLLocationManager!, didUpdateToLocation locations: [AnyObject]) {
var latValue = locationManager.location.coordinate.latitude
var lonValue = locationManager.location.coordinate.longitude
}
}
Why can't I get the user's location when I use 10 and more seconds?
Because approximately after 10 seconds after calling didEnterBackground the app gets suspended.
The app is in the background but is not executing code. The system
moves apps to this state automatically and does not notify them before
doing so. While suspended, an app remains in memory but does not
execute any code.
It's the iOS app lifecycle.
If you want to get location updates in background you have two options:
background task - but it only keeps the app running for 3 minutes maximum
Run update locations background mode.