Enable blocked rows after in app purchase. Coding issue - swift

I have an app that blocks User access to a few rows of a view controller. This is done by checking if a variable of type bool is set to true or false.
var unlocked: bool = false
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
//blocking cells if they are not paid for.
if unlocked == false {
if ( indexPath.row >= 2 ) {
cell.userInteractionEnabled = false
cell.contentView.alpha = 0.5
}
else{
cell.userInteractionEnabled = true
cell.contentView.alpha = 1
}
}
return cell
}
This works perfectly. I then have an option for the user to purchase access to the remaining rows, and hence the remaining content of the app. Once the In-app-purchase has been purchased it will run the function "updateSections()". I know this function is called upon purchase as I have tested it.
I now want to allow the user access to the remaining rows in the table view from the "updatedSections()" function as they will have paid for it.
What i have tried is:
//function to unlock
func unlockSections() {
//This is the code for what happens once the device has bought the IAP. going to have to save what happens here in using nsuserdefaults to make sure it will work when the app opens and closes.
print("The IAP worked")
let unlocked = true
tableview.reloadData()
}
However this does not seem to work. I can't see where I am going wrong.

The problem is that this line:
let unlocked = true
is defining a new constant called unlocked which only exists in the scope of your unlockSections method. It is quite separate from the property called unlocked which is defined at the start of your class. To update the property instead of creating the new constant, just drop the "let":
unlocked = true
or if you want to be crystal clear (or you want to have both, but use the property in a specific case), use "self." to emphasise that you are intending to use the property:
self.unlocked = true

Related

How do I get UIConfigurationState without a reference to the cell?

Normally, when updating a cell's contentConfiguration for a particular cell's state you ask the cell for its contentConfiguration, then update it using updated(for:).
let content = cell.defaultContentConfiguration().updated(for: cell.configurationState)
However, in order to get this state you first need to have a reference to the cell. UIConfigurationState doesn't have an initializer. How can get the updated styling for a state without a reference to the cell?
For example, here I am trying to create a reusable configuration that adjusts itself for particular state
class Person {
let name: String
}
extension Person {
func listContentConfig(state: UICellConfigurationState) -> UIListContentConfiguration {
var content = UIListContentConfiguration.cell().updated(for: state)
content.text = self.name
return content
}
}
Then, during cell registration I can configure it with my reusable config.
extension UICollectionViewController {
func personCellRegistration(person: Person) -> UICollectionView.CellRegistration<UICollectionViewListCell, Person> {
return .init { cell, indexPath, person in
cell.contentConfiguration = person.listContentConfig(state: cell.configurationState)
}
}
}
That works fine, but what if I want to mix and match different properties for difference states? In order to actually get this state I need to first get the cell, update the state, then set it back. This is quite a few steps.
extension UICollectionViewController {
func personCellRegistration(person: Person) -> UICollectionView.CellRegistration<UICollectionViewListCell, Person> {
return .init { cell, indexPath, person in
// 1. Change the cell's state
cell.isUserInteractionEnabled = false
// 2. Grab my content config for the new state
let disabledConfig = person.listContentConfig(state: cell.configurationState)
// 3. Change the cell's state back
cell.isUserInteractionEnabled = true
// 4. Get the cell's default config
var defaultConfig = cell.defaultContentConfiguration()
// 5. Copy the pieces I want
defaultConfig.textProperties.color = disabledConfig.textProperties.color
}
}
}
What I'd like is to be able to do something like this:
extension Person {
func listContentConfig(state: UICellConfigurationState) -> UIListContentConfiguration {
let disabledState = UICellConfigurationState.disabled // no such property exists.
var content = UIListContentConfiguration.cell().updated(for: disabledState)
// customize...
}
}
I realize that I could pass in the cell itself to my reusable config, but this a) breaks encapsulation, b) defeats the purpose of configurations to be view agnostic, c) requires the same number of steps.
(FYI: The reason I am doing this is to allow the user to delete a cell that represents 'missing data'. The cell's style should appear disabled, but when setting isUserInteractionEnabled = false the delete accessory becomes unresponsive.)
Am I missing something?

Filter viewController

In my app I have a search bar where people can add free text and get the results the meet the criteria
func search(searchClinics: [Clinica], searchArr: [String]) -> [Clinica] {
var searchArr = searchArr
// base case - no more searches - return clinics found
if searchArr.count == 0 {
return searchClinics
}
// itterative case - search clinic with next search term and pass results to next search
let foundClinics = searchClinics.filter { item in
(item.name.lowercased() as AnyObject).contains(searchArr[0]) ||
item.specialty1.lowercased().contains(searchArr[0]) ||
item.specialty2.lowercased().contains(searchArr[0])
}
// remove completed search and call next search
searchArr.remove(at: 0)
return search(searchClinics: foundClinics, searchArr: searchArr)
}
I also have a flag to identify if the searchBar is being used
var searching = false // Checks if searchBar was used
In case searchBar was used, it returns the filtered array data otherwise returns the full list. For example
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
let totalClinics = clinicsSearch.count
if totalClinics == 0 {
return 1
} else {
return totalClinics
}
} else {
let totalClinics = clinics.count
if totalClinics == 0 {
return 1
} else {
return totalClinics
}
}
}
I'm now willing to add another viewController where the user would be able to define specific filters (like State, City, Specialty among others). Once the user clicks apply, it would go back to the previous viewController with filtering data.
I'm in doubt with the best approach to apply this filter. At first I though about doing something like:
User clicks a button in the navigationBar and opens "Search" viewController;
User inputs Data;
User clicks "Apply";
Call previous viewController;
I would add another status flag with status "True" that will be used in my tableView ViewController. If true, considers the list of clinics with filters applied. If not, show the full list of clinics;
I've a lot of searching in stackoverflow but I found a lot of filtering/searchbar stuff but none related to a separate search/filter viewController.
Would you recommend this approach or is there a better way to do it?
One of my concerns here if with step 4... if I call a segue, wouldn't I be stacking views and consuming more memory?
Thanks
In my app user click filter button present Controller open
let vc = storyboard?.instantiateViewController(withIdentifier: "searchFilter") as! SearchFilter
vc.modalPresentationStyle = .overCurrentContext
vc.SearchCompletion = {(model,flag) in
if(flag){
self.serachArr.removeAllObjects()
for i in 0..<model.count{
self.serachArr.add(ListModel(data: model[i] as! NSDictionary))
}
self.ListTable.reloadData()
}
}
self.present(vc, animated: true, completion: nil)
User click apply button and pass data previous Viewcontroller
self.SearchCompletion(dataArr,true)

how do I just show the profile Image next to the last message of an user in a chat

I am using JSQ Messages Controller to add a chat feature to my app, but I also managed to create one on my own with a collectionview. However Injust can‘t figure out how to show the profile picture just next to the last message of an user.
For example he writes 3 messages and no one else in the chat does write anything in between. Now I just want to show only next to the third message the profile picture. When I return the items (cells) I just can edit the item I am about to return (at indexPath.item) though. I can make sure that the message before (at indexPath.item - 1) has the same senderID. But I can‘t check if the cell at indexPath.item + 1) is from the same sender. Since I am not able to check the second one, I have no clue how to solve my problem. I hope you understood what I want to do.
First I would like to point you to the new project that is taking over JSQMessageViewController since JSQMessageViewController is deprecated, It is called MessageKit.
Make a function to determine if it is the last message in a set.
//Check if it is the last message in all your messages
//Check that the next message is not sent by the same person.
func isLastInSet(indexOfMessage: IndexPath) -> Bool {
if indexOfMessage.item == messages.count -1 {
return true
} else if {
return messages[indexOfMessage.item].senderID() != messages[indexOfMessage.item + 1].senderID()
}
}
Now just add a call to isLastInSet within the avatarImageDataForItemAt if it returns a false set the image to nil
func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
if let message = messages[indexPath.item]{
if isLastInSet(indexOfMessage: indexPath) {
return ImageData
}
}
return nil
}

Some TableView views are not reloading, as my TableView Delegate is not receiving tableView:viewFor:row: for every row

I have a TableView, and a TableViewController
The TableView's delegate and datasource are set to the TableViewController
So when the table goes to draw, it asks the delegate for the views, and this code runs:
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let result = tableView.make(withIdentifier: "flagView", owner: self) as? TableCellView
case "technicalFlag", "supplyFlag":
let button = result?.control as? NSButton {
button.state = value.boolValue ? NSOnState : NSOffState
}
}
Which updates the buttons properly:
Notice how there are 5 flags for Technical Risk
However, when I go to sort by Supply Risk, it calls tableView.reloadData()
But, not every view gets reloaded?
As a result, some of the flags stay black, even when they shouldn't be (notice 8+ black flags in Technical Risk, now)
It's seemingly random which flags are black (when I look at the data via another view via context menu, I can tell which flags should truly be black)
So ostensibly, it looks like a redrawing issue. I've tried everything, but can't get those views to redraw. Am I missing something?
I've confirmed that tableView:viewFor:row: is just not getting called as many times as I think it should
I guess, to sum things up, I thought that calling reloadData() would update every view at every row x column? How does the runtime decide which views get the update call?
Ah solved it, I had modified the original code because I didn't think it was relevant to show:
case "technicalRiskFlag", "supplyRisk":
if let value = node.value(forKeyPath: identifier) as? NSNumber,
let button = result?.control as? NSButton {
button.state = value.boolValue ? NSOnState : NSOffState
}
But turns out that value could be nil, which ended up not redrawing the flag. Not sure why the delegate method didn't get called still, but I changed the code to this:
if let button = result?.control as? NSButton {
if let value = node.value(forKeyPath: identifier) as? NSNumber {
button.state = value.boolValue ? NSOnState : NSOffState
} else {
button.state = NSOffState
}
}
and it works perfect now

First time to add text to textField [Swift]

I think it is something really easy but I am a noob and I can't figure out what to do, So I am trying to check if the TextField has nothing inside it, it would let the user write something in it, but if it has some text saved (I have saved the data using NSUserDefaults) in there when you quit the app it will remain the text that was typed in that TextField.
I had accomplished this before but something went wrong and stopped working.
Here is what I have done:
var notTheFirstTime = false
if notTheFirstTime == true{
let mail = NSUserDefaults.standardUserDefaults().objectForKey("usersMailID") as! String
textField.text = "\(mail)"
}
Here is the part where the text filed is saved and change the status of the "notTheFirstTime" to true
func textFieldShouldReturn(textField: UITextField) -> Bool {
performAction()
return true
}
func performAction() {
notTheFirstTime = false
NSUserDefaults.standardUserDefaults().setObject(textField.text!, forKey: "usersMailID")
NSUserDefaults.standardUserDefaults().synchronize()
print("Saved!!!!!!")
saveLabel.text! = "Saved!"
savedIcon.image = UIImage(named: "Saved")
}
I see two things in this code, After the text saved in to user default you should use
notTheFirstTime = true
but you set this as false in performAction()
Second one is you should use notTheFirstTime value also as a User defaults. When you close the app. This value also rest to the false( as you given on initial step of the application) So use it's value also as a user defaults.