Define a section of code in a class that's configured for two targets to run only in one of the targets

80 views Asked by At

I'm working on a custom keyboard project. In this project I have two targets (the App and the Keyboard targets). Now I build a class that's responsible for handling network data for both of the targets:

class FeedDataManager: URLManagerdelegate, ModelManagerDelegate {
//Class Variables....

func initForKeyboard() {
    self.mModelManager = ModelManager(aContext: self.managedObjectContext())
    self.mModelManager.delegate = self
    self.mModelManager.mDelegateHashString = hashString(self)

    self.mFeedsArray = Array<News>()
    mModelManager.getFeeds(&self.mFeedsArray)
    self.mURLManager = UrlManager()
    self.mURLManager.delegate = self
}

func initForApp() {
    self.mModelManager = ModelManager(aContext: self.managedObjectContext())
    self.mModelManager.delegate = self
    self.mModelManager.mDelegateHashString = hashString(self)

    let uuid: String? = UserDefaultsManager.sharedInstance.getObjectForKey("UUID") as? String
    self.mURLManager = UrlManager()
    self.mURLManager.delegate = self
    mURLManager.doGetLanguagesRequest()

    if uuid == nil {
        mURLManager.doRegistrationRequest()
    }
}

//MARK: - News Related Methods
func getNews() {
    self.mFeedsArray.removeAll(keepCapacity: false)
    self.mModelManager.getFeeds(&self.mFeedsArray)
    Logger.printLogToConsole(TAG, aMethodName: __FUNCTION__, aMessage: "Feeds Array Count: \(mFeedsArray.count)")
    self.mURLManager.doGetSourcesRequest()
}

func handleRequestResults(aActionCode: Int, aData: NSData? = nil) {
    if let val = aData {
        if aActionCode == KiboConstants.NetworkActionCodes.GET_NEWS_DONE {
            let currentTime: Double = NSDate().timeIntervalSince1970
            UserDefaultsManager.sharedInstance.setDouble(currentTime, aKey: KiboConstants.CommonStrings.USER_DEF_LAST_UPDATE_TIME_STAMP)
            var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(aData!, options: .MutableContainers, error: nil)
            var nextUpdate: AnyObject? = json.valueForKey(KiboConstants.CommonStrings.USER_DEF_NEXT_UPDATE)
            self.setTimeWithNextUpdateValue(nextUpdate)
            self.mModelManager.updateNews(json, aCallerHashString: hashString(self))

        } else if aActionCode == KiboConstants.NetworkActionCodes.GET_SOURCES_DONE {
            var jsonWrapped: AnyObject! = NSJSONSerialization.JSONObjectWithData(aData!, options: .MutableContainers, error: nil)
            if self.mModelManager == nil {
                self.mModelManager = ModelManager(aContext: self.managedObjectContext())
                self.mModelManager.delegate = self
                self.mModelManager.mDelegateHashString = hashString(self)
            }
            if let json: AnyObject = jsonWrapped {
                mModelManager.updateSources(json)
            }
        } else if aActionCode == KiboConstants.NetworkActionCodes.GET_LANGUAGES_DONE {
            #if APPLICATION
            var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(aData!, options: .MutableContainers, error: nil)
            if let val: AnyObject = json {
                let languages: AnyObject = json.objectForKey(KiboConstants.JsonKeys.JSON_KEY_DATA_LANGUAGES_LOWERCASE)!
                for item in languages as! Array<AnyObject> {
                    ApplicationLanguagesManager.sharedInstance.addLanguageToAvailableLanguagesArray(item)
                }
            }
            #endif
        }
    } else {
        self.setTimeWithNextUpdateValue(KiboConstants.UserDefaultsValues.DEFAULT_NEXT_NEWS_UPDATE_VAL)
    }
}
.....
}

If you look into the handleRequestResults method, you will see this section:

#if APPLICATION
var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(aData!, options: .MutableContainers, error: nil)
if let val: AnyObject = json {
    let languages: AnyObject = json.objectForKey(KiboConstants.JsonKeys.JSON_KEY_DATA_LANGUAGES_LOWERCASE)!
    for item in languages as! Array<AnyObject> {
        ApplicationLanguagesManager.sharedInstance.addLanguageToAvailableLanguagesArray(item)
    }
}
#endif

And in the Application target I have defined the following in the Preprocessor Macros section:

enter image description here

Now when I run the application in debug and use "po APPLICATION" at this point I get a relevant print, while in the keyboard extension I get the following error: error: <EXPR>:1:1: error: use of unresolved identifier 'APPLICATION'

Meaning the configuration is read correctly.

The problem/question is: In the application's run this code is still isn't executed. Does someone knows why? What am I missing here?

3

There are 3 answers

0
Emil Adz On BEST ANSWER

Actually, none of the answers here are right. I have achieved what I wanted using the following configuration:

plist file: plist file

And done the following in code:

#if APPLICATION
        var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(aData!, options: .MutableContainers, error: nil)
        if let val: AnyObject = json {
            let languages: AnyObject = json.objectForKey(KiboConstants.JsonKeys.JSON_KEY_DATA_LANGUAGES_LOWERCASE)!
            for item in languages as! Array<AnyObject> {
                ApplicationLanguagesManager.sharedInstance.addLanguageToAvailableLanguagesArray(item)
            }
        }
        #endif

The result is the this section of code is compiled only for the targets that have the APPLICATION flag.

A link that cleared it all up for me: Build Configuration Management with Swift

3
Rainer Schwarze On

My answer is not correct - thanks Emil Adz for letting me know.

In your code you use #if, but you should use #ifdef.

#if evaluates APPLICATION and if it is not defined in the keyboard extension, the error message should appear.

#ifdef checks, whether or not APPLICATION is defined. So you very probably want #ifdef APPLICATION instead of #if APPLICATION.

(It's been some time since I used preprocessor macros, but I hope that I remember things correctly...)

1
Andriy Gordiychuk On

Rainer's answer is correct.

However, you may also try to do this. In your Application target add the macro:

APPLICATION = 1

and in your keyboard extension add

APPLICATION = 0

With this you will be able to use your current code without any problems. And, as an additional benefit, your keyboard extension won't complain that it cannot find the definition of APPLICATION and will simply ignore the code.

Either approach works.