How to reload UITableView from different controller - ios

I am attempting to make a to-do app with a controller for both "this week" and "next week." (Objects with a date in the current week will be shown in the thisWeek view controller, while objects with a date next week will be shown in the nextWeek view controller. However, sometimes objects will not appear in the table until the app restarts, and I'm not sure why this is happening or how to fix it.
My theory is that each table in the different controllers needs to be reloaded each time data is added/changed. I just don't know how to access the table from a different controller than the one I am in.
Attempt 1 I tried to use a singleton that looked like this:
class ToDoTables {
private init() {}
static let shared = ToDoTables()
lazy var thisTable: UITableView = {
let thisWeek = UITableView()
return thisWeek
}()
lazy var nextTable: UITableView = {
let nextWeek = UITableView()
return nextWeek
}()
}
then to be called like:
let thisTable = ToDoTables.shared.thisTable
However this did not work because I initially created the table using storyboard and just used an IBOutlet in the controller, and couldn't find way to keep the outlet and also use singleton. Next, I tried to remove the outlet and just use the code above. When I simulated the app, it crashed because the outlet couldn't be found or something like that (which was expected).
Attempt 2
I tried to access the other controller's table by creating an instance of the vc and accessing the table that way:
NextWeekController().nextTable.reloadData()
but got an error that said "Unexpectedly found nil while implicitly unwrapping an Optional value"
So I'm not sure if that is my tables need to be reloaded each time, or if it is something else.
Overview of non-functioning operations (an app restart is needed, or the controller/table needs loaded again):
something created in the nextWeek controller with a date in the current week will not appear
when an object in created in nextWeek controller, then date is changed from next week to this week
something created in the thisWeek controller with a date in the next week will not appear
when an object in created in thisWeek controller, then date is changed from this week to next week

My theory is that each table in the different controllers needs to be reloaded each time data is added/changed.
You are correct. For UITableView.reloadData(), the Apple Docs state to:
Call this method to reload all the data that is used to construct the
table
In other words, it is necessary to reload the table view's data with this method if the data is changed.
I just don't know how to access the table from a different controller
than the one I am in.
One solution is to create global references to all your view controllers through one shared instance.
// GlobalVCs.swift
class GlobalVCs {
static var shared = GlobalVCs()
var nameOfVC1!
var nameOfVC2!
var nameOfVC3!
// and so on
private init() {}
}
Then set the global vc references from each view controller's viewDidLoad() method.
Example:
// VC1.swift
override func viewDidLoad() {
GlobalVCs.shared.vc1 = self
}
Then you can access a view controller anywhere with GlobalVCs.shared.vc1, GlobalVCs.shared.vc2, etc.

Sorry, but need to say that this is wrong approach. We need to follow to MCV model where we have data model, view and controller. If you example for to-do model you could have:
import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift
struct ToDoItem: Codable, Identifiable {
static let collection = "todos"
#DocumentID var id: String?
var uid: String;
var done: Bool;
var timestamp: Timestamp;
var description: String
}
Then you need to have repository for this type of data, for example if we are using Firebase we can have
import Foundation
import Combine
import FirebaseFirestore
import FirebaseFirestoreSwift
class BaseRepository<T> {
#Published var items = Array<T>()
func add(_ item: T) { fatalError("This method must be overridden") }
func remove(_ item: T) { fatalError("This method must be overridden") }
func update(_ item: T) { fatalError("This method must be overridden") }
}
and then we have ToDoRepository (singleton object)
import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift
import Resolver
import CoreLocation
import Combine
class ToDoRepository: BaseRepository<ToDoItem>, ObservableObject {
var db = Firestore.firestore()
uid: String? {
didSet {
load()
}
}
private func load() {
db.collection(ToDoItem.collection).whereField("uid", isEqualTo: uid).order(by: "timestamp").addSnapshotListener {
( querySnapshot, error ) in
if let querySnapshot = querySnapshot {
self.items = querySnapshot.documents.compactMap { document -> ToDoItem? in
try? document.data(as: ToDoItem.self)
}
}
}
}
}
Now we need to register repository in AppDelegate+Injection.swift:
import Foundation
import Resolver
extension Resolver: ResolverRegistering {
public static func registerAllServices() {
// Services
register { ToDoRepository() }.scope(application)
}
}
After that we can use ToDoRepository in any controller :
import UIKit
import Combine
import Resolver
class MyController: UIViewController {
private var cancellables = Set<AnyCancellable>()
#LazyInjected var toDoRepository: ToDoRepository
// and listen and update any table view like this
override func viewDidLoad() {
super.viewDidLoad()
toDoRepository.$items
.receive(on: DispatchQueue.main)
.sink { [weak self] todos
self?.todos = todos
tableView.reloadData()
}
.store(in: &cancellables)
}
}

Related

Swift UI - Using UIVIewControllerRepresentable to update logs

The logic I'm trying to create for my logging in the app is:
A ScrollView with a frame to control the height and allow the user to see logs from actions in the app, the logs should be scrollable to scroll up on previous appended logs.
I've created a log view model which allows the log to be set and then appends to a log array and then get.
The logs are set through actions in callbacks from various view controllers and actions from the user.
currently I have the logs being retrieved in the UIViewControllerRepresentable - updateUIViewController method.
The code works for each callback and for the user actions, the problems are: 5a. It's not scrollable to go to the top of the log messages, 5b. The log messages keep showing on the screen as updateUIViewController is continuously being called.
I was trying to think of a way to empty the array after each action, but not sure the best way to go about this.
Code:
LogViewModel:
import UIKit
import SwiftUI
class LogViewModel: ObservableObject{
#Published var mTime: String = ""
#Published var id: String = "#"
#Published var mMessage: String = ""
private var fullLogMessages: [String] = [""]
func setTimeFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
return formatter
}
func setTheTime(date: Date){
self.mTime = setTimeFormatter().string(from: date)
}
func getTheTime() -> String{
return self.mTime
}
func setTheMessage(mMessage: String) {
ThreadUtil.runAsyncOnMainThread { [weak self] in
self?.mMessage = mMessage
}
}
func getTheMessage() -> String {
return self.mMessage
}
func getTheFullLogMessage() -> [String] {
let fullLog: String = getTheTime() + " - " + getTheGivenId() + " - " + getTheMessage()
self.fullLogMessages.append(fullLog)
return self.fullLogMessages
}
func setTheGivenId(id: String) {
ThreadUtil.runAsyncOnMainThread { [weak self] in
self?.id = id
}
}
func getTheGivenId() -> String {
return self.id
}
}
Controllers:
In each controller I've created a method like this to set the log messages:
func setTheLogMessages(message: String) {
self.logViewModel.setTheTime(date: date)
self.logViewModel.setTheMessage(mMessage: message)
}
In the view I have the UIViewControllerRepresentable:
struct MyScreenView_UI: UIViewControllerRepresentable{
#ObservedObject var viewModel: myScreenViewModel
#ObservedObject var logViewModel: LogViewModel
#Binding var fullLogMessage: [String]
func makeUIViewController(context: Context) -> some myViewController {
print(#function)
return myViewController(viewModel: viewModel, logViewModel: logViewModel)
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
print(#function)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
fullLogMessage = logViewModel.getTheFullLogMessage()
}
}
}
and the ScrollView for the UI:
ScrollView{
VStack(alignment: .leading, content: {
Text("Logs")
.font(.footnote).fontWeight(.medium)
ForEach($fullLogMessage, id: \.self) { $logMessage in
Text(logMessage)
.font(.custom("FONT_NAME", size: 12))
.disabled(true)
}
})
.frame(width: 400, height: 50, alignment: .leading)
}
You haven't provided a Minimal Reproducible Example but here is a simplified version of what seems to be what you are trying to do.
First, add a LogManager that can be created by ANY class or struct
struct LogManager{
var name: String
///Simplified post that takes in the String and uses the name as the source
func postMessage(message: String){
postMessage(message: .init(timestamp: Date(), message: message, source: name))
}
//MARK: Notification
///Sends a Notification with the provided message
func postMessage(message: Message){
NotificationCenter.default.post(name: .logManagerMessage, object: message)
}
///Adds an observer to the manager's notification
func observeMessageNotification(observer: Any, selector: Selector){
NotificationCenter.default.addObserver(observer, selector: selector, name: .logManagerMessage, object: nil)
}
}
Put at class or struct level the declaration for the manager
private let log = LogManager(name: "YourClassStructName")
Then call
log.postMessage(message: "your message here")
When you want to log a message.
In the ViewModel you would
subscribe to the notifications
maintain the array
Like below
class AppLogSampleViewModel: ObservableObject{
static let shared: AppLogSampleViewModel = .init()
private let manager = LogManager(name: "AppLogSampleViewModel")
#Published var messages: [Message] = []
private init(){
//Observe the manager's notification
manager.observeMessageNotification(observer: self, selector: #selector(postMessage(notification:)))
}
///Puts the messages received into the array
#objc
func postMessage(notification: Notification){
if notification.object is Message{
messages.append(notification.object as! Message)
}else{
messages.append(.init(timestamp: Date(), message: "Notification received did not have message", source: "AppLogSampleViewModel :: \(#function)"))
}
}
}
If your View won't be at the highest level you need to call.
let startListening: AppLogSampleViewModel = .shared
In the ContentView or AppDelegate so the ViewModel starts listening as quickly as possible. You won't necessarily use it for anything but it needs to be called as soon as you want it to start logging messages.
Only the View that shows the messages uses the instance of the ViewModel.
struct AppLogSampleView: View {
#StateObject var vm: AppLogSampleViewModel = .shared
//Create this variable anywhere in your app
private let log = LogManager(name: "AppLogSampleView")
var body: some View {
List{
Button("post", action: {
//Post like this anywhere in your app
log.postMessage(message: "random from button")
})
DisclosureGroup("log messages"){
ForEach(vm.messages, content: { message in
VStack{
Text(message.timestamp.description)
Text(message.message)
Text(message.source)
}
})
}
}
}
}
Here is the rest of the code you need to get this sample working.
struct AppLogSampleView_Previews: PreviewProvider {
static var previews: some View {
AppLogSampleView()
}
}
extension Notification.Name {
static let logManagerMessage = Notification.Name("logManagerMessage")
}
struct Message: Identifiable{
let id: UUID = .init()
var timestamp: Date
var message: String
var source: String
}
Only the one View that shows the messages needs access to the ViewModel and only one ViewModel subscribes to the notifications.
All the other classes and structs will just create an instance of the manager and call the method to post the messages.
There is no sharing/passing between the classes and structs everybody gets their own instance.
Your manager can have as many or as little methods as you want, mine usually mimics the Logger from osLog with log, info, debug and error methods.
The specific methods call the osLog and 3rd party Analytics Services corresponding methods.
Also, the error method sends a notification that a ViewModel at the top level receives and shows an Alert.
To get all this detail working it takes a lot more code but you have the pattern with the code above.
In your code, in the updateUIViewController you break the single source if truth rule by copying the messages and putting them in another source of truth, right here.
fullLogMessage = logViewModel.getTheFullLogMessage()
This is also done without a check to make sure that you don't go into an infinite loop. Anytime there is code in an update method you should check that the work actually needs to be done. Such as comparing that the new location doesn't already match the old location.
It seems like you made it very complicated. Let's do this with a simple approach.
1. Define what is a Log
Each log should be identifiable
Each log should represent it's creation date
Each log should have a message
struct Log: Equatable, Hashable {
let id = UUID()
let date = Date()
let message: String
}
2. Define where logs should be
Changes in the logs should be observable
Logs should be accessible in any view
import Combine
class LogManager: ObservableObject {
#Published var logs = [Log]()
}
Note: An EnvironmentObject is a good choice for such an object!
3. Define how to show logs
import SwiftUI
extension Log: Identifiable { }
struct ContentView: View {
#EnvironmentObject private var logManager: LogManager
var body: some View {
List(logManager.logs) { log in
HStack {
Text(log.message)
Text(log.date.ISO8601Format()) // Or any format you like
}
}
}
}
This UI is just a simple sample and could be replaced by any other UI
Note: ScrollToTop is automatically enabled in the List
Note: You may want to use a singleton or injected logger because of the nature of the logger
Note: Don't forget to create and pass in the environment object

Using Arrays in Singleton Class in iOS

PetInfo.class
class PetInfo {
static let shared: PetInfo = PetInfo()
lazy var petArray: [PetInfo] = []
var PetID:Int
var PetName:String
...
init(){ .. }
}
ViewController.swift
class ViewController: UIViewController {
var PetArray = PetInfo.shared.petArray
override func viewDidLoad() {
super.viewDidLoad()
let pet = PetInfo()
pet.PetName = "Jack"
PetArray.append(pet). **Create Object and gave a name**
print(PetArray[0].PetName) //works!
}
}
secondViewController.swift
class secondViewController: UIViewController {
var PetArray = PetInfo.shared.petArray
override func viewDidLoad() {
super.viewDidLoad()
let label: UILabel = {
let label = UILabel()
...
label.text = PetArray[0].PetName **tried to print**
return label
}()
view.addSubview(label)
}
}
I want to share PetArray array in all of the view controllers.(It's more than two.)
It put data in the first VC, but doesn't work in the Second VC.
How can I share this array using a Singleton pattern?
Except for the array, It works perfect.(like String.. PetID, PetName.. )
Array in swift is implemented as Struct, which means Array is a value type and not a reference type. Value types in Swift uses copy on write (COW) mechanism to handle the changes to their values.
So in your ViewController when you assigned
var PetArray = PetInfo.shared.petArray
your PetArray was still pointing to the same array in your PetInfo.shared instance (I mean same copy of array in memory) . But as soon as you modified the value of PetArray by using
PetArray.append(pet)
COW kicks in and it creates a new copy of petArray in memory and now your PetArray variable in your ViewController and PetInfo.shared.petArray are no longer pointing to same instances instead they are pointing to two different copies of array in memory.
So all the changes you did by using PetArray.append(pet) is obviously not reflected when you access PetInfo.shared.petArray in secondViewController
What can I do?
remove PetArray.append(pet) and instead use PetInfo.shared.petArray.append(pet)
What are the other issues in my code?
Issue 1:
Never use Pascal casing for variable name var PetArray = PetInfo.shared.petArray instead use camel casing var petArray = PetInfo.shared.petArray
Issue 2:
class PetInfo {
static let shared: PetInfo = PetInfo()
lazy var petArray: [PetInfo] = []
var PetID:Int
var PetName:String
...
init(){ .. }
}
This implementation will not ensure that there exists only one instance of PetInfo exists in memory (I mean it cant ensure pure singleton pattern), though you provide access to instance of PetInfo using a static variable named shared there is nothing which stops user from creating multiple instances of PetInfo simply by calling PetInfo() as you did in let pet = PetInfo()
rather use private init(){ .. } to prevent others from further creating instances of PetInfo
Issue 3:
You are holding an array of PetInfo inside an instance of PetInfo which is kind of messed up pattern, am not really sure as to what are you trying to accomplish here, if this is really what you wanna do, then probably you can ignore point two (creating private init) for now :)
I think the best solution to use Combine and Resolver frameworks. Works perfectly in my case with shared arrays.
In your case it could be
import Combine
import Resolver // need to add pod 'Resolver' to Podfile and install it first
// Data Model
struct PetInfo: Codable {
var PetID:Int
var PetName:String
...
}
// Repository to read manage data (read/write/search)
class PetRepository: ObservableObject {
#Published var petArray = Array<PetInfo>()
override init() {
super.init()
load()
}
private func load() {
// load pets info from server
}
}
Need to add AppDelegate+Injection.swift that will contain repository registration:
import Foundation
import Resolver
extension Resolver: ResolverRegistering {
public static func registerAllServices() {
// Services
register { PetRepository() }.scope(application)
}
}
Then use it in any controllers
import UIKit
import Combine
import Resolver
class ViewController: UIViewController {
#LazyInjected var petRepository: PetRepository
private var cancellables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
petRepository.$petArray
.receive(on: DispatchQueue.main)
.debounce(for: 0.8, scheduler: RunLoop.main)
.sink { [weak self] petInfos in
// set UI here
}
.store(in: &cancellables)
}
}
If you want PetInfo to be a singleton, make its initializer private:
class PetInfo {
static let shared: PetInfo = PetInfo()
lazy var petArray: [PetInfo] = []
var PetID:Int
var PetName:String
...
private init(){ .. } // !!
}
This way, any attempt to create new instances (like you do in your first ViewController) will fail, and will remind you to always use PetInfo.shared to access the singleton.

Keep reference on view/data model after View update

Consider we have a RootView and a DetailView. DetailView has it's own BindableObject, let's call it DetailViewModel and we have scenario:
RootView may be updated by some kind of global event e.g. missed
internet connection or by it's own data/view model
When RootView handling event it's
content is updated and this is causes new struct of DetailView to
be created
If DetailViewModel is created by DetailView on init,
there would be another reference of DetailViewModel and it's state (e.g. selected object) will be missed
How can we avoid this situation?
Store all ViewModels as EnvironmentObjects that is basically a singleton pool. This approach is causes to store unneeded objects in memory when they are not used
Pass throw all ViewModels from RootView to it's children and to children of child (has cons as above + painfull dependencies)
Store View independent DataObjects (aka workers) as EnvironmentObjects. In that case where do we store view dependent states that corresponds to Model? If we store it in View it will end up in situation where we cross-changing #States what is forbidden by SwiftUI
Better approach?
Sorry me for not providing any code. This question is on architecture concept of Swift UI where we trying to combine declarative structs and reference objects with data.
For now I don't see da way to keep references that corresponds to appropriate view only and don't keep them in memory/environment forever in their current states.
Update:
Lets add some code to see whats happening if VM is created by it's View
import SwiftUI
import Combine
let trigger = Timer.publish(every: 2.0, on: .main, in: .default)
struct ContentView: View {
#State var state: Date = Date()
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: ContentDetailView(), label: {
Text("Navigation push")
.padding()
.background(Color.orange)
})
Text("\(state)")
.padding()
.background(Color.green)
ContentDetailView()
}
}
.onAppear {
_ = trigger.connect()
}
.onReceive(trigger) { (date) in
self.state = date
}
}
}
struct ContentDetailView: View {
#ObservedObject var viewModel = ContentDetailViewModel()
#State var once = false
var body: some View {
let vmdesc = "View model uuid:\n\(viewModel.uuid)"
print("State of once: \(once)")
print(vmdesc)
return Text(vmdesc)
.multilineTextAlignment(.center)
.padding()
.background(Color.blue)
.onAppear {
self.once = true
}
}
}
class ContentDetailViewModel: ObservableObject, Identifiable {
let uuid = UUID()
}
Update 2:
It seems that if we store ObservableObject as #State in view (not as ObservedObject) View keeps reference on VM
#State var viewModel = ContentDetailViewModel()
Any negative effects? Can we use it like this?
Update 3:
It seems that if ViewModel kept in View's #State:
and ViewModel is retained by closure with strong reference - deinit will never be executed -> memory leak
and ViewModel is retained by closure with weak reference - deinit invokes every time on view update, all subs will be reseted, but properties will be the same
Mehhh...
Update 4:
This approach also allows you to keep strong references in bindings closures
import Foundation
import Combine
import SwiftUI
/**
static func instanceInView() -> UIViewController {
let vm = ContentViewModel()
let vc = UIHostingController(rootView: ContentView(viewModel: vm))
vm.bind(uiViewController: vc)
return vc
}
*/
public protocol ViewModelProtocol: class {
static func instanceInView() -> UIViewController
var bindings: Set<AnyCancellable> { get set }
func onAppear()
func onDisappear()
}
extension ViewModelProtocol {
func bind(uiViewController: UIViewController) {
uiViewController.publisher(for: \.parent)
.sink(receiveValue: { [weak self] (parent) in
if parent == nil {
self?.bindings.cancel()
}
})
.store(in: &bindings)
}
}
struct ModelView<ViewModel: ViewModelProtocol>: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<ModelView>) -> UIViewController {
return ViewModel.instanceInView()
}
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<ModelView>) {
//
}
}
struct RootView: View {
var body: some View {
ModelView<ParkingViewModel>()
.edgesIgnoringSafeArea(.vertical)
}
}
Apple says that we should use ObservableObject for the data that lives outside of SwiftUI. It means you have to manage your data source yourself.
It looks like a single state container fits best for SwiftUI architecture.
typealias Reducer<State, Action> = (inout State, Action) -> Void
final class Store<State, Action>: ObservableObject {
#Published private(set) var state: State
private let reducer: Reducer<State, Action>
init(initialState: State, reducer: #escaping Reducer<State, Action>) {
self.state = initialState
self.reducer = reducer
}
func send(_ action: Action) {
reducer(&state, action)
}
}
You can pass the instance of the store into the environment of your SwiftUI app and it will be available in all views and will store your app state without data losses.
I wrote a blog post about this approach, take a look at it for more information
https://swiftwithmajid.com/2019/09/18/redux-like-state-container-in-swiftui/

Swift WatchKit Tables Found nil while unwrapping optional

I've seen a bunch of threads reporting this error, however, after trying all solutions, I still have the same problem. Running with the debugger I can see that the .setNumberOfRows method works, as it assigns the appropriate number of rows to my table.
import WatchKit
import Foundation
class CountInterfaceController: WKInterfaceController {
//MARK: Properties
#IBOutlet var dataTable: WKInterfaceTable!
var counters = [Counter]()
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
if let savedCounters = loadCounters(){
counters+=savedCounters
}else{
loadSampleCounters()
}
dataTable.setNumberOfRows(counters.count, withRowType: "CountRowController")
for(index, counter) in counters.enumerated(){
let row = dataTable.rowController(at: index) as! CountRowController
row.countNameLabel.setText(counter.name)
row.countCounterLabel.setText("\(counter.count)")
}
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
private func loadSampleCounters(){
guard let counter1 = Counter(name:"Pots of coffee", count: 20) else {
fatalError("Unable to instantiate counter 1")
}
guard let counter2 = Counter(name:"Tea kettles", count: 5) else {
fatalError("Unable to instantiate counter 2")
}
counters += [counter1, counter2]
}
I have created a Table Row Class and added it in the Identity field of the Table Row Controller as you can see in the picture below. The class contains the outlets for the two labels in the table row.
Table Row Controller with class
I have tried populating my table with random data from a simple array of strings but that doesn't work either(same error). I honestly don't see what I'm missing here... The only thing that I can think of is that something's changed in Swift 4 as most of the answers and info that I've found were related to Swift 3.
error

What is best practice for global variables and functions in Swift?

I coding an app with several (15-25 different swigft files one for each view.
Some variables and functions I will use in every viewcontroller.
What would be best practice to enable code reusage?
For instance I need to communicate with a server in which the first request is for an access token, this request I imagine could be a global function setting a global variable (access token). And then using it for the more specific requests.
I started placing a lot of global constants in appdelegate file, in a Struct is there a problem with this?
LibraryAPI.swift
import UIKit
import CoreData
class LibraryAPI: NSObject
{
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
private var loginD: LoginDetails
private var isOnline: Bool
class var sharedInstance: LibraryAPI
{
struct Singleton
{
static let instance = LibraryAPI()
}
return Singleton.instance
}
override init()
{
super.init()
}
func getIsOnline() -> Bool
{
return isOnline
}
func setIsOnline(onlineStatus: Bool)
{
isOnline = onlineStatus
}
func getLoginDetails() -> LoginDetails
{
return loginD
}
func setLoginDetails(logindet: LoginDetails)
{
loginD = logindet
}
// Execute the fetch request, and cast the results to an array of objects
if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [LoginDetails] {
setLoginDetails(fetchResults[0])
}
}
You should avoid using global variables.
Depending on what you have / what you need to do, either you can :
Create a class and make an instance on your first call. Then, you can pass the object through your views (prepareForSegue). But that can still be repetitive to implement everytime ;
Create a singleton class that will be instantiate only once and accessible from everywhere (singleton are considered as a bad practice by some);
Use the NSUserDefaults to store String ;
Save your data somehow (CoreData, ...).
You can do like this
User.swift
import Foundation
import UIKit
class User: NSObject {
var name: String = ""
func getName() -> String{
name = "Nurdin"
return name
}
}
ViewController.swift
import Foundation
import UIKit
let instanceOfUser = User()
println(instanceOfUser.getName()) // return Nurdin

Resources