set static image to snapshotView(afterScreenUpdates:) - ios

When apps goes background mode, I want to show a default image in capture screen. If user may try to kill the application with double press the home button which is show the list of application, there also need to show my static image.

I tried with this process, If any thing wrong please correct me.
func applicationDidEnterBackground(_ application: UIApplication) {
if((self.imgViewStaticImage) != nil)
{
self.imgViewStaticImage?.removeFromSuperview()
}
self.imgViewStaticImage = UIImageView(frame: (self.window?.bounds)!)
self.imgViewStaticImage?.image = UIImage(named: "banner")
self.window?.addSubview(self.imgViewStaticImage!)
self.window?.bringSubview(toFront: self.imgViewStaticImage!)
}
func applicationWillEnterForeground(_ application: UIApplication) {
if((self.imgViewStaticImage) != nil)
{
self.imgViewStaticImage?.removeFromSuperview()
}
}

Related

iOS app black screen when app coming from background

I am trying to find out what is the cause of a black screen that a comparatively small set of users are facing. Most of them are coming from the background. The app uses ReSwift for data refreshing, although that does not trigger the re run of the didFinishLaunchingWithOptions. I also thought might be OOM (Out Of Memory), but if I provoque it and test it, the app just crashes. For what is worth, I have also observed that is happening to 6s and SE (2nd edition) users. What can I try next?
This is the code that runs on changing its status:
func applicationWillEnterForeground(_ application: UIApplication) {
let internetConnectionStatus = InternetConnectionStatus()
_ = internetConnectionStatus.isOk().subscribe(onNext: { connected in
if !connected {
self.showFloatr(withBody: "ctkit.error.no-internet")
}
})
}
func applicationDidBecomeActive(_ application: UIApplication) {
if let keyWindow = UIApplication.shared.keyWindow,
keyWindow.rootViewController is AuthPasswordRecoveryViewController ||
keyWindow.rootViewController is AuthNavigationViewController {
return
}
UIApplication.shared.applicationIconBadgeNumber = 0
UIApplication.shared.registerForRemoteNotifications()
}
The affected users are running iOS 15.

Check when a system alert is shown in iOS

I have the following usecase.
I want to show an overlay over the current window when the application moves to the task manager.
Currently I am doing the following:
func applicationWillResignActive(_ application: UIApplication) {
guard let window = application.keyWindow else { return }
ApplicationPrivacyProtector.showOverlay(inKeyWindow: window)
}
func applicationDidBecomeActive(_ application: UIApplication) {
guard let window = application.keyWindow else { return }
ApplicationPrivacyProtector.hideOverlay(inKeyWindow: window)
}
This works fine but
applicationWillResignActive is also called when a system alert (eg. faceID, touchID, etc.) is shown which triggers this overlay view as well.
Is there a way to distinguish between system alert and application moves to taskmanager?

HomeKit - How to update accessory isReachable value, when i come back from background to foreground?

Situation: i am developing an app, which manages HomeKit accessories. For example i do that:
Accessory is power on, and i see it via app. Also in foreground mode HMAccessoryDelegate method:
func accessoryDidUpdateReachability(HMAccessory)
works fine and i can handle status of my accessory.
I switch app to background mode.
I turn off accessory (i mean completely power off) so it must be unreachable.
I switch app to foreground mode, but accessory is still reachable.
method func accessoryDidUpdateReachability(HMAccessory) — not called.
value accessory.isReachable not updated.
Example of code when i go to foreground:
func applicationDidBecomeActive(_ application: UIApplication) {
if let home = HomeStore.sharedStore.home {
for accessory in home.accessories {
print(accessory.isReachable) //not updated
for service in accessory.services {
for characteristic in service.characteristics {
characteristic.readValue { (error) in //updated
if error == nil {
let notification = Notification(name: Notification.Name(rawValue: "UpdatedCharacteristic"))
NotificationCenter.default.post(notification)
}
}
}
}
}
}
}
Question: how to update isReachable values of accessories, when i come back from background mode to foreground?
You can create a function in the ViewController that implements HMHomeManagerDelegate:
func startHomeManager() {
manager = HMHomeManager()
manager.delegate = self
// do something here
}
and add a call to startHomeManager() to your viewDidLoad(). That will refresh your HMHome object. Then call this func in your AppDelegate:
func applicationWillEnterForeground(_ application: UIApplication) {
viewController.startHomeManager()
viewController.didRestartHomeManager()
}
A bonus is that you can call startHomeManager() for pull to refresh, etc.

How to fix IUnityGraphicsMetal error when I run Xcode project

I work to integrate an amazing AR model from Unity into Swift 4.
I finally did that using Unity 2018 (version 2018.2.4) and Swift 4.1 (Xcode 9.4.1) but when I run the project on my device (iPhone 7 Plus) is crushing and the error is coming from the path MyProjectName/Unity/Classes/Unity/IUnityGraphicsMetal.h
The error is: Thread 1: EXC_BAD_ACCESS (code=1, address=0x8).
I exported the project from Unity selecting Graphics API as Metal and also as OpenGLES3, none of this helped me.
Also in XCode -> Edit Scheme -> Run -> Options Tab -> Metal API Validation -> I set this one to Disabled, and I still get the same error.
I also update ARKit Plugin (in Unity) to the latest version (1.5) hoping that will fix the problem with UnityGraphicsMetal but apparently not.
Can anyone help me please to fix this error or to guide me to the wright way ?
Here I have 3 screenshots with the errors which maybe can help you more.
Thank you if you are reading this !
EDIT:
Here is the source code for my ViewController which is managing the bridge between Swift and Unity:
import UIKit
class ViewController: UIViewController {
#IBOutlet var rotateSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.startUnity()
NotificationCenter.default.addObserver(self, selector: #selector(handleUnityReady), name: NSNotification.Name("UnityReady"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleUnityToggleRotation(_:)), name: NSNotification.Name("UnityToggleRotation"), object: nil)
}
}
#objc func handleUnityReady() {
showUnitySubView()
}
#objc func handleUnityToggleRotation(_ n: NSNotification) {
if let isOn = n.userInfo?["isOn"] as? NSNumber {
rotateSwitch.isOn = isOn.boolValue
}
}
#IBAction func handleSwitchValueChanged(sender: UISwitch) {
UnityPostMessage("NATIVE_BRIDGE", "PlayHologram", sender.isOn ? "start" : "stop")
}
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:|-75-[view]-0-|", options: [], metrics: nil, views: views)
view.addConstraints(w + h)
}
}
}
Here is the AppDelegate file:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var application: UIApplication?
#objc var currentUnityController: UnityAppController!
var isUnityRunning = false
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
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) {
// 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.
if isUnityRunning {
currentUnityController.applicationWillResignActive(application)
}
}
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.
if isUnityRunning {
currentUnityController.applicationDidEnterBackground(application)
}
}
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.
if isUnityRunning {
currentUnityController.applicationWillEnterForeground(application)
}
}
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.
if isUnityRunning {
currentUnityController.applicationDidBecomeActive(application)
}
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
if isUnityRunning {
currentUnityController.applicationWillTerminate(application)
}
}
func startUnity() {
if !isUnityRunning {
isUnityRunning = true
currentUnityController.applicationDidBecomeActive(application!)
}
}
func stopUnity() {
if isUnityRunning {
currentUnityController.applicationWillResignActive(application!)
isUnityRunning = false
}
}
}
This seems to be a tricky problem, here's a few things which may help, please try them:
In the Unity Editor -> Project Settings -> Player, there is also a checkbox option called "Metal API Validation", you said that you only set the Xcode option to disabled, perhaps this one needs to be disabled as well. Or a combination of on/off for the two.
Instead of selecting Metal and OpenGLES3, try ticking the checkbox "Auto Graphics API". In one of my projects, I left it on manual with Metal selected, but it wouldn't render at all on iOS, fixed it by turning on automatic.
In Color Gamut, have both sRGB and P3 Wide-colour selected. The iPhone 7 and newer all have displays rated for P3 wide-colour.
Hope this helps!

How to lock the app in 5 minutes after entering to background

If my app has been in background for more than 5 minutes, I want to perform the navigation to lock screen view controller. Here is my code. But sometimes it works, as it should, and sometimes it does not work. How can it be fixed?
private var lockTimer: Timer?
func applicationDidEnterBackground(_ application: UIApplication) {
lockTimer = Timer.scheduledTimer(withTimeInterval: 300, repeats: false) { _ in
// Navigation code
}
}
func applicationWillEnterForeground(_ application: UIApplication) {
lockTimer?.invalidate()
lockTimer = nil
}
Background tasks are not guaranteed to run on iOS for very long. There are ways to improve your chances of completing your background task detailed here or here.
An alternative way you can implement this is by saving the time (say in the user defaults) the app enters the background and then when the app opens again, you check the time and move to the lock screen if it has been over 5 minutes.
Try below approach:
func applicationDidEnterBackground(_ application: UIApplication) {
let defaults = UserDefaults.standard
defaults.set(Date(), forKey: "LastInactiveDate")
defaults.synchronize()
}
func applicationWillEnterForeground(_ application: UIApplication) {
let defaults = UserDefaults.standard
if let lastInactiveDate = defaults.object(forKey: "LastInactiveDate") as? Date{
let seconds = Date().timeIntervalSince(lastInactiveDate)
print("Seconds ::" , seconds)
if seconds >= 300{
//Do any thing here to lock the app
}
}
defaults.set(nil, forKey: "LastInactiveDate")
defaults.synchronize()
}

Resources