My code for NSUserDefaults is not working - ios

I am very new to swift. And need your help!
I want that, when the user logs in for the second time , the app should directly take it to the next view controller named CoreView. It should not ask for details, but I don't know why its not working. And it's asking for details everytime the app is launched. Please check the below code. I am not getting any sort of error too. Unless and until the app is killed or logged out, the user should be able to log in directly .
func pref_write()
{
// To write the data to NSUserDefaults
let prefs = NSUserDefaults.standardUserDefaults() // make a reference
print("OTP:\(OTP)")
// Adding values. Creating objects in prefs
prefs.setObject(OTP, forKey: "OTP")
print("check_OTP:\(check_OTP)")
prefs.setObject(U_ID, forKey: "U_ID")
print("Check_U_ID:\(check_U_ID)")
prefs.synchronize()
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
}
And in the viewDidLoad function:
override func viewDidLoad()
{
super.viewDidLoad()
//Read the data
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
pref_write()
let prefs = NSUserDefaults.standardUserDefaults()
check_OTP = prefs.objectForKey("OTP")!
check_U_ID = prefs.objectForKey("U_ID")!
prefs.objectForKey("U_ID")
print("prefs:\(prefs)")
prefs.synchronize()
}
Thanks!

Create a class as
class User_Details : NSObject
{
var user_id : String?
var user_otp : String?
var otp_verified : Bool?
init(u_id:String, otp:String?, verified:Bool)
{
super.init()
self.user_id = u_id
self.otp_verified = verified
self.user_otp = otp
}
}
In AppDelegate,
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
navController = self.window?.rootViewController as? UINavigationController
if self.checkIfUserLoggedIn()
{
let user_details = NSUserDefaults.standardUserDefaults().objectForKey("user_details") as! User_Details
self.moveToNextScreen(user_details)
}
return true
}
//AppDelegate Class or in the class which is globally accessible
func pref_write_user(user_details : User_Details)
{
let prefs = NSUserDefaults.standardUserDefaults()
prefs.setObject(user_details, forKey: "user_details")
prefs.setBool(true, forKey: "is_user_login")
//After saving the OTP for current user, check for otp verified, move to OTP Screen
self.moveToNextScreen(user_details)
}
func moveToNextScreen(user_details : User_Details)
{
if user_details.otp_verified == false
{
// Move to OTP screen
let viewController = self.navController?.storyboard?.instantiateViewControllerWithIdentifier("otpScreen")
self.navController?.pushViewController(viewController!, animated: false)
}
else // Move to Home Screen
{
let viewController = self.navController?.storyboard?.instantiateViewControllerWithIdentifier("homeScreen")
self.navController?.pushViewController(viewController!, animated: false)
}
}
func logoutUser()
{
let prefs = NSUserDefaults.standardUserDefaults()
prefs.setObject(nil, forKey: "user_details")
prefs.setBool(false, forKey: "is_user_login")
}
func checkIfUserLoggedIn() -> Bool
{
let prefs = NSUserDefaults.standardUserDefaults()
if prefs.boolForKey("is_user_login")
{
if let _ = prefs.objectForKey("user_details")
{
return true
}
else
{
//User details not found for some reason, so setting the inital values and return false
self.logoutUser()
}
}
return false
}
Login Class :
Call the API for login by providing the basic credential, get the user_id and user_otp, save them to NSUserDefaults
func requestLoginToServer()
{
//Perform basic server action
....
//In Success Block write this
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
// pass the values as return by the server
let user_details = User_Details(u_id: "123", otp: "1234", verified: false)
appDel.pref_write_user(user_details)
appDel.moveToNextScreen(user_details)
}

Please try this way. I just rearranged your code.
First it will check the login credentials with in the didload method of initial view controller. If it not there it will call the method pref_write() . Please make sure that the values used in pref_write() method are not nil
override func viewDidLoad()
{
super.viewDidLoad()
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
// You can give conditions for your need like if(prefs.valueForKey("U_ID") != nil))
// It will check the user defaults whether you already login
if(prefs.valueForKey("OTP") != nil) {
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
}
else{
pref_write()
}
}
// Make sure the Values are not nil
func pref_write()
{
// To write the data to NSUserDefaults
let prefs = NSUserDefaults.standardUserDefaults() // make a reference
print("OTP:\(OTP)")
// Adding values. Creating objects in prefs
prefs.setObject(OTP, forKey: "OTP")
print("check_OTP:\(check_OTP)")
prefs.setObject(U_ID, forKey: "U_ID")
print("Check_U_ID:\(check_U_ID)")
prefs.synchronize()
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
}
Hope its working...

Related

Swift - app crashes after setting UserDefaults

I am trying to implement a "always logged in" function in to my app. The problem is that if I restart my app, it crashes. This is what I did:
set Userdefault:
#objc func loginButtonTapped(_ sender: Any) {
let email = self.emailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = self.passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
// start button animation
loginButton.startAnimation()
let qualityOfServiceClass = DispatchQoS.QoSClass.background
let backgorundQueue = DispatchQueue.global(qos: qualityOfServiceClass)
backgorundQueue.async {
// check if account details correct
Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
if error != nil {
DispatchQueue.main.async {
// error -> stop animation
self.loginButton.stopAnimation(animationStyle: .shake, revertAfterDelay: 0) {
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
}
}
}else {
// correct acount details -> login
DispatchQueue.main.async {
UserDefaults.standard.set(true, forKey: "isLoggedIn")
UserDefaults.standard.synchronize()
// transition to home ViewController
self.transitionToHome()
}
}
}
}
}
checking UserDefault:
class MainNavigationControllerViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
if isLoggedIn() {
let homeController = MainViewController()
viewControllers = [homeController]
}
}
fileprivate func isLoggedIn() -> Bool {
return UserDefaults.standard.bool(forKey: "isLoggedIn")
}
}
The user logs in via Firebase and all the data is stored in Cloud Firestore.
Error
cell.customWishlistTapCallback = {
let heroID = "wishlistImageIDX\(indexPath)"
cell.theView.heroID = heroID
let addButtonHeroID = "addWishButtonID"
self.addButton.heroID = addButtonHeroID
// track selected index
self.currentWishListIDX = indexPath.item
let vc = self.storyboard?.instantiateViewController(withIdentifier: "WishlistVC") as? WishlistViewController
vc?.wishList = self.dataSourceArray[self.currentWishListIDX]
// pass drop down options
vc?.theDropDownOptions = self.dropDownButton.dropView.dropDownOptions
vc?.theDropDownImageOptions = self.dropDownButton.dropView.dropDownListImages
// pass current wishlist index
vc?.currentWishListIDX = self.currentWishListIDX
// pass the data array
vc?.dataSourceArray = self.dataSourceArray
// set Hero ID for transition
vc?.wishlistImage.heroID = heroID
vc?.addWishButton.heroID = addButtonHeroID
// allow MainVC to recieve updated datasource array
vc?.dismissWishDelegate = self
vc?.theTableView.tableView.reloadData()
self.present(vc!, animated: true, completion: nil)
}
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
at line:
let vc = self.storyboard?.instantiateViewController(withIdentifier: "WishlistVC") as! WishlistViewController
I guess it is not as easy as I thought. Does anyone know why the app crashes and how I can solve this? :)
You are creating your MainViewController instance using a simple initialiser (MainViewController()) rather than instantiating it from the storyboard. As a result, any #IBOutlet properties will be nil since it is the the storyboard process that allocates those object instances and assigns them to the properties.
You need to add an identifier to your main view controller scene (if it doesn't already have one) and use that to instantiate the view controller instance. E.g. assuming the scene identifier is "MainScene" you would have something like:
override func viewDidLoad() {
super.viewDidLoad()
if isLoggedIn() {
let homeController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("MainScene")
viewControllers = [homeController]
}
}
The crash in your updated question indicates that either the scene with the identifier WishlistVC doesn't have its class set to WishlistViewController or it isn't found so the forced downcast crashes.

How to add an Avatar image in GetStream iOS Activity Feed component?

My config: XCode 10.3, Swift 5, MacOS Catalina v10.15
I followed the native iOS Activity Feed demo (https://getstream.io/ios-activity-feed/tutorial/?language=python) to successfully add an activity feed to my XCode project.
How do I add an avatar image for each user? Here is what I have tried so far:
I uploaded an avatar image to my backend storage, obtained the corresponding URL, and used a json object to create a new user using my backend server like so:
{
"id" : "cqtGMiITVSOLE589PJaRt",
"data" : {
"name" : "User4",
"avatarURL" : "https:\/\/firebasestorage.googleapis.com\/v0\/b\/champXXXXX.appspot.com\/o\/profileImage%2FcqtGMiITVSOLXXXXXXXX"
}
}
Verified that user was created successfully, but the FlatFeedPresenter view controller shows up with a blank avatar image even though activities in the feed show up correctly. How can I use the user's data.avatarURL property to populate the avatar image correctly?
Here is the StreamActivity ViewController class behind the Main storyboard.
import UIKit
import GetStream
import GetStreamActivityFeed
class StreamActivityViewController: FlatFeedViewController<GetStreamActivityFeed.Activity> {
let textToolBar = TextToolBar.make()
override func viewDidLoad() {
if let feedId = FeedId(feedSlug: "timeline") {
let timelineFlatFeed = Client.shared.flatFeed(feedId)
presenter = FlatFeedPresenter<GetStreamActivityFeed.Activity>(flatFeed: timelineFlatFeed, reactionTypes: [.likes, .comments])
}
super.viewDidLoad()
setupTextToolBar()
subscribeForUpdates()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let detailViewController = DetailViewController<GetStreamActivityFeed.Activity>()
detailViewController.activityPresenter = activityPresenter(in: indexPath.section)
detailViewController.sections = [.activity, .comments]
present(UINavigationController(rootViewController: detailViewController), animated: true)
}
func setupTextToolBar() {
textToolBar.addToSuperview(view, placeholderText: "Share something...")
// Enable image picker
textToolBar.enableImagePicking(with: self)
// Enable URL unfurling
textToolBar.linksDetectorEnabled = true
textToolBar.sendButton.addTarget(self,
action: #selector(save(_:)),
for: .touchUpInside)
textToolBar.updatePlaceholder()
}
#objc func save(_ sender: UIButton) {
// Hide the keyboard.
view.endEditing(true)
if textToolBar.isValidContent, let presenter = presenter {
// print("Message validated!")
textToolBar.addActivity(to: presenter.flatFeed) { result in
// print("From textToolBar: \(result)")
}
}
}
}
UPDATE:
I updated the AppDelegate as suggested in the answer below, but avatar image still does not update even though rest of the feed does load properly. Set a breakpoint at the following line and found that avatarURL property of createdUser is nil even though streamUser.avatarURL is set correctly.
print("createdUser: \(createdUser)")
Updated AppDelegate code (had to comment out
initialViewController?.reloadData() to address a "Value of type 'UIViewController' has no member 'reloadData'" error -- not sure whether is contributing to the avatar issue.)
import UIKit
import Firebase
import GetStream
import GetStreamActivityFeed
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance()?.clientID = FirebaseApp.app()?.options.clientID
Database.database().isPersistenceEnabled = true
configureInitialRootViewController(for: window)
return true
}
}
extension AppDelegate {
func configureInitialRootViewController(for window: UIWindow?) {
let defaults = UserDefaults.standard
let initialViewController: UIViewController
if let _ = Auth.auth().currentUser, let userData = defaults.object(forKey: Constants.UserDefaults.currentUser) as? Data, let user = try? JSONDecoder().decode(AppUser.self, from: userData) {
initialViewController = UIStoryboard.initialViewController(for: .main)
AppUser.setCurrent(user)
Client.config = .init(apiKey: Constants.Stream.apiKey, appId: Constants.Stream.appId, token: AppUser.current.userToken)
let streamUser = GetStreamActivityFeed.User(name: user.name, id: user.id)
let avatarURL = URL(string: user.profileImageURL)
streamUser.avatarURL = avatarURL
Client.shared.create(user: streamUser) { [weak initialViewController] result in
if let createdUser = try? result.get() {
print("createdUser: \(createdUser)")
// Refresh from here your view controller.
// Reload data in your timeline feed:
// initialViewController?.reloadData()
}
}
} else {
initialViewController = UIStoryboard.initialViewController(for: .login)
}
window?.rootViewController = initialViewController
window?.makeKeyAndVisible()
}
}
The recommended approach is to ensure the user exists on Stream's side in AppDelegate.
extension AppDelegate {
func configureInitialRootViewController(for window: UIWindow?) {
let defaults = UserDefaults.standard
let initialViewController: UIViewController
if let _ = Auth.auth().currentUser, let userData = defaults.object(forKey: Constants.UserDefaults.currentUser) as? Data, let user = try? JSONDecoder().decode(AppUser.self, from: userData) {
initialViewController = UIStoryboard.initialViewController(for: .main)
AppUser.setCurrent(user)
Client.config = .init(apiKey: Constants.Stream.apiKey,
appId: Constants.Stream.appId,
token: AppUser.current.userToken,
logsEnabled: true)
let streamUser = GetStreamActivityFeed.User(name: user.name, id: user.id)
streamUser.avatarURL = user.avatarURL
// ensures that the user exists on Stream (if not it will create it)
Client.shared.create(user: streamUser) { [weak initialViewController] result in
if let createdUser = try? result.get() {
Client.shared.currentUser = createdUser
// Refresh from here your view controller.
// Reload data in your timeline feed:
// flatFeedViewController?.reloadData()
}
}
} else {
initialViewController = UIStoryboard.initialViewController(for: .login)
}
window?.rootViewController = initialViewController
window?.makeKeyAndVisible()
}
}

Why does the second view controller didn´t appear after loading the entry view, but by clicking on a button, he does?

Why does the second view controller didn´t appear after loading the entry view, but by clicking on a button, he does?
Here is my code:
#IBAction func Pressed(_ sender: UIButton) {
var firstName = FirstName.text;
var lastName = LastName.text;
let defaults = UserDefaults.standard;
defaults.set(firstName, forKey: "FN");
defaults.set(lastName, forKey: "LN");
loadNextView()
}
override func viewDidLoad() {
super.viewDidLoad()
self.FirstName.text = "TESTAUSGABE";
let defaults = UserDefaults.standard;
var fn = defaults.object(forKey: "FN")
var ln = defaults.object(forKey: "LN")
if(fn==nil){
self.FirstName.placeholder="Nicht angegeben"
}
else {
self.FirstName.text = fn as? String
loadNextView()
}
if(ln==nil){
self.LastName.placeholder="Nicht angegeben"
}
else{
self.LastName.text = ln as? String
self.Pressed(self.SaveButton);
}
}
func loadNextView(){
let storyboard: UIStoryboard = UIStoryboard(name:"Main", bundle:nil);
let newViewControler = storyboard.instantiateViewController(withIdentifier: "View2") as UIViewController
self.present(newViewControler, animated: true, completion: nil)
}
I want to check the preferences and if they are already set then it should appear automatically another view.
Or is there another method I didn't know?
Prsenting VCs doesn't work inside viewDidLoad as the view hierarchy is not yet complete , You can try to implement viewDidAppear or viewWillAppear
var runOnce = true
//
override func viewDidAppear(_ animated:Bool){
super.viewDidAppear(animated)
if runOnce {
loadNextView() // here read default value
runOnce = false
}
}

Add initial note

I am looking at adding an inital note to the note page within my app. this is so that when people click to the notes part there will be some detail on how to use it rather than just a big empty screen. I have no idea where to implement this though. Could you please help, below is the page where it talks about the dictionaries.
import UIKit
import MessageUI
class DetailViewController: UIViewController, MFMailComposeViewControllerDelegate, UITextViewDelegate {
#IBOutlet weak var tView: UITextView!
#IBAction func BarButton(sender: UIBarButtonItem) {
let textToShare = ""
if let myWebsite = NSURL(string: "")
{
let objectsToShare = [textToShare, myWebsite]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
}
OpenMail()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tView.text = (allNotes[currentNoteIndex] as Note).note
tView.becomeFirstResponder()
// Set controller as swipe gesture recogniser, to allow keyboard dismissal for text box
var swipe: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "dismissKeyboard")
swipe.direction = UISwipeGestureRecognizerDirection.Down
self.view.addGestureRecognizer(swipe)
self.tView.delegate = self
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if tView.text == "" {
allNotes.removeAtIndex(currentNoteIndex)
}
else {
(allNotes[currentNoteIndex] as Note).note = tView.text
}
Note.saveNotes()
noteTable?.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func configuredMailComposeViewController() -> MFMailComposeViewController {
// Open mail controller on screen and prepare with preset values.
let mailComposerVC = MFMailComposeViewController()
var MessageText: String!
MessageText = tView.text
mailComposerVC.mailComposeDelegate = self
mailComposerVC.setToRecipients([""])
mailComposerVC.setSubject("")
mailComposerVC.setMessageBody(MessageText, isHTML: false)
return mailComposerVC
}
func showSendMailErrorAlert() {
// Alert user to email error
let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail. Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
sendMailErrorAlert.show()
}
// MARK: MFMailComposeViewControllerDelegate Method
func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
func OpenMail() {
//Function to open mail composer on screen
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
func dismissKeyboard() {
// Dismiss keyboard for textfield
self.tView.resignFirstResponder()
}
}
note.swift
import UIKit
var allNotes:[Note] = []
var currentNoteIndex:NSInteger = -1
var noteTable:UITableView?
let KAllNotes:String = "notes"
class Note: NSObject {
var date:String
var note:String
override init() {
date = NSDate().description
note = ""
}
func dictionary() -> NSDictionary {
return ["note":note, "date":date]
}
class func saveNotes() {
var aDictionaries:[NSDictionary] = []
for (var i:NSInteger = 0; i < allNotes.count; i++) {
aDictionaries.append(allNotes[i].dictionary())
}
NSUserDefaults.standardUserDefaults().setObject(aDictionaries, forKey: KAllNotes)
// aDictionaries.writeToFile(filePath(), atomically: true)
}
class func loadnotes() {
allNotes.removeAll(keepCapacity: true)
var defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var savedData:[NSDictionary]? = defaults.objectForKey(KAllNotes) as? [NSDictionary]
// var savedData:NSArray? = NSArray(contentsOfFile: filePath())
if let data:[NSDictionary] = savedData {
for (var i:NSInteger = 0; i < data.count; i++) {
var n:Note = Note()
n.setValuesForKeysWithDictionary(data[i] as [NSObject : AnyObject])
allNotes.append(n)
}
}
}
class func filePath() -> String {
var d:[String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if let directories:[String] = d {
var docsDirectory:String = directories[0]
var path:String = docsDirectory.stringByAppendingPathComponent("\(KAllNotes).notes")
return path;
}
return ""
}
}
Thanks in advance
Sam
Add an NSUserDefault boolean that stores whether or not the initial note should be shown, e.g. that the app has been launched for the first time. Then load an initial note accordingly. When a note is added or the initial note is deleted, then change the boolean accordingly so the initial note doesn't show up next time.
You could also initialize your database with an initial note. Not clear from your code how the notes are saved, but this approach would probably rely on the NSUserDefault approach above, except it could be done in the AppDelegate or something.
example:
let InitialSetupComplete = "InitialSetupComplete" // Note: I would define this at the top of a file
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.boolForKey(InitialSetupComplete) {
// Show initial note
}
// Later on when the note is deleted, or modified (or immediately after initial note loaded into the database, see below)
defaults.setBool(true, forKey: InitialSetupComplete)
Would be easier/cleaner just to initialize your database with the initial note in the app delegate (e.g. call within applicationDidFinishLaunching), so your view controller doesn't have to figure this out. Similar code, except you would use setBool right away after the initial note has been saved to the database. I don't know anything about your database from the question, so can't really provide a more detailed example than this. Hope this helps.

Detect first launch of iOS app [duplicate]

This question already has answers here:
How to detect Apps first launch in iOS?
(9 answers)
Closed 5 years ago.
I am trying to find a way in Swift to detect the first launch.
Typically you would write a value to NSUserDefaults to indicate that an app has launched before.
let launchedBefore = NSUserDefaults.standardUserDefaults().boolForKey("launchedBefore")
if launchedBefore {
print("Not first launch.")
}
else {
print("First launch, setting NSUserDefault.")
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "launchedBefore")
}
UPDATE - Swift 3
let launchedBefore = UserDefaults.standard.bool(forKey: "launchedBefore")
if launchedBefore {
print("Not first launch.")
} else {
print("First launch, setting UserDefault.")
UserDefaults.standard.set(true, forKey: "launchedBefore")
}
I kinda always need this so I put it in a category
General Usage:
let isFirstLaunch = UserDefaults.isFirstLaunch()
Usage inside your AppDelegate
// use this if you need to refer to it later
var optionallyStoreTheFirstLaunchFlag = false
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
optionallyStoreTheFirstLaunchFlag = UserDefaults.isFirstLaunch()
// .. do whatever else
return true
}
Some important considerations:
This flag is only set on the first invocation. If you want to know
about the first launch multiple times throughout different screens,
set a variable you can later refer to, as per the
'optionallyStoreTheFirstLaunchFlag' example.
In iOS, apps are usually never shut down. Apps are backgrounded, foregrounded,
state-saved to flash memory, but they are only relaunched if they're
force shutdown by the user (rare) or the user restarts their phone.
So if you store it in a variable, it could potentially stick around
for a long time. Manually reset it once you're done with showing all
the tutorial screens and whatnot.
Swift 4
Put the following in UserDefaults+isFirstLaunch.swift
extension UserDefaults {
// check for is first launch - only true on first invocation after app install, false on all further invocations
// Note: Store this value in AppDelegate if you have multiple places where you are checking for this flag
static func isFirstLaunch() -> Bool {
let hasBeenLaunchedBeforeFlag = "hasBeenLaunchedBeforeFlag"
let isFirstLaunch = !UserDefaults.standard.bool(forKey: hasBeenLaunchedBeforeFlag)
if (isFirstLaunch) {
UserDefaults.standard.set(true, forKey: hasBeenLaunchedBeforeFlag)
UserDefaults.standard.synchronize()
}
return isFirstLaunch
}
}
Swift 3
extension UserDefaults {
var hasLaunchBefore: Bool {
get {
return self.bool(forKey: #function)
}
set {
self.set(newValue, forKey: #function)
}
}
}
Swift 5 (Property wrappers)
UserDefaultWrapper:
#propertyWrapper
struct UserDefaultWrapper<T> {
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
UserDefaultsStore:
struct UserDefaultsStore {
#UserDefaultWrapper("has_launch_before", defaultValue: false)
static var hasLaunchBefore: Bool
}
Usage:
UserDefaultsStore.hasLaunchBefore = false
I refined a bit user n13 answer in order to
have the method always return true during the whole first launch
be an extension to UIApplication
Just use it wherever you want as UIApplication.isFirstLaunch() and be sure to reach it at least once during first execution.
Swift 3
import UIKit
private var firstLaunch : Bool = false
extension UIApplication {
static func isFirstLaunch() -> Bool {
let firstLaunchFlag = "isFirstLaunchFlag"
let isFirstLaunch = UserDefaults.standard.string(forKey: firstLaunchFlag) == nil
if (isFirstLaunch) {
firstLaunch = isFirstLaunch
UserDefaults.standard.set("false", forKey: firstLaunchFlag)
UserDefaults.standard.synchronize()
}
return firstLaunch || isFirstLaunch
}
}
Swift 2
import UIKit
private var firstLaunch : Bool = false
extension UIApplication {
static func isFirstLaunch() -> Bool {
let firstLaunchFlag = "isFirstLaunchFlag"
let isFirstLaunch = NSUserDefaults.standardUserDefaults().stringForKey(firstLaunchFlag) == nil
if (isFirstLaunch) {
firstLaunch = isFirstLaunch
NSUserDefaults.standardUserDefaults().setObject("false", forKey: firstLaunchFlag)
NSUserDefaults.standardUserDefaults().synchronize()
}
return firstLaunch || isFirstLaunch
}
}
Use NSUserDefaults. Register a BOOL key with a value of false. Read the key at launch time; if it's false, set it to true and show the welcome. Next launch, it will be true, you won't show the welcome, problem solved.
In case of Swift In applicationdidFinishLaunchingWithOptions in AppDelegate Add:
if UserDefaults.standard.bool(forKey: "isFirstLaunch") {
UserDefaults.standard.set(true, forKey: "isFirstLaunch")
UserDefaults.standard.synchronize()
}
And Use this wherever you want to.
let isFirstLaunch = UserDefaults.standard.value(forKey: "isFirstLaunch") as? Bool
if isFirstLaunch {
//It's the initial launch of application.
}
else {
// not initial launch
}
I did an edit of n13's post. This code seems cleaner to me. You can call as a class or instance function.
Also, according to apple docs you shouldn't call synchronize() since it's called periodically, unless the app is about to close. I have it called in the AppDelegate in applicationDidEnterBackground(). https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/#//apple_ref/occ/instm/NSUserDefaults/synchronize
if NSUserDefaults().isFirstLaunchForUser("me") {
print("First launch")
} else {
print("Not first launch")
}
if NSUserDefaults.isFirstLaunch() {
print("First launch")
} else {
print("Not first launch")
}
extension NSUserDefaults {
static func isFirstLaunch() -> Bool {
let firstLaunchFlag = "FirstLaunchFlag"
if !standardUserDefaults().boolForKey(firstLaunchFlag) {
standardUserDefaults().setBool(true, forKey: firstLaunchFlag)
// standardUserDefaults().synchronize()
return true
}
return false
}
// For multi user login
func isFirstLaunchForUser(user: String) -> Bool {
if !boolForKey(user) {
setBool(true, forKey: user)
// synchronize()
return true
}
return false
}
}
you can use UserDefaults to store the times that App has opened
First:
AppDelegate.swift
let userDefaults = UserDefaults.standard
var currentTimesOfOpenApp:Int = 0
func saveTimesOfOpenApp() -> Void {
userDefaults.set(currentTimesOfOpenApp, forKey: "timesOfOpenApp")
}
func getCurrentTimesOfOpenApp() -> Int {
return userDefaults.integer(forKey: "timesOfOpenApp") + 1
}
each time the App is open, you should add the property currentTimesOfOpenApp, so modify this property in the function func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.currentTimesOfOpenApp = getCurrentTimesOfOpenApp()
return true
}
in addition, when the app is closed, you should save the currentTimesOfOpenApp, that is important!
func applicationWillTerminate(_ application: UIApplication) {
saveTimesOfOpenApp()
self.saveContext()
}
Second:
if you want to show the times, you can get this value form UserDefaults to display it on the Label.
ViewController.swift
let delegate = UIApplication.shared.delegate as! AppDelegate
let times = delegate.currentTimesOfOpenApp
timesToOpenAppLabel.text = "\(times)"
the App is open every time, the currentTimesOfOpenApp will be increase. if you delete the App, this value will be reset as 1.
let applicationLaunchedOnce: Bool = {
let launchedOnce = NSUserDefaults.standardUserDefaults().boolForKey(UserDefaultsService.ApplicationLaunchedOnce)
if launchedOnce {
return launchedOnce
} else {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: UserDefaultsService.ApplicationLaunchedOnce)
NSUserDefaults.standardUserDefaults().synchronize()
return false
}
}()

Resources