issue while accessing global func in app delegate - ios

I am new to swift and i created one swift file with name mySession and in that file i am storing login data like below
Store Login Data
func setLoginData (data:Data) {
let preferences = UserDefaults.standard
let Key_Login = "Login"
preferences.set(data, forKey: Key_Login)
preferences.synchronize()
}
and second func for use that stored data
Get Data
func getLoginData ()->Data {
let preferences = UserDefaults.standard
let Key_Login = "Login"
if preferences.object(forKey: Key_Login) == nil {
return data
} else {
return preferences.data(forKey: Key_Login)!
}
}
So now my question is that i want to use getLoginData func in my app delegate to check user is logged in or not so not able to under stand how to use that func in app delegate

You should encapsulate these methods inside a class and use an instance of that class inside AppDelegate
class MySession {
func setLogin(_ data: Data){...}
func getLoginData() -> Data {...}
}
At the call site, instantiate MySession and use your methods to do what's needed.
class AppDelegate {
//...
let sessionHandler = MySession()
sessionHandler.getLoginData()
}
Sidenote, make sure you're using proper Swift 4 naming conventions.

First Import the file name in AppDelegate
Import mySession
Then just call the method in your desired function.
setLoginData(data:<your Data>)
For example.If you want to use it in didFinishLaunchingWithOptions method, follow this
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DefaultNetworkManager.appsConfigSetup()
window = UIWindow(frame: UIScreen.main.bounds)
setupApplication()
setLoginData(data: <Your Data>)
return true
}

Related

How to track changes in currently visible ViewController as user navigating the screens in iOS?

I'm trying to replicate Firebase Analytics behaviour, which automatically fire screen events whenever ViewController screen get's changed with another.
Though I'm able to find currently visible ViewController using :
UIApplication.shared.windows.first?.rootViewController?.presentedViewController
But I need some way to get notified for any change in rootViewController. I tried to observe this rootViewController using KVO, but I don't get any callback. I found that KVO only works on NSObject with dynamic properties.
Is there any way I could receive callback for change in ViewController? Since this will be a library project, I couldn't make changes in main code to support the feature.
Following solution worked for me:-
import Foundation
import UIKit
public extension UIViewController {
#objc dynamic func _tracked_viewWillAppear(_ animated: Bool) {
UserActivityTracker.startTracking(viewController: self)
}
static func swizzle() {
//Make sure This isn't a subclass of UIViewController,
//So that It applies to all UIViewController childs
if self != UIViewController.self {
return
}
let _: () = {
let originalSelector =
#selector(UIViewController.viewWillAppear(_:))
let swizzledSelector =
#selector(UIViewController._tracked_viewWillAppear(_:))
let originalMethod =
class_getInstanceMethod(self, originalSelector)
let swizzledMethod =
class_getInstanceMethod(self, swizzledSelector)
method_exchangeImplementations(originalMethod!, swizzledMethod!);
}()
}
}
In above code _tracked_viewWillAppear() is my custom function which I want to call my implementation before actual implementation called.
Then in AppDeligate class, call UIViewController.swizzle() method, as follows:-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UIViewController.swizzle()
return true
}

Chartboost Delegate not working in swift

Desired : I want to do something when Delegates method call Observed :Delegates method not calling Ad's show on the screen successfully
Error
code:Chartboost.delegate=self
Error: Type 'Chartboost' has no member 'delegate'
AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Chartboost.start(withAppId: "4f21c409cd1cb2fb7000001b", appSignature: "92e2de2fd7070327bdeb54c15a5295309c6fcd2d", delegate: nil)
return true
}
ViewController Code
class ViewController: UIViewController,GADBannerViewDelegate, GADInterstitialDelegate,GADRewardBasedVideoAdDelegate,IMBannerDelegate, IMInterstitialDelegate ,ChartboostDelegate{
#IBAction func Vedio(_ sender: Any) {
Chartboost.showRewardedVideo(CBLocationMainMenu)
}
#IBAction func LoadFullAd(_ sender: Any) {
Chartboost.showInterstitial(CBLocationHomeScreen)
}
private func shouldDisplayRewardedVideo(_ location: CBLocation) -> Bool {
return true
}
private func shouldRequestInterstitial(_ location: CBLocation) -> Bool {
return true
}
}
A had to set the delegate as self with Chartboost.setDelegate(self)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Chartboost.start(withAppId: "4f21c409cd1cb2fb7000001b", appSignature: "92e2de2fd7070327bdeb54c15a5295309c6fcd2d", delegate:self as ChartboostDelegate)
Chartboost.setDelegate(self as ChartboostDelegate)
return true
}
After looking at how to properly convert Objective-C methods in Swift, I added the underscore (_), which changed the function to:
func shouldDisplayRewardedVideo(_ location: CBLocation) -> Bool
{
return true
}
func shouldRequestInterstitial(_ location: CBLocation) -> Bool {
return true
}
XCode then gave me a hint that I was close to the delegate method, but needed to change the type of location and I ended up with
func shouldDisplayRewardedVideo(_ location: String) -> Bool
{
return true
}
func shouldRequestInterstitial(_ location: String) -> Bool {
return true
}
If the delegate is set to nil, the class that calls the delegate's methods (in this case Chartboost) will not be able to make the delegate's method calls. You should set the delegate to the 'self' of the class where you have implemented the delegate methods expected by Chartboost.
In the example above, you could set the Chartboost delegate to the 'self' of the ViewController.
For example, inside of ViewController, you have already declared the 'ChartboostDelegate' in the class signature. When you want to turn on the Chartboost delegate methods, assign the ViewController's 'self' to the Chartboost delegate using something like:
Chartboost.delegate = self
In the case of Chartboost, it looks like the author made the delegate private, so it can be set in the ViewController using:
Chartboost.start(withAppId: "some uid", appSignature: "some other uid", delegate: self)
or, as later found out:
Chartboost.setDelegate(self)
(It can also be set in the AppDelegate class by locating the ViewController instance in the storyboard. Not a great fit in this case.)
If you're having problems generating the delegate method's call signature stubs (the method calls expected by the delegate), XCode will autogenerate them for you. Just click on the error message found next to your class declaration:
Type '<your class implementing the delegate methods>' does not conform to protocol '<the delegate protocol to implement>'
More detail about the error will appear. Click the 'Fix' button and XCode will autogenerate the method stubs for you.

Swift: User default values not being saved/ loaded when app is opened and closed

I have a global settings variable (SettingsVariable.settingOne) that is changed by the user during the apps runtime. I want to make the users change permanent, but at the moment every time the app is then reloaded it reverts to the original values e.g. user changes value to false, but then when the app is rerun the variables value changes back to the original value.
When the value is changed the following code is called (swift 2):
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "settingOne")
and when the app is closed:
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
NSUserDefaults.standardUserDefaults().setBool(SettingsVariables.settingOne, forKey: "settingOne")
}
then once the app is opened again:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
NSUserDefaults.standardUserDefaults().boolForKey("settingOne")
return true
}
But the value of settingOne keeps reverting back to the default value of 'true' set originally within the application.
SettingsVariables.settingOne is contained within a struct:
import UIKit
struct SettingsVariables {
static var settingOne = true
}
Now Apple documentation suggests to use CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication) instead of calling UserDefaults.standard.synchronize() which is marked as a deprecated method.
You need to give synchronize to save NSUserDefaults.
So your code must be
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
NSUserDefaults.standardUserDefaults().setBool(SettingsVariables.settingOne, forKey: "settingOne")
NSUserDefaults.standardUserDefaults().synchronize()
}
You have to synchronize the values NSUserDefaults.standardUserDefaults().synchronize()
this what I use for getting element from NSUserDefaults , UserDataKey is just Enum
func userDataForKey(key: UserDataKey) -> String? {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let value: AnyObject = userDefaults.objectForKey(key.rawValue) {
return value as? String
} else {
return nil
}
}
and this for saving
func setUserData(value: AnyObject?, key: UserDataKey) -> () {
let userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(value, forKey: key.rawValue)
}

Where best to call updateApplicationContext using Watch Connectivity?

Several of the good blog posts detailing Watch Connectivity (http://www.kristinathai.com/watchos-2-tutorial-using-application-context-to-transfer-data-watch-connectivity-2/ and http://natashatherobot.com/watchconnectivity-application-context/) use simple app examples that send data to the watch when you tap on UI on the iPhone.
My app simply lists the data from my iPhone app, so I don't need to send data immediately, I just wanted to send it when the app loads or enters background...to this end I've made the updateApplicationContext in didFinishLaunching and didEnterBackground...however my dataSource delegate in my watch interface controllers are very spotting at getting triggered...particularly the glance only loads on the simulator and never on device. Is there a better time and place to push the info?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
WatchSessionManager.sharedManager.startSession()
do {
try WatchSessionManager.sharedManager.updateApplicationContext(["peopleDict" : peopleDict])
} catch {
print(error)
}
return true
}
func applicationDidEnterBackground(application: UIApplication) {
do {
try WatchSessionManager.sharedManager.updateApplicationContext(["peopleDict" : peopleDict])
} catch {
print(error)
}
}
below is my WatchSessionManager I used to call activiateSession in my extensionDelegate's appliciationDidFinishLaunching
import WatchConnectivity
protocol DataSourceChangedDelegate {
func dataSourceDidUpdate(dataSource: DataSource)
}
class WatchSessionManager: NSObject, WCSessionDelegate {
static let sharedManager = WatchSessionManager()
private override init() {
super.init()
}
private var dataSourceChangedDelegates = [DataSourceChangedDelegate]()
private let session: WCSession = WCSession.defaultSession()
func startSession() {
session.delegate = self
session.activateSession()
}
func addDataSourceChangedDelegate<T where T: DataSourceChangedDelegate, T: Equatable>(delegate: T) {
dataSourceChangedDelegates.append(delegate)
}
func removeDataSourceChangedDelegate<T where T: DataSourceChangedDelegate, T: Equatable>(delegate: T) {
for (index, indexDelegate) in dataSourceChangedDelegates.enumerate() {
if let indexDelegate = indexDelegate as? T where indexDelegate == delegate {
dataSourceChangedDelegates.removeAtIndex(index)
break
}
}
}
}
// MARK: Application Context
// use when your app needs only the latest information
// if the data was not sent, it will be replaced
extension WatchSessionManager {
// Receiver
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.dataSourceChangedDelegates.forEach { $0.dataSourceDidUpdate(DataSource(data: applicationContext))}
}
}
}
As updateApplicationContext only stores the newest application context you can update it whenever you like. The watch will only get the newest data. There is no queue with old contexts.
On the watch side the most secure location to activate the session and set the WCSessionDelegate is in the ExtensionDelegate init method:
class ExtensionDelegate: NSObject, WKExtensionDelegate {
override init() {
super.init()
WatchSessionManager.sharedManager.startSession()
}
...
}
Your Glance does not update because when the Glance is shown, applicationDidFinishLaunching is not being called (because the watch app is not launched when only the Glance is launched)

openURL in APPDelegate conversion error NSString -> String (Swift & iOS8)

I'm currently developing an iOS application that integrates Facebook and I'm having a bit of a problem while investigating this with Swift (with ObjC I have no problems).
The thing is, this is the method that gets executed in the appDelegate when coming from another APP (in this case FB in a WebBrowser):
func application(
application: UIApplication,
openURL url: NSURL,
sourceApplication: NSString,
annotation: AnyObject)
-> Bool {
let appString : String = sourceApplication as String // Try to convert format => EXCEPTION
let appString : String = String(sourceApplication) // 'SSS' Suggestion: EXCEPTION
println(sourceApplication) // Try to print the value => EXCEPTION
return FBAppCall.handleOpenURL(url, sourceApplication:sourceApplication,
withSession:session) // With Parse => EXCEPTION
}
And inside that method I'm having real trouble with the 'sourceApplication' parameter. I try to use it, I get an exception. I try to convert it, another exception...can't even log its value because it crashes when accessing its value. Changing the parameter type in the functions signature to String neither worked.
This is the error I'm getting:
EXEC_BAD_ACCESS
And I've been able to track down until I could read this that it's definitely a valuable hint:
ObjectiveC.NSString.__conversion (ObjectiveC.NSString)() -> Swift.String
Could it be an iOS8 bug?
Any of you has had this problem and/or knows how to solve it?
You have made two mistakes:
The function declaration from the app Delegate is func application(application: UIApplication!, openURL url: NSURL!, sourceApplication: String!, annotation: AnyObject!) -> Bool : sourceApplication is an optional String value not NSString.
Since sourceApplication is an optional it may return nil value (In your case returning nil) . Type casting nil to String is not safe , therefore it is crashing.
Solutions :
No type casting is required in your case Since returned value is String type
Use optional form type cast operator as? to safely type cast i.e
if let appString = sourceApplication {
println(appString as? String)
}
This is working for me (with FacebookSDK):
func application(application: UIApplication, openURL url: NSURL, sourceApplication: NSString?, annotation: AnyObject) -> Bool {
var wasHandled:Bool = FBAppCall.handleOpenURL(url, sourceApplication: sourceApplication)
return wasHandled
}
I don't get this in the playground. Could be a iOS 8 bug as you suggest
But for the sake of trying, can you try
let appString : String = String(sourceApplication)
For FB Messenger here's what I did to get some better handling in my AppDelegate. Most of the ideas were taken straight from the FB IOS documentation and ported to Swift.
Why did I feel like I should write an additional reply? I've got some experience with Swift, but felt like I wasted enough time trying to get the right set of code to do what I wanted with FB Messenger. Hopefully the raw code is useful to someone, just to consolidate a lot of bits and pieces and save some time.
NOTE: This does not include all the AppDelegate lifecycle methods you'll want/need, but hopefully it's a head start
#UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, FBSDKMessengerURLHandlerDelegate {
var window: UIWindow?
var messengerUrlHandler: FBSDKMessengerURLHandler?
var cancelContext : FBSDKMessengerURLHandlerCancelContext?
var composerContext : FBSDKMessengerURLHandlerOpenFromComposerContext?
var replyContext: FBSDKMessengerURLHandlerReplyContext?
// Facebook Messenger
enum MessengerShareMode : Int {
case MessengerShareModeCancel
case MessengerShareModeSend
case MessengerShareModeComposer
case MessengerShareModeReply
}
// shareMode holds state indicating which flow the user is in.
// Return the corresponding FBSDKMessengerContext based on that state.
var shareMode : MessengerShareMode?
/*
* Initialize the FB messenger handler and set self as the delegate.
*/
func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
YARAppearance.setAppearance()
let rootController = TabBarController()
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = rootController
self.window!.makeKeyAndVisible()
// Facebook messenger handling
self.messengerUrlHandler = FBSDKMessengerURLHandler()
if (self.messengerUrlHandler != nil) {
self.messengerUrlHandler!.delegate = self
}
return true
}
/*
* Handle the cancel context flow.
*/
func messengerURLHandler(messengerURLHandler: FBSDKMessengerURLHandler!,
didHandleCancelWithContext context: FBSDKMessengerURLHandlerCancelContext!) {
self.cancelContext = context
self.shareMode = .MessengerShareModeCancel
}
/*
* When people enter your app through the composer in Messenger,
* this delegate function will be called.
*/
func messengerURLHandler(messengerURLHandler: FBSDKMessengerURLHandler!,
didHandleOpenFromComposerWithContext context: FBSDKMessengerURLHandlerOpenFromComposerContext!) {
self.composerContext = context
self.shareMode = .MessengerShareModeComposer
}
/*
* When people enter your app through the "Reply" button on content
* this delegate function will be called.
*/
func messengerURLHandler(messengerURLHandler: FBSDKMessengerURLHandler!,
didHandleReplyWithContext context: FBSDKMessengerURLHandlerReplyContext!) {
self.replyContext = context
self.shareMode = .MessengerShareModeReply
}
/*
* Handle URL calls from external applications, particularly Messenger
*/
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
let wasHandled:Bool = self.messengerUrlHandler!.openURL(url, sourceApplication: sourceApplication)
return wasHandled
}
/*
* A way to access the context objects elsewhere
*/
func getContextForShareMode() -> FBSDKMessengerContext? {
// shareMode holds state indicating which flow the user is in.
// Return the corresponding FBSDKMessengerContext based on that state.
if (shareMode == .MessengerShareModeSend) {
// Force a send flow by returning a broadcast context.
return FBSDKMessengerBroadcastContext()
} else if (shareMode == .MessengerShareModeComposer) {
// Force the composer flow by returning the composer context.
return self.composerContext!
} else if (shareMode == .MessengerShareModeReply) {
// Force the reply flow by returning the reply context.
return self.replyContext!
}
return nil
}
}

Resources