I am developing an application in a flutter, and I want to extend one library class in AppDelegate.swift file. As iOS is not supporting multiple inheritances I can't extend the library class and FlutterAppDelegate class together.
Can someone help me with this? Is there any other way to achieve this kind of function?
Here is some code snippet for ios
App's AppDelegate.swift
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return true
}
}
This is my library's header class which I also want to extends to AppDelegate
#import <UIKit/UIKit.h>
#interface AppDelegateSEG : UIResponder <UIApplicationDelegate>
#property(strong, nonatomic) UIWindow *window;
#end
This is my AppDelegateSEG.m
// AppDelegateSEG.m
// ObjcPodsSample
//
//
#import "AppDelegateSEG.h"
#import "SEGAppsFlyerIntegrationFactory.h"
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#interface AppDelegateSEG ()
#end
#interface AppDelegateSEG ()<SEGAppsFlyerLibDelegate>
#end
#implementation AppDelegateSEG
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// For ApsFlyer debug logs
[AppsFlyerLib shared].isDebug = YES;
// [[AppsFlyerLib shared] waitForATTUserAutxhorizationWithTimeoutInterval:60];
/*
Based on your needs you can either pass a delegate to process deferred
and direct deeplinking callbacks or disregard them.
If you choose to use the delegate, see extension to this class below
*/
// SEGAppsFlyerIntegrationFactory* factoryNoDelegate = [SEGAppsFlyerIntegrationFactory instance];
SEGAppsFlyerIntegrationFactory* factoryWithDelegate = [SEGAppsFlyerIntegrationFactory createWithLaunchDelegate:self];
SEGAnalyticsConfiguration *config = [SEGAnalyticsConfiguration configurationWithWriteKey:#"********"];
// [config use:factoryNoDelegate];
[config use:factoryWithDelegate]; // use this if you want to get conversion data in the app. Read more in the integration guide
config.enableAdvertisingTracking = YES; //OPTIONAL
config.trackApplicationLifecycleEvents = YES; //OPTIONAL
config.trackDeepLinks = YES; //OPTIONAL
config.trackPushNotifications = YES; //OPTIONAL
[SEGAnalytics debug:YES]; //OPTIONAL
[SEGAnalytics setupWithConfiguration:config];
NSLog(#"before delay");
NSString *userId = [[SEGAnalytics sharedAnalytics] getAnonymousId];
NSLog(#"----%#", userId);
[[SEGAnalytics sharedAnalytics] identify:(userId)];
// [[SEGAnalytics sharedAnalytics] identify:userId];
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Code below is to collect IDFA. Read more here - https://support.appsflyer.com/hc/en-us/articles/360011451918-iOS-SDK-V6-beta-integration-guide-for-developers#integration-34-support-apptrackingtransparency-att
// if (#available(iOS 14, *)) {
// [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
// //....
// }];
// }
}
#pragma mark - UISceneSession lifecycle
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return [[UISceneConfiguration alloc] initWithName:#"Default Configuration" sessionRole:connectingSceneSession.role];
}
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
-(void)onConversionDataFail:(NSError *) error {
NSLog(#"%#",error);
}
-(void)onConversionDataSuccess:(NSDictionary*) installData {
id status = [installData objectForKey:#"af_status"];
if([status isEqualToString:#"Non-organic"]) {
id sourceID = [installData objectForKey:#"media_source"];
id campaign = [installData objectForKey:#"campaign"];
NSLog(#"This is a none organic install. Media source: %# Campaign: %#",sourceID,campaign);
} else if([status isEqualToString:#"Organic"]) {
NSLog(#"This is an organic install.");
}
}
- (void) onAppOpenAttribution:(NSDictionary*) attributionData {
NSLog(#"%#",attributionData);
}
- (void) onAppOpenAttributionFailure:(NSError *)error {
NSLog(#"%#",error);
}
#end
Looks like you need only setup part, declared in didFinishLaunchingWithOptions. You need to create some other class in your PodsSample project where you can handle all setup. So when you need some necessary configs for AppsFlyer or SEGAnalytics, from anywhere you can just call this static (for example) method from your Pod project.
AppConfigProvider.h
#interface AppConfigProvider : NSObject
+ (void)setupConfig
#end
AppConfigProvider.m
#import "SEGAppsFlyerIntegrationFactory.h"
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#implementation AppConfigProvider
+ (void)setupConfig {
// For ApsFlyer debug logs
[AppsFlyerLib shared].isDebug = YES;
// [[AppsFlyerLib shared] waitForATTUserAutxhorizationWithTimeoutInterval:60];
/*
Based on your needs you can either pass a delegate to process deferred
and direct deeplinking callbacks or disregard them.
If you choose to use the delegate, see extension to this class below
*/
// SEGAppsFlyerIntegrationFactory* factoryNoDelegate = [SEGAppsFlyerIntegrationFactory instance];
SEGAppsFlyerIntegrationFactory* factoryWithDelegate = [SEGAppsFlyerIntegrationFactory createWithLaunchDelegate:self];
SEGAnalyticsConfiguration *config = [SEGAnalyticsConfiguration configurationWithWriteKey:#"********"];
// [config use:factoryNoDelegate];
[config use:factoryWithDelegate]; // use this if you want to get conversion data in the app. Read more in the integration guide
config.enableAdvertisingTracking = YES; //OPTIONAL
config.trackApplicationLifecycleEvents = YES; //OPTIONAL
config.trackDeepLinks = YES; //OPTIONAL
config.trackPushNotifications = YES; //OPTIONAL
[SEGAnalytics debug:YES]; //OPTIONAL
[SEGAnalytics setupWithConfiguration:config];
NSLog(#"before delay");
NSString *userId = [[SEGAnalytics sharedAnalytics] getAnonymousId];
NSLog(#"----%#", userId);
[[SEGAnalytics sharedAnalytics] identify:(userId)];
// [[SEGAnalytics sharedAnalytics] identify:userId];
}
#end
Something like this. Also you'll be need to add AppConfigProvider to public headers of your library to be visible outside
Related
I am working on an Analytics Project by Swizzling UIViewController methods viewDidAppear and viewDidAppear, code snippet as follows,
- (void) swizzledViewDidAppear : (BOOL)animated {
if ([UA isAppInitialized]) { // Check if Analytics Initialized
if ([[self class] isSubclassOfClass:[UIViewController class]]) {
[UA startPage:[NSString stringWithFormat:#"%#", NSStringFromClass ([self class])]];
}
}
[self swizzledViewDidAppear:animated];
}
- (void) swizzledViewDidDisappear : (BOOL)animated {
if ([UA isAppInitialized]) { // Check if Analytics Initialized
if ([[self class] isSubclassOfClass:[UIViewController class]]) {
[UA endPage:[NSString stringWithFormat:#"%#", NSStringFromClass ([self class])]];
}
}
[self swizzledViewDidDisappear:animated];
}
This is the code snippet where I want to track only the Custom ViewController, ex: MyViewController or FooViewController...etc and not Framework related classes like UICompatibilityInputViewController, UIInputWindowController...etc.
Please let me know how can I achieve this. I tried to check for Subclass but still at one point Framework classes are getting recorded.
Thanks,
Vijay
you can get a class's NSBundle and check if that bundle is yours.
swift example using an extension:
//code
extension NSObject {
static var isOurClass: Bool {
let appBundle = Bundle.main
let clsBundle = Bundle(for: self.self);
return clsBundle.bundlePath.hasPrefix(appBundle.bundlePath)
}
}
//test
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
print("appdelegate class ours? \(AppDelegate.isOurClass)")
print("NSString class ours? \(NSString.isOurClass)")
return true
}
}
basic objc example:
#import <Foundation/Foundation.h>
#interface T : NSObject
#end
#implementation T
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSBundle *bundleOfApp = [NSBundle mainBundle];
NSLog(#"%#", bundleOfApp.bundlePath);
//ours
T *t = [T new];
NSBundle *bundleOfT = [NSBundle bundleForClass:t.class];
NSLog(#"%#", bundleOfT.bundlePath);
if([bundleOfT.bundlePath hasPrefix:bundleOfApp.bundlePath]) {
NSLog(#"ours");
}
//theirs
bundleOfT = [NSBundle bundleForClass:NSString.class];
NSLog(#"%#", bundleOfT.bundlePath);
if([bundleOfT.bundlePath hasPrefix:bundleOfApp.bundlePath]) {
}
else {
NSLog(#"theirs");
}
}
}
In this case you have make instance of your class like and check it like
MyViewController *myVcntrl = [[MyViewController alloc]init];
if ([myVcntrl isKindOfClass:[MyViewController class]]) {
NSLog(#"Class is of MyViewController type");
}
else {
NSLog(#"Not of MyViewController type");
}
I was swizzling viewDidLoad, loadView and awakeFromNib for measuring load times and there is some strange behavior I've noticed.
For framework classes like UIInputWindowController, UIKeyboardCandidateGridCollectionViewController : awakeFromNib or loadView doesn't get called. They immediately appear after viewDidLoad has been called.
So the following lines of codes:
- (void)swizzled_awakeFromNib
{
NSLog(#"*** Awake from nib %#", NSStringFromClass(self.class));
[self swizzled_awakeFromNib];
}
- (void)swizzled_viewDidLoad
{
NSLog(#"*** View did load %#", NSStringFromClass(self.class));
[self swizzled_viewDidLoad];
}
- (void)swizzled_loadView
{
NSLog(#"*** Load view %#", NSStringFromClass(self.class));
[self swizzled_loadView];
}
Prints:
*** Awake from nib UINavigationController
*** View did load UINavigationController
*** Load view MyLoginViewController
*** View did load UIInputWindowController
*** View did load UIKeyboardCandidateGridCollectionViewController
*** View did load UIInputWindowController
*** View did load UIApplicationRotationFollowingControllerNoTouches
*** View did load MyLoginViewController
So maybe you can add a property to your category and set it to YES if awakeFromNib and/or loadView has called. Then check that bool in your viewDidLoad method to see if its a framework class.
I have a simple tvOS application starting with a UITabBarController and I wish the main view to have the focus when the app launches, not the tab bar.
I've tried playing with self.tabBarController.tabBar.userInteractionEnabled to remove temporarily the focus, but in vain. (Besides I do no like that kind of workaround)
Any clue?
Thanks in advance.
My original solution no longer works on tvOS 9.3, so I found a new one with subclassing UITabBarController:
#interface TVTabBarController : UITabBarController
#property (nonatomic) BOOL useDefaultFocusBehavior;
#end
#implementation TVTabBarController
- (UIView *)preferredFocusedView {
return self.useDefaultFocusBehavior ? [super preferredFocusedView] : self.selectedViewController.preferredFocusedView;
}
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
[super didUpdateFocusInContext:context withAnimationCoordinator:coordinator];
self.useDefaultFocusBehavior = YES;
}
#end
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.tabBarController.tabBar.hidden = YES; // or do it in storyboard
}
If you use storyboard for initial UI setup, don't forget to set custom class TVTabBarController to your tab bar controller there.
Original solution:
Proposed approach with inheriting from UITabBarController didn't work for me because in fact -preferredFocusedView is called twice on startup, so I had to add a counter to return self.selectedViewController.preferredFocusedView for the first 2 calls. But it's a really hacky solution and there's no guarantee that it won't break in future.
So I found a much better solution: force focus update in appdelegate's -applicationDidBecomeActive: on the first call.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.tabBarController.tabBar.hidden = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
static BOOL forceFocusToFirstTab = YES;
if (forceFocusToFirstTab) {
forceFocusToFirstTab = NO;
[self.tabBarController.selectedViewController updateFocusIfNeeded];
}
}
The above approach mostly works but does not allow you to select a tab bar item with click as it returns the tabBar in that case when it should return the selectedItem. Here is an improved version which solves this by returning [super preferredViewController] instead of tabBar in the normal case. This version also hides the tab bar with alpha at launch so that it doesn't flicker in. There are probably more elegant ways to do the hiding.
#interface MWTabBarController ()
#property (nonatomic, assign) BOOL firstTime;
#end
#implementation MWTabBarController
- (void)viewDidLoad {
[super viewDidLoad];
self.firstTime = YES;
self.tabBar.alpha = 0;
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(unAlphaTabBar) userInfo:nil repeats:NO];
}
- (void) unAlphaTabBar
{
self.tabBar.alpha = 1;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (UIView *)preferredFocusedView {
if (self.firstTime) {
self.firstTime = NO;
return self.selectedViewController.preferredFocusedView;
}
else {
return [super preferredFocusedView];
}
}
I've found the solution, so if someone is interested, you just have to subclass UITabBarController and to override preferredFocusedView:
#interface ZTWTabBarController ()
#property (nonatomic, assign) BOOL firstTime;
#end
#implementation ZTWTabBarController
- (void)viewDidLoad {
[super viewDidLoad];
self.firstTime = YES;
}
- (UIView *)preferredFocusedView {
if (self.firstTime) {
self.firstTime = NO;
return self.selectedViewController.preferredFocusedView;
}
else {
return [super preferredFocusedView];
}
}
#end
I was able to achieve this effect very simply with the isHidden property of the UITabBar.
override func viewDidLoad() {
super.viewDidLoad()
self.tabBar.isHidden = true
}
When the user scrolls up to display the tab bar, the UITabBarController will unhide it automatically.
This is the easiest & cleanest solution in my opinion:
override var preferredFocusedView: UIView? {
if tabBar.hidden {
return selectedViewController?.preferredFocusedView
}
return super.preferredFocusedView
}
Since preferredFocusedView is deprecated in tvOS, you should override the preferredFocusEnvironments property instead
Swift 4.0
override var preferredFocusEnvironments: [UIFocusEnvironment] {
if firsTime, let enviroments = selectedViewController?.preferredFocusEnvironments {
firsTime = false
return enviroments
}
return super.preferredFocusEnvironments
}
I need to add Speech Recognition to an App for a Personal Project.
I need the iOS built-in speech recognition framework because it is fast, accurate and it can also recognise your contact names and other information about yourself.
So far, I think I have found the framework which contains the headers for the speech recognition on iOS 8: the SAObjects.framework
I got the headers of Github and added them successfully in my Xcode Project.
The headers I have tried so far are these:
<SAObjects/SASRecognition.h>
<SAObjects/SASStartSpeechDictation.h>
<SAObjects/SASSpeechRecognized.h>
However, I am not sure how to work with them. For instance, these are two possible methods that can fire a Speech Recognition:
SASStartSpeechDictation *object1 = [SASStartSpeechDictation startSpeechDictation];
SASSpeechRecognized *object2 = [SASSpeechRecognized speechRecognized];
When I debug it though, I cannot find any string in any of these objects. So, obviously something is wrong. Maybe I need to set a notification observer?
Another Solution could be to start a Dictation (through the Keyboard)
to a hidden text field (without the keyboard showing).
Like the Activator action for Jailbroken devices, if you are familiar with it.
But I haven't found any methods that can start the Keyboard dictation, or the Activator action source code to find it out.
Maybe someone has experimented with these things and can give me some help?
Please tell me if you need more information about this question :)
Thanks a lot!
So, I managed to find an answer myself. I luckily found a Github repo with some helpful code: https://github.com/erica/useful-things
The code I found is under the appstore unsafe pack/DictationHelper directory. This code helps to use the UIDictationController and start and stop the Dictation, and get the text value. Of course, without any Text Fields...
Important: In order for this to work, you need to have the headers of the UIKit framework, link the framework to the Target and import them in the Project!
However, I modified the code a bit, because the sample code is only available to speak for a specific duration. I needed to stop speaking by pressing a button.
This is the modified code, for anyone who might be interested in the future:
DicationHelper.h:
/*
Erica Sadun, http://ericasadun.com
NOT APP STORE SAFE BUT HANDY
Siri-ready devices only. Will not work in simulator.
Example:
[SpeechHelper speakModalString:#"Please say something"];
[[DictationHelper sharedInstance] dictateWithDuration:5.0f completion:^(NSString *dictationString) {
if (dictationString)
NSLog(#"You said:'%#'", dictationString);
else
NSLog(#"No response");}];
//-> OR: (My modification)
[SpeechHelper speakModalString:#"Please say something"];
[[DictationHelper sharedInstance] startDictation:0 completion:^(NSString *dictationString) {
if (dictationString)
NSLog(#"You said:'%#'", dictationString);
else
NSLog(#"No response");}];
// Then you need to call this to stop the Dictation: [[DictationHelper sharedInstance] stopDictation]
*/
#import <UIKit/UIKit.h>
//#import <Foundation/Foundation.h>
extern NSString *const DictationStringResults;
typedef void (^DictationBlock)(NSString *dictationString);
#interface DictationHelper : NSObject
+ (instancetype) sharedInstance;
- (void) dictateWithDuration: (CGFloat) duration;
- (void) dictateWithDuration: (CGFloat) duration completion:(DictationBlock) completionBlock;
-(void) startDictation:(CGFloat) whatever completion:(DictationBlock) completionBlock;
-(void) stopDictationWithFallback;
#property (nonatomic, readonly) BOOL inUse;
#end
DictationHelper.m
/*
Erica Sadun, http://ericasadun.com
NOT APP STORE SAFE BUT HANDY
Siri-ready devices only. Will not work in simulator.
*/
#import "DictationHelper.h"
#define MAKELIVE(_CLASSNAME_) Class _CLASSNAME_ = NSClassFromString((NSString *)CFSTR(#_CLASSNAME_));
NSString *const DictationStringResults = #"Dictation String Results";
static DictationHelper *sharedInstance = nil;
#class UIDictationController;
#interface UIDictationController
+ (UIDictationController *) sharedInstance;
- (void) startDictation;
- (void) stopDictation;
- (void) preheatIfNecessary;
#end;
#interface DictationHelper () <UITextFieldDelegate>
#end
#implementation DictationHelper
{
UITextField *secretTextField;
id dictationController;
DictationBlock completion;
BOOL handled;
}
- (void) preheat
{
if (!secretTextField)
{
secretTextField = [[UITextField alloc] initWithFrame:CGRectZero];
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
[window addSubview:secretTextField];
secretTextField.inputView = [[UIView alloc] init];
secretTextField.delegate = self;
}
if (!dictationController)
{
MAKELIVE(UIDictationController);
dictationController = [UIDictationController sharedInstance];
[dictationController preheatIfNecessary];
}
}
+ (instancetype) sharedInstance
{
if (!sharedInstance)
{
sharedInstance = [[self alloc] init];
[sharedInstance preheat];
}
return sharedInstance;
}
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *tftext = textField.text;
tftext = [tftext stringByReplacingCharactersInRange:range withString:string];
[[NSNotificationCenter defaultCenter] postNotificationName:DictationStringResults object:tftext];
if (completion) completion(tftext);
// Treat this dictation as handled
handled = YES;
_inUse = NO;
completion = nil;
// Resign first responder
[textField resignFirstResponder];
return YES;
}
- (void) fallback
{
// 1. Test completion
if (!completion) return;
// 2. Check for handled
if (handled)
{
_inUse = NO;
handled = NO;
return;
}
// 3. Assume the dictation didn't work
completion(nil);
// 4. Reset everything
handled = NO;
_inUse = NO;
completion = nil;
// 5. Resign first responder
[secretTextField resignFirstResponder];
}
-(void) startDictation:(CGFloat) whatever completion:(DictationBlock) completionBlock{
if (completionBlock) completion = completionBlock;
if (_inUse)
{
NSLog(#"Error: Dictation Helper already in use");
return;
}
_inUse = YES;
handled = NO;
secretTextField.text = #"";
[secretTextField becomeFirstResponder];
[[UIDevice currentDevice] playInputClick];
[dictationController startDictation];
}
- (void) dictateWithDuration: (CGFloat) numberOfSeconds
{
if (_inUse)
{
NSLog(#"Error: Dictation Helper already in use");
return;
}
_inUse = YES;
handled = NO;
secretTextField.text = #"";
[secretTextField becomeFirstResponder];
[[UIDevice currentDevice] playInputClick];
[dictationController startDictation];
[self performSelector:#selector(stopDictation) withObject:nil afterDelay:numberOfSeconds];
[self performSelector:#selector(fallback) withObject:nil afterDelay:numberOfSeconds + 1.0f];
}
- (void) dictateWithDuration: (CGFloat) duration completion:(DictationBlock) completionBlock
{
if (completionBlock) completion = completionBlock;
[self dictateWithDuration:duration];
}
- (void) stopDictation
{
[dictationController stopDictation];
}
- (void) stopDictationWithFallback
{
[self performSelector:#selector(stopDictation) withObject:nil afterDelay:0.0];
[self performSelector:#selector(fallback) withObject:nil afterDelay:1.0f];
}
#end
#undef MAKELIVE
It's been asked a dozen times but this error is a result of several different causes and I have no idea if my problem is relevant to any other causes.
I get these errors in console:
2014-03-17 16:15:41.190 [31659:70b] setting to solo ambient
2014-03-17 16:15:42.606 [31659:70b] backing dimensions: (640,960)
2014-03-17 16:15:42.835 [31659:70b] no other audio playing
2014-03-17 16:15:42.836 [31659:70b] no audio player..
2014-03-17 16:15:42.854 [31659:70b] Application windows are expected to have a root view controller at the end of application launch
(lldb)
I get Thread 1: breakpoint 1.1 message in this code:
#import <SemiSecret/SemiSecret.h>
#implementation SemiSecretFont
- (NSString *) description
{
return [NSString stringWithFormat:#"<SemiSecretFont: name:%#, size:%.1f>", NSStringFromClass([self class]), size];
}
+ (SemiSecretFont *)fontWithName:(NSString *)name
size:(CGFloat) size;
{
//dynamically search for a class with this name
**Class klass = NSClassFromString([NSString stringWithFormat:#"%#Font", name]); //error is on this line**
//NSLog(#"looking for font: %#", name);
// NSLog(#"klass: %#", klass);
SemiSecretFont * font = nil;
if (klass)
// font = [[[klass alloc] initWithSize:size] autorelease];
font = [[(SemiSecretFont *)[klass alloc] initWithSize:size] autorelease];
return font;
}
- (id) fontWithSize:(CGFloat)s
{
Class klass = [self class];
SemiSecretFont * f = nil;
//f = [[[klass alloc] initWithSize:s] autorelease];
f = [[(SemiSecretFont *)[klass alloc] initWithSize:size] autorelease];
return f;
}
//this is not meant to be instantiated directly!
- (id) initWithSize:(CGFloat)fontsize
{
if ((self = [super init])) {
size = fontsize;
font = nil;
}
return self;
}
In AppDelegate, I have:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//in canabalt, we never want linear filtering (not even on ipad)
[SemiSecretTexture setTextureFilteringMode:SSTextureFilteringNearest];
[application setStatusBarOrientation:UIInterfaceOrientationLandscapeRight
animated:NO];
game = [[Canabalt alloc] init];
//preload textures here, now that opengl stuff should be created
preloadTextureAtlases();
return YES;
}
- (void) applicationDidEnterBackground:(UIApplication *)application
{
[FlxG didEnterBackground];
}
- (void) applicationWillEnterForeground:(UIApplication *)application
{
[FlxG willEnterForeground];
}
- (void) applicationWillResignActive:(UIApplication *)application
{
[FlxG willResignActive];
}
- (void) applicationDidBecomeActive:(UIApplication *)application
{
[FlxG didBecomeActive];
}
- (void) applicationWillTerminate:(UIApplication *)application
{
}
- (void) dealloc
{
[game release];
[super dealloc];
}
#end
I am also trying to figure out how to make it so that audio player will be ignored. I want the game to be played normally.
In your case it is fairly simple.
In the application: didFinishLaunchingWithOptions:, the rootViewController property of the window is to be set; which does not seem to be happening in your code.
If you are doing it in storyboard / xib then you are not doing it right, as stated by the error Application windows are expected to have a root view controller at the end of application launch
You can refer the Apple Docs for more info on window and its rootViewController. To be precise, scroll down to the end of that page.
Given the following code example (iOS 7, Xcode 5):
/**
* SampleProvider Class
*/
typedef void(^RequestCallback)(UIViewController *result);
static NSString * const cControllerRequestNotification = #"controllerRequestNotification";
static NSString * const cRequestClassNameKey = #"className";
static NSString * const cRequestCallbackKey = #"callback";
#interface SampleProvider : NSObject
+ (void)requestControllerForClassName:(NSString *)className completion:(RequestCallback)callback;
#end
#interface SampleProvider ()
- (UIViewController *)controllerForClassName:(NSString *)className;
- (void)didReceiveControllerRequest:(NSNotification *)n;
#end
#implementation SampleProvider
#pragma mark - Overrides
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (id)init {
self = [super init];
if( self ) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didReceiveControllerRequest:) name:cControllerRequestNotification object:nil];
}
return self;
}
#pragma mark - Public API
+ (void)requestControllerForClassName:(NSString *)className completion:(RequestCallback)callback{
NSDictionary *requestInfo = #{ cRequestClassNameKey : className, cRequestCallbackKey : [callback copy] };
[[NSNotificationCenter defaultCenter] postNotificationName:cControllerRequestNotification object:requestInfo];
}
#pragma mark - Private API
- (UIViewController *)controllerForClassName:(NSString *)className {
UIViewController *result = nil;
Class controllerClass = NSClassFromString(className);
if( (nil != controllerClass) && ([controllerClass isSubclassOfClass:[UIViewController class]]) ) {
result = [[controllerClass alloc] init];
}
return result;
}
- (void)didReceiveControllerRequest:(NSNotification *)n {
NSDictionary *requestInfo = [n object];
NSString *className = requestInfo[cRequestClassNameKey];
RequestCallback callback = requestInfo[cRequestCallbackKey];
UIViewController *result = [self controllerForClassName:className];
if( nil != callback ) {
callback(result);
}
}
#end
/**
* SampleViewController Class
*/
#interface SampleViewController : UIViewController
#end
#implementation SampleViewController
#pragma mark - Overrides
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSString *className = #"ClassName";
[SampleProvider requestControllerForClassName:className completion:^(UIViewController *result) {
if( nil != result ) {
// Result is valid pointer, not a zombie.
[self.navigationController pushViewController:result animated:YES];
// Result is released, not nil.
} else {
NSLog(#"Unable to load controller with class name: %#", className);
}
}];
}
#end
Why would my UINavigationController fail to take ownership of the callback controller, received by SampleProvider's public class method, even after showing the view?
I'm seeing the following behavior:
The new controller class is properly allocated and returned via the callback method. Upon entering the callback the result parameter is pointing to valid memory.
The new controller is pushed to my UINavigationController's navigation stack.
The newly pushed controller's "viewDidLoad" method is called.
When inspecting the UINavigationController's "viewControllers" property, the newly pushed controller is referenced in the array.
The newly push controller is is deallocated while UINavigationController pushViewController:animated: is still executing.
The new controller is now a zombie.
Thank you for any assistance.
I don't have a clearcut answer because the answer may be in code you haven't posted -- the code you have posted looks valid apart from two observations (which could lead you to an answer):
Should that isKindOfClass be isSubclassOfClass? -isKindOfClass: is an
instance method on NSObject, not a class method.
Calling pushViewController: synchronously during viewDidLoad seems
dangerous. It's quite possible that the state of the view hierarchy
is not stable at that time. That push should happen in response to
some other discrete event, I'd think. Try making that push (or the
entire requestControllerForClassName:) asynchronous via
dispatch_async, as a test, and see if that solves your problem.