2 app delegates, swift and objective-c - objective-c

So, I have an app mixed with objective-c and swift, (originally objective-c) And I need to figure out how to have 2 app delegates (one for swift, and the other for objective-c). I've done some research, but found nothing. Please help!
EDIT:
So, I succesfully switched, but now in my delegate, i have this code:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let splitViewController = window!.rootViewController as! UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
splitViewController.delegate = self
return true
}
But, splitViewController isn't the first vc, so how would I go around fixing it? (I don't know swift very well)

You only need one delegate, do it in Swift. If you create a new app Xcode will use some default code, which as of 8.3.1 is the following:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
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.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
To answer the comment: it doesn't mess up your code. Remove previous Objective C delegate if needed.

Related

Attempt to present UITabBarController whose view is not in the view hierarchy - Firebase, Swift

I'm working on the login system for an app, and when a user registers, I would like it to go straight "into" the app. Meaning, not back to the login screen, and then redirected "into" the app, or prompted to then login after registering. The desired outcome is working with the following code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//The disallowed character set is the inverse of the allowed characters
disallowedUsernameCharacters = allowedUsernameCharacters.invertedSet
//Set the delegates
confirmPassTextField.delegate = self
passwordTextField.delegate = self
usernameTextField.delegate = self
emailTextField.delegate = self
//Observe authentication events, if the authentication is successful, perform the segue
ref.observeAuthEventWithBlock { (authData) -> Void in
if authData != nil {
//Use standard defaults
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
//Store the uid
prefs.setObject(authData.uid as String, forKey: "UID")
self.performSegueWithIdentifier(self.successfulSignUp, sender: nil)
}
}
}
In which if the user registers, and is authenticated, it will perform the segue to the UITabBarController which contains 5 tabs/views for the app itself. This works fine, but I am given the Warning: Attempt to present on whose view is not in the window hierarchy!
From doing some research, this seems to be a fairly common warning that people get. However, I would like to fix it so that all portions of the app will behave as expected. I found that some people recommended putting the code portion:
//Observe authentication events, if the authentication is successful, perform the segue
ref.observeAuthEventWithBlock { (authData) -> Void in
if authData != nil {
//Use standard defaults
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
//Store the uid
prefs.setObject(authData.uid as String, forKey: "UID")
self.performSegueWithIdentifier(self.successfulSignUp, sender: nil)
}
}
Inside of the viewWillAppear method because of the current view potentially not being on the hierarchy at the time the segue is called, but I don't think that is the fix here, because it's not being called immediately, only on authentication events - also I don't want to put it here because my understanding is that this method is called frequently depending on events.
I also found that some people were executing it on a different thread with something along the lines of:
dispatch_async(dispatch_get_main_queue(), {
self.performSegueWithIdentifier(self.successfulSignUp, sender: nil)
})
But what I am thinking is that, the "register" view controller that is triggering the segue, is not the top most view controller and that's why the error is occurring. Also, I read that a solution is to embed a UINavigationController, if this is the solution...does anyone know any resources on how to do this? If there is a different solution, what would it be? Any help would be great! Cheers.
EDIT:
My Storyboard looks like this:
The entry point is to the UITabBarController, and in the AppDelegate.swift I check to see if the user is logged in by checking a boolean value in the user defaults, if they aren't logged in, I change the rootController to the LoginViewController, in which the user can login, and it will segue to the UITabBarController, or they can register, and on successful registration, it will segue to the UITabBarController - it is here that I'm getting the warning
I have not figured out why, and once I do I will update the answer, but to resolve this i put the line self.performSegueWithIdentifier(self.LoginToFeed, sender: nil)
into a seperate function
func callSegue() {
self.performSegueWithIdentifier(self.LoginToFeed, sender: nil)
}
and call the function from the block and the warning no longer appears

Open iCloud Pics in my app

I have to show all the iCloud picture in my app. So i have to use those photos for further actions.
Is there any framework which i have use to implement the iCloud functionality.
I have checked the various codes and tuts but not able to get the images form the iCloud.
You should use Photos Framework to get access to photos on iCloud. Photos Framework also support photo editing.
For more details you can follow this link - Photos Framework
Perhaps what you are looking for is the UIImagePickerControllerDelegate.
Make your class to inherit UIImagePickerControllerDelegate;
Create a function to pick image from library or camera, initialize a pickerController constant, set delegate to self, get pickerController source type, from camera or library, set allowsEditing to true and present view controller.
Check the code below:
class YourClass: UIViewController, UIImagePickerControllerDelegate {
...
#IBAction func picImage(sender: UIButton) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
pickerController.allowsEditing = true
self.presentViewController(pickerController, animated: true, completion: nil)
// before user finish picking image, another func has to run, imagePickerController.
}
// After user pick the image, this method will dismiss active view controller.
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
This is well understood when watching the link tutorial about CoreData, the video is a bit long but worth your time.
My thanks to Jason Rybka, https://www.youtube.com/watch?v=1kCKlv1npw0.
Works for Swift 2.1 / iOS 9.2.
Good luck.

NSStoryboardSegue sample code (Yosemite Storyboard)

OS X Yosemite introduced NSStoryboardSegue
“A storyboard segue specifies a transition or containment relationship between two scenes in a storyboard…”
Update:
• If I attempt to use a NSStoryboardSegue subclass in a Storyboard with Yosemite., it crashes with SIGABRT.
• If I ignore segues, and manually present a view controller using a specified, custom animator for presentation and dismissal,
func presentViewController(_ viewController: NSViewController,
animator animator: NSViewControllerPresentationAnimator)
it works as expected.
This post provides additional insight: Animate custom presentation of ViewController in OS X Yosemite
Using that as a reference, here's my attempt so far:
class FadeSegue: NSStoryboardSegue {
override func perform() {
super.perform()
sourceController.presentViewController(destinationController as NSViewController,
animator: FadeTransitionAnimator())
}
}
class FadeTransitionAnimator: NSObject, NSViewControllerPresentationAnimator {
func animatePresentationOfViewController(toViewController: NSViewController, fromViewController: NSViewController) {
toViewController.view.wantsLayer = true
toViewController.view.layerContentsRedrawPolicy = .OnSetNeedsDisplay
toViewController.view.alphaValue = 0
fromViewController.view.addSubview(toViewController.view)
toViewController.view.frame = fromViewController.view.frame
NSAnimationContext.runAnimationGroup({ context in
context.duration = 2
toViewController.view.animator().alphaValue = 1
}, completionHandler: nil)
}
func animateDismissalOfViewController(viewController: NSViewController, fromViewController: NSViewController) {
viewController.view.wantsLayer = true
viewController.view.layerContentsRedrawPolicy = .OnSetNeedsDisplay
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = 2
viewController.view.animator().alphaValue = 0
}, completionHandler: {
viewController.view.removeFromSuperview()
})
}
}
The problem appears to be with the Swift 'subclassing' of NSStoryboardSegue. If you implement the same functionality using Objective-C, everything works as expected. The problem is specifically with your FadeSeque class. The animator object works fine in either Objective-C or Swift.
So this:
class FadeSegue: NSStoryboardSegue {
override func perform() {
super.perform()
sourceController.presentViewController(destinationController as NSViewController,
animator: FadeTransitionAnimator())
}
}
Will work if provided as an Objective-C class:
#interface MyCustomSegue : NSStoryboardSegue
#end
#implementation FadeSegue
- (void)perform {
id animator = [[FadeTransitionAnimator alloc] init];
[self.sourceController presentViewController:self.destinationController
animator:animator];
}
#end
(I don't think you need to call super )
As this doesn't seem to be documented much anywhere, I have made a small project on github to demonstrate:
NSStoryboardSegue transitions from one NSViewController to another in the same Storyboard
NSViewController present: methods to achieve the same affect to a separate Xib-based NSViewController without using a Storyboard Segue
presentViewController:asPopoverRelativeToRect:ofView:preferredEdge:behavior:
presentViewControllerAsSheet:
presentViewControllerAsModalWindow:
presentViewController:animator:
animator and segue objects in Objective-C and Swift
edit
OK I've tracked down the EXC_BAD_ACCESS issue. Looking in the stack trace it seemed to have something to do with (Objective-C) NSString to (Swift) String conversion.
That made wonder about the identifier property of NSStoryboardSegue. This is used when setting up segues in the Storyboard, and is not so useful in Custom segues created in code. However, it turns out that if you set an identifier in the storyboard to any string value, even "", the crash disappears.
The identifier property is an NSString* in Objective-C
#property(readonly, copy) NSString *identifier
and an optional String in Swift:
var identifier: String? { get }
Note the read-only status. You can only set the identifier on initialising the object.
The designator initialiser for NSStoryboardSegue looks like this in Objective-C:
- (instancetype)initWithIdentifier:(NSString *)identifier
source:(id)sourceController
destination:(id)destinationController
and in Swift:
init(identifier identifier: String,
source sourceController: AnyObject,
destination destinationController: AnyObject)
Note the non-optional requirement in the Swift initialiser. Therein lies the problem and the crash. If you don't deliberately set an identifier in the storyboard, the Custom segue's designated initialiser will be called using a nil value for the identifier. Not a problem in Objective-C, but bad news for Swift.
The quick solution is to ensure you set an identifier string in Storyboard. For a more robust solution, it turns out that you can override the designated initialiser in your custom subclass to intercept a nil-valued string. Then you can fill it in with a default value before passing on to super's designated initialiser:
override init(identifier: String?,
source sourceController: AnyObject,
destination destinationController: AnyObject) {
var myIdentifier : String
if identifier == nil {
myIdentifier = ""
} else {
myIdentifier = identifier!
}
super.init(identifier: myIdentifier,
source: sourceController,
destination: destinationController)
}
I have updated the sample project to reflect this solution
The same issue comes to me since I forgot make Identity to the segue.
After that, my segue subclass could worked fine.
Highly recommend you take a look at the Apple documentation. If you dig into it a bit, you'll notice in the perform method, you can override animations and such:
SWIFT
func perform()
OBJECTIVE-C
- (void)perform
"You can override this method in your NSStoryboardSegue subclass to perform custom animation between the starting/containing controller and the ending/contained controller for a storyboard segue. Typically, you would use Core Animation to set up an animation from one set of views to the next. For more complex animations, you might take a snapshot image of the two view hierarchies and manipulate the images instead of the view objects.*
Regardless of how you perform the animation, you are responsible for installing the destination view controller o window controller (and its contained views) in the right place so that it can handle events. Typically, this entails calling one of the presentation methods in the NSViewController class."
What you might do as well is have a look at some of the iOS UIStoryboardSegue examples out there in the wild and you should find they're quite similar.

Can't endBackgroundTask: no background task exists with identifier, or it may have already been ended

I am using background task to run the timer in the background to update the user's location. It's declared as:
UIBackgroundTaskIdentifier bgTask;
in the header file, and initialized as:
bgTask = UIBackgroundTaskInvalid;
But still, I am getting this message in the gdb:
Can't endBackgroundTask: no background task exists with identifier 23dc, or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.
Why? And how can I solve this?
I lose many days looking for the piece of code or framework that was causing this warning in the debug console Can't end BackgroundTask: no background task exists with identifier 2 (0x2), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.
Finally I've created an empty project Single View App. Only code generated by Xcode, I run the app on simulator, put it in background and I see the same warning.
So I can say it's an iOS 13 issue.
I hope Apple will fix it quickly because in Crashlytics I found some crash in my app caused by it.
In Xcode, switch to the breakpoint navigator (View > Navigators > Show Breakpoint Navigator) then push the + button in the bottom left and select Add Symbolic Breakpoint and enter “UIApplicationEndBackgroundTaskError” as the symbol.
You need to set
bgTask = UIBackgroundTaskInvalid
in two moments
In the expiration handler.
After finishing your task.
I believe you are missing any of those two moments and that is why you are getting that error.
See apple example code:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
link:
https://developer.apple.com/documentation/backgroundtasks/bgtask?language=objc
Reproduced, here is my entire app:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
return true
}
}
And when I take it to background I get the same message. No storyboard or any other noise.
Are you using location update in the background?
If yes, add the below code at the time of getting location authorization from the user - Apple changed the default for allowsBackgroundLocationUpdates to NO from iOS 9 onwards.
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
locationManager.allowsBackgroundLocationUpdates = YES;
}
Please make sure that you activated the 'Location updates' for background modes under your project 'Signing & Capabilities'. Otherwise it will crash.
Using Swift 5 - IOS 13.2.2 - Xcode 11.2 beta 2 (11B44)
I managed to get rid of this error by
enabling Push notifications in the Capabilities of the app
Import the UserNotifications framework
then add this code to
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
// If granted comes true you can enabled features based on authorization.
guard granted else { return }
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
Regarding the “Can’t end BackgroundTask: no background task exists with identifier ...” message, as others have said, this can be a red herring, unrelated to what you’re doing.
Regarding your goal of trying to periodically retrieve the user’s location, rather than trying to keep your app running in the background with a timer, you should instead avail yourself of various services for updating the user location in the background, such as the visits location service or the significant change location service. With these services, the OS can wake your app when there is a change in the user’s location.
I had the same issue and figured out the cause and solution!
Apple has fixed this issue in iOS 13.4 GM though I have workaround for the earlier versions.
While you should embrace using scenes when your app is run under iOS 13 and later, you can fully opt-out while you still support iOS 12 or earlier.
First, completely remove the “Application Scene Manifest” entry from Info.plist.
If there is a scene delegate class, remove it.
If there are any scene related methods in your app delegate, remove those methods.
If missing, add the property var window: UIWindow? to your app delegate.
Your app should now only use the app delegate and under iOS 13 it should have the same life cycle as iOS 12.
Happy coding!
I think there is a bug working with xcode 11.2 and the swiftUI framework because I always get the
"Can't end BackgroundTask: no background task exists with
identifier..."
message even when my app is not working with background tasks!
The good news is that this only stops my app when executing from xcode; use the 'continue program execution' button and the app will be running again.
This has been fixed in iOS 13.4 GM.
https://developer.apple.com/forums/thread/121990

How to re-open the Mac Desktop App after closing it by using X?

I have created an App for Mac Desktop, it is working, but Apple rejected it because when we run the App and close it by using "X", the we can not re-open it from the dock though the App icon is still there but it doesn't open the App again and the main issue for that I am struggling is that "If we close the App then in the menu bar there is no option to open it" other App which I have seen does that.
What should I do?
Here's an answer for Swift 3:
Conform to NSWindowDelegate in your View Controller class. Then hide the window instead of closing it by overriding the following method.
self.view.window?.delegate = self
func windowShouldClose(_ sender: Any) -> Bool {
NSApplication.shared().hide(self)
return false
}
Then unhide the application when the app icon is clicked in the dock.
func applicationDidBecomeActive(_ notification: Notification) {
NSApplication.shared().unhide(self)
}
To implement simple show/hide functionality for the red (x) button, make your App Delegate class the window delegate for your main window, as well.
Then add the following code to it:
- (BOOL)windowShouldClose:(id)sender {
[[NSApplication sharedApplication] hide:self];
return NO;
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
[[NSApplication sharedApplication] unhide:self];
}
my 2 cernts for swift 5/Xcode 10
note: You can call these methods also in ViewController (if useful) to prevent splitting code between NASWindow/NSView-Controllers.
in this case:
class ViewController: NSViewController, **NSWindowDelegate**
{
...
override func viewWillAppear() {
// we choose to delegate ourselves. must be done here, in viewDidLoad window is nil
let window = self.view.window
window!.delegate = self
}
...
no neeed to pass self.. : (as per other guys above.)
func windowShouldClose(_ sender: NSWindow) -> Bool {
NSApplication.shared.hide(nil)
return false // prevent closing.
}
func applicationDidBecomeActive(_ notification: Notification) {
NSApplication.shared.unhide(nil)
}