Terms and Conditions View Controller for iOS - ios

I was trying to make a simple Terms and Conditions view controller for my app. I was figuring a ScrollView with a Text Field.
What I am running into is displaying a really long formatted text string with multiple line feeds and quoted words and phrases.
NSString #termsAndConditions = #"Terms and Conditions ("Terms") ...
...
..."
How have others handled this? Since terms can change, how would one program this to be easily updated?
A code example would be really appreciated.

There is no one right way to ever do anything. What I have done in the past is to have the client or copywriter supply a word document. A word document is great for some one maintaining the content because it is easy for a non programmer. It also allows someone to easily specify some simple formatting for their terms of service content. Ie. bullet list, headers, links etc.
Then I would use an online doc2html converter to create an HTML file. (There are plenty of these online, a quick google can find one) Then I display the HTML file in a web view with in the terms of service or privacy policy view controller. If the app is using a specific font I just use a text editor to string replace the font declarations. Any extra styling requirements can be specified in a separate css file. For example, one that I just did required a specific colour for links to fit the theme of the app.
When it needs updating I can usually just do the edits in HTML if their are a few or regenerate it from a new Doc file.
Here is some example code of the view controller:
class TermsViewController: UIViewController {
#IBOutlet weak var termsWebView: UIWebView!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
loadTermsHTML()
}
func loadTermsHTML() {
//Load the HTML file from resources
guard let path = NSBundle.mainBundle().
pathForResource("terms", ofType: "html") else {
return
}
let url = NSURL(fileURLWithPath: path)
if let data = NSData(contentsOfURL: url) {
termsWebView.loadHTMLString(NSString(data: data,
encoding: NSUTF8StringEncoding) as! String, baseURL: nil)
}
}
}

Related

Get the name of file(s) within last directory and the full directory path using Swift

I am trying to obtain the name of a file (JSON format but saved without an extension) within the last directory of a given path. Each file is saved with its own unique subpath inside the app's data container.
I also need to get the full path of the file, including the filename.
From what I've read, I believe it is better to use URLs to do this rather than using string paths.
I have tried the following code:
do {
let enumerator = FileManager.default.enumerator(at: filePath, includingPropertiesForKeys: nil)
while let element = enumerator?.nextObject() as? URL {
var nexObject = element.lastPathComponent
print(nextObject)
}
} catch let error {
print(error.localizedDescription)
}
This does seem to iterate through each level of the path until the end. Great, but what is the best way to get the full path, including the filename, other than concatenation of each object from the above?
All advice gratiously received. Thanks!
As element is an URL, if you're interested in the full path name rather than the last component, just go for:
var nextObject = element.absoluteURL // instead of .lastPathComponent
or just
var nextObject = element.path // or even relativePath
Thank you, #Christophe (+1)
I've also since spotted that the documentation for enumerator(at:includingPropertiesForKeys:options:errorHandler:) provides a nice example, which can be modifed for my purposes by using additional resource keys (e.g. name, path, etc.).

Swift Firebase database overwriting

I am making a real-time messenger using Firebase. Currently, whenever I press a button I want a new message to be appended to the channel with the index of the message, but currently, whenever I press the button a new message is created that overwrites the old message. I know that setValue is usually the issue, but I really cannot tell what I'm doing wrong. What the database looks like before I add my new message. This is what it looks like after I add a new message here, and then the code I am using to add to the database.
#IBAction func sendMessageTapped(_ sender: Any) {
if messageTextField.text == "" {
print("blank")
return
} else {
// First we will update the amount of messages that the channel has.
ref.child("channels").child(channelName!).setValue(["numberOfMessages" : numberOfMessages+1 ])
numberOfMessages += 1
// after we have updated the amount of messages we will try to create a new message.
ref.child("channels").child(channelName!).child("messages").child(String(numberOfMessages)).child("message").child("content").setValue(messageTextField.text)
ref.child("channels").child(channelName!).child("messages").child(String(numberOfMessages)).child("message").child("name").setValue("Buddy")
}
}
ok, Firebase is not a traditional table based database, is a DOCUMENT based database. At the very top you have a thing called a "collection" which is just a list of "document" things. In your case, you'd have several collection things to serve as channels: "General", "TopicQ", "InterstingStuff" etc, and within them each message as a document. No need to have a document, to then list the messages within it.
Second, you don't need indexes as you're using them, make the message id an attribute of the message, because firebase support querying by field, and even then is questionable because if you make each message a document, they will have their own auto generated id's if you want.
Third, in your code you're rewriting the whole document each time, this is why you lose your previous messages, so if you keep it, you need to add a merge option:
// Update one field, creating the document if it does not exist.
db.collection("cities").document("BJ").setData([ "capital": true ], merge: true)
you probably want to do something like this. This is what I did for my app, hope this helps someone. This rootRef.childByAutoId() generates a new entry with unique id. You can use this as reference for your case.
let rootRef = Database.database().reference(withPath: "channels")
let childRef = rootRef.childByAutoId()
let values = ["Type": self.textField.text!, "message": self.textView.text!] as? [String : Any]
childRef.updateChildValues(values)

ios application Localization

How can I set ios application supported languages?
e.g I use NSDate to get current day. If the device language is other than my supported languages NSDateFormatter returns "day" in device's language but I want to get in English if I don't support that language.
I know there is a way to get day in specific language using NSLocal but I don't want to do that way because I need to convert other strings as well.
The Apple documentation covers this pretty clearly. I know all you need is the word "day", but the following will help you include any word for any language if you do as follows:
1) You need to place all of the words (Strings) in your application into a single .swift file. Each word should be returned in a function that converts this string into the localized string per the device's NSLocal set in the device settings:
struct Localization {
static let all: String = {
return getLocalized("All")
}()
static let allMedia: String = {
return getLocalized("All Media")
}()
static let back: String = {
return getLocalized("Back")
}()
// ...and do this for each string
}
2) This file should also contain a static function that will convert the string:
static func getLocalized(_ string: String) -> String {
return NSLocalizedString(string, comment: "")
}
Here, the NSLocalizedString( method will do all of the heavy lifting for you. If will look into the .XLIFF file (we will get to that) in your project and grab the correct string per the device NSLocale. This method also includes a "comment" to tell the language translator what to do with the "string" parameter you passed along with it.
3) Reviewing all of the strings that you placed in your .swift file, you need to include each of those into an .XLIFF file. This is the file that a language expert will need to go over and include the proper translated word per string in the .XLIFF. As I stated before, this is the file that once included inside your project, the NSLocalizedString( method will search this file and grab the correct translated string for you.
And that's it!

How do I access an image URL that's not in the list JSON for my API in my Siesta Resource?

I'm using Siesta with an API that returns a very lightweight list response for root entities. For instance, for /entity this is what the response looks like:
{
count: 200,
results: [
{
url: "https://example.com/api/entity/1/",
name: "foo"
},
{
url: "https://example.com/api/entity/2/",
name: "bar"
},
{
url: "https://example.com/api/entity/3/",
name: "bat"
}]
}
The full object found at the url in the results has an avatar property that I'd really like to show in my table view for this list but I can't figure out how to make that happen with the Siesta framework. Is it possible to fetch more details from the underlying /entity/1 endpoint as part of loading the resource for the /entity list?
In Siesta’s view of the world, one url ⟺ one resource. There is thus a “summary list” resource, /entity, plus a separate “entity detail” resource for each row, /entity/1 etc. It doesn’t matter that they happen to share some of the same data; Siesta itself doesn’t make any effort to merge, synchronize, prepopulate one resource from the other. Separate URLs, separate resources.
The rule of thumb is, “If you need data from a resource, observe that resource.” Since you want to use both summary info from /entities and detail info from /entities/n, you observe both resources.
Here is a sketch of an approach you might use:
Get your table view showing just the info from /entities, no avatars. You can use RepositoryListViewController from the example project as a starting point.
Make each table cell accept a summary model, and observe its corresponding detail resource:
class EntityTableViewCell: UITableViewCell, ResourceObserver {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var avatar: RemoteImageView!
private var summary: EntitySummary?
private var detailResource: Resource?
func showEntity(summary: EntitySummary) {
self.summary = summary
detailResource?.removeObservers(ownedBy: self)
detailResource = MyApi.resource(absoluteURL: summary?.url)
detailResource.addObserver(self).loadIfNeeded()
}
Now populate the cell in resourceChanged(), mixing and matching from the summary and detail as you see fit:
func resourceChanged(resource: Resource, event: ResourceEvent) {
let detail: EntityDetail? = detailResource?.typedContent()
nameLabel.text = detail?.name ?? summary?.name
avatar.imageURL = detail?.avatar
}
You might also want to stop observing when the cell moves out of view:
override func prepareForReuse() {
showEntity(nil)
}
}
(This sketch assumes that you have separate EntitySummary and EntityDetail models. You might also have a single Entity model with the detail-only fields optional, or you might just be using raw JSON dictionaries. The approach is the same regardless.)
Here’s what happens when a cell scrolls into view:
Your cellForRowAtIndexPath calls showEntity(_:), passing an EntitySummary it got from the /entities resource.
The cell starts observing /entities/n.
This immediate triggers resourceChanged(). The detail resource has no data yet, so your cell immediately gets populated with summary info only.
Eventually the detail resource loads. If your cell is still observing it, then resourceChanged() gets called again, and this time it sees the detail info.
Note that in #4, if your cell got scrolled out of view and reused before that detail resource loaded, then your cell will no longer be observing it — and thus the late-arriving response will not clobber the reused cell’s contents.

How to internationalize the UITextField placeholder property value string in interface builder itself?

How can we internationalize the placeholder property's value of a UITextField in interface builder-attribute inspector?
You can easily change it without writing any code.
here is the example in your localized strings file set your placeholder as follows
"itw-s8-fkt.placeholder" = "YOUR TEXT";
You do the same localization as usual:
myTextField.placeholder = NSLocalizedString(#"emptyUsername",
#"userNameTextFieldPlaceholder");
Then you enter the according string in your localizable strings file.
The question has been discussed here, too:
How to use NSLocalizedString in IB [iPhone SDK]?
Describing an alternative, using several xib files.
What I've done, to minimise the amount of localisation boilerplate, is follow the model outlined here (GitHub here): create Interface Builder-friendly helper extensions (#IBInspectable vars, in a single file for convenience) for all the types of controls I want localised, including one for UITextField placeholders. I can then centralise all localisations to a single .strings file for each language and hook them up via IB. The only downside is remembering to add the keys when I create the controls. Localisation debugging (set by editing the Run scheme; I run the app in a non-base language by default) helps spot these early.
I have a single Excel spreadsheet containing keys and translations that gets passed to local translators to update, and a script that (re)generates the .strings files from the .csv prior to release.
With reference to the linked tutorial, my UITextField extension looks like this:
extension UITextField: XIBLocalizable {
#IBInspectable var xibLocKey: String? {
get { return nil }
set(key) {
text = key?.localized
}
}
#IBInspectable var xibPlaceholder: String? {
get { return nil }
set(key) {
placeholder = key?.localized
}
}
}
(The linked GitHub has an almost identical version I've only just spotted). Worth noting that when adding a new localisation I have to manually delete the contents of the XCode autogenerated .strings file, while leaving the file in place, to ensure the extensions get called.

Resources