2

I want the user to be able to know how many times they have visited each class. Then add together the totals from each page together to form a group sum. I want to print the total sum in the log file in each of the two view controllers. So just one string should be printed.

class oneV: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        UserDefaults.standard.set(true, forKey: "VC1")
    }
}

class twoV: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        UserDefaults.standard.set(true, forKey: "VC2")
    }
}
Rakesha Shastri
  • 11,053
  • 3
  • 37
  • 50

6 Answers6

3

If you mean visited each view controller, when you say visited each class. Then i'd recommend you do it viewDidAppear.

class YourViewController: UIViewController {

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        let key = String(describing: type(of: self))
        let count = UserDefaults.standard.value(forKey: key) as? Int ?? 0
        UserDefaults.standard.set(value + 1, forKey: key)
    }

}

To make it simpler, you could use an extension on UIViewController.

extension UIViewController {

    func updateVisitCount() {
        let key = String(describing: type(of: self))
        let count = UserDefaults.standard.value(forKey: key) as? Int ?? 0
        UserDefaults.standard.set(count + 1, forKey: key)
    }

}

Or, if you need this for every view controller that you create, then you can create a base view controller which you would use everywhere instead of UIViewController.

class BaseViewController: UIViewController {

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        updateVisitCount()
    }

}
Rakesha Shastri
  • 11,053
  • 3
  • 37
  • 50
  • Second option (more simpler one ) is more promising, because if a custom view controller override the `ViewDidAppear` method, then extension's overridden method won't be called. – Ratul Sharker Nov 27 '18 at 06:39
  • @RatulSharker Yes. The extension has to be used consciously like the first snipper of code. I'm just making it shorter by not making the the OP write it for every view controller. The last one is the smallest where the OP doesn't even need to call the method, but instead just create a view controller using the subclass. – Rakesha Shastri Nov 27 '18 at 06:41
  • OP has to call this extension method in each and every view controller has within the project. From objective-c world there was another option like method swizzling, It's like black magic, replacing the existing `UIViewController`'s `viewDidLoad` with count logic, and register another method with the original implementation of the `viewDidLoad`. To maintain the lifecycle consistent using the call the newly registered method within the new `viewDidLoad` implementation. It's a bit complicated, but OP don't have to manually insert any code within the existing viewControllers. – Ratul Sharker Nov 27 '18 at 06:47
2

The most automatic solution would be inject the accounting call in viewDidLoad without replacing the original viewDidLoad.

Here demo purpose i've created a sample Playground

import UIKit
import PlaygroundSupport

extension UIViewController {
    @objc dynamic func substitutedViewDidAppear() {
        print("This is injected code in view did appear")
        substitutedViewDidAppear() // it may look like recursive, but it isn't, actually it calls the original `viewDidAppear` method.
    }
    
    class func swizzle() {
        let originalMethod = class_getInstanceMethod(UIViewController.self, #selector(viewDidAppear(_:)))
        let substitutedMethod = class_getInstanceMethod(UIViewController.self, #selector(substitutedViewDidAppear))
        
        if  let originalMethod = originalMethod,
            let substitutedMethod = substitutedMethod {
            print("swizzled")
            method_exchangeImplementations(originalMethod, substitutedMethod)
        } else {
            print("not swizzled")
        }
    }
}

class MyViewController : UIViewController {
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let label = UILabel()
        label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
        label.text = "Hello World!"
        label.textColor = .black
        
        view.addSubview(label)
        self.view = view
        print("view loaded")
    }
}

// Swizzle
UIViewController.swizzle() // call this in @top of didFinishLaunchingWithOptions

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

Output:

swizzled

view loaded

This is injected code in view did appear

Now in the substitutedViewDidAppear upper portion inject your counting code as @Rakesha Shastri Suggested, call the updateVisitCount method inside of substitutedViewDidAppear & place the UIViewController.swizzle() in applicationDidFinishLaunchingWithOptions before creating the root window.

Community
  • 1
  • 1
Ratul Sharker
  • 7,484
  • 4
  • 35
  • 44
0

Create a static variable. A static variable is a type of class, not object therefore throughout all objects a variable maybe maintained. I think this example may better explain how this works. Click here

vi_ral
  • 369
  • 4
  • 19
  • You would lose the information once the app is closed. – Rakesha Shastri Nov 27 '18 at 05:48
  • Well yes you would loose that information when the application is closed, but this is a very common problem. In order to persist that a database layer could be used. Thanks. – vi_ral Nov 27 '18 at 06:25
  • This is a good read. https://stackoverflow.com/questions/16407594/when-should-i-use-the-various-storage-mechanisms-in-ios – Rakesha Shastri Nov 27 '18 at 06:28
  • Interesting NSUserDefaults seems like a good way to store info related to the app. Upvote my post if you think its a good solution :) – vi_ral Nov 27 '18 at 06:30
  • It isn't for this use case. Sorry. Which is what i was trying to convey in my first comment. – Rakesha Shastri Nov 27 '18 at 06:31
0

In ViewDidLoad method call this function :

func updateVisitingCounter() {

    var counter = UserDefaults.standard.integer(forKey: "firstPageCounter")
    counter += 1
    UserDefaults.standard.set(counter, forKey: "firstPageCounter")

}
amin
  • 313
  • 1
  • 10
0

You may set declare variables at project scope "outside of classes"

    var vc1Count = 0
    class oneV: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            vc1Count = vc1Count+1
        }
    }

    var vc2Count = 0
    class twoV: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            vc2Count = vc2Count+1
        }
    }

you can also declare these variables at a common place.

Abu Ul Hassan
  • 1,340
  • 11
  • 28
-1

As per your requirements its kind of Analytics on app usage. You can implement in 2 ways

  1. By storing data with screen visit in local DB and show it on Analysis Page or on summery page.

Sample code for storing Screen details in DB:

==> Create your Entity for Screen capture. ScreenVisit.

==> Store Data with screen name.

let entity = NSEntityDescription.entity(forEntityName: "ScreenVisit", in: context)
let newVisit = NSManagedObject(entity: entity!, insertInto: context)

newVisit.setValue("HomeScreen", forKey: "screenname")
newVisit.setValue("1", forKey: "visited")

do {          
   try context.save()       
  } catch {       
   print("Failed saving")
}

==> Fetch data where you required.

    let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ScreenVisit")
    //request.predicate = NSPredicate(format: <Your Filter Logic>)
    request.returnsObjectsAsFaults = false
    do {
        let result = try context.fetch(request)
        for data in result as! [NSManagedObject] {
           print(data.value(forKey: "screenname") as! String)
           print(data.value(forKey: "visited") as! String)
      }

    } catch {

        print("Failed")
    }
  1. You can use any 3rd party library like Google analytics, Crashlytics for tracking your user actions.

Ref Links :

Firebase iOS analytics

Crashlytics

but as per my experience 2nd way is more convenient and powerful.

All depends on your requirements.

Hope this will helps you to get your user action captured.

CodeChanger
  • 7,953
  • 5
  • 49
  • 80