What is the best practice to identify views in iOS?
For instance, you could have a lot of views that aren't worth subclassing because you need to work with just one subview in those views.
I like to use view tags, and for easy identification I would do like so for ObjC:
enum {
kViewTagSomeButton = 10,
kViewTagSomeOtherButton,
}
But to do this in Swift, this becomes quite verbose:
enum ViewTag: Int {
case SomeButton = 10, SomeOtherButton, ...
}
if view.tag == ViewTag.SomeButton.rawValue {
// Do something
}
Or should I use a completely different approach?
Edited
Changed the whole question to a more appropriate context
Related
What's the main difference between property observers and property wrappers? They seem to be very similar in that they manage how the properties are stored. The only thing I can think of is that you can reuse property wrappers since there is a layer of separation between code that manages how a property is stored and the code that defines a property.
Property Wrapper
#propertyWrapper
struct TwelveOrLess {
private var number: Int
init() { self.number = 0 }
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
struct Rectangle {
#TwelveOrLess var height: Int
#TwelveOrLess var width: Int
}
Property Observer
struct Rectangle {
var height: Int {
didSet {
if oldValue > 12 {
height = 12
} else {
height = oldValue
}
}
}
var width: Int {
didSet {
if oldValue > 12 {
width = 12
} else {
width = oldValue
}
}
}
}
The two cases above accomplish pretty much the same thing, which is to set the properties to be equal to or less than 12.
You say:
The only thing I can think of is that you can reuse property wrappers since there is a layer of separation between code that manages how a property is stored and the code that defines a property.
Your example (and some of your text) appears to be lifted from the Swift Programming Language: Property Wrapper manual:
A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property. For example, if you have properties that provide thread-safety checks or store their underlying data in a database, you have to write that code on every property. When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.
So, yes, the virtue of the property wrapper is the reuse achieved by separating the “code that manages how a property is stored and the code that defines a property.” This resulting reuse is the whole m.o. of property wrappers.
You clearly, you can write your own setters and getters (which is better, IMHO, than a pattern of writing an observer that mutates itself), too, but you lose the reuse and abstraction that the property wrappers offer.
You go on to say:
The two cases above accomplish pretty much the same thing, which is to set the properties to be equal to or less than 12.
Sure, but if you want to do this for ten different properties, the wrapper avoids you from needing to repeat this code ten times. It also abstracts the details of this “equal to or less than 12” logic away from where you declare the property.
Another big difference betwixt property observers and property wrappers is that property observers can access self, whilst property wrappers cannot yet (as of this writing) access self using a stable, documented interface.
You can work around this limitation by manually passing self to the property wrapper in init. This workaround is described in the property wrapper proposal.
You can access self in a property wrapper using an undocumented, unstable interface which you can learn about by typing “property wrapper _enclosingInstance” into your favorite search engine.
First of all a small introduction, im relatively new to Swift and to programming in general, been doing it for the last year and loving every and each new thing of this vast world.
My post is about some technical advices and to know if the decisions that are being made in my company make some sense.
I will first address the design that is being suggested and next my conclusions.
The design that's being implemented;
We are working in a big app, this app has some flows where they follow a sequence of 5 to 8 controllers, our team leader insists in this design pattern;
Let’s call the first controller a holder and the holder(black border) is responsible to have a container, this container has a proper navigation controller(red border), also, the holder hold all the data(orange) that those secondary controllers are generating.
Diagram of what this pattern is trying to achieve
To do this every red border controller has a method:
private func getParent() -> HolderViewController? {
if let parent = navigationController?.parent as? HolderViewController {
return parent
}
return nil
}
And to write in holder we call the method;
getParent().someModelInstance.someModelProperty = "some data”
Conclusion;
Passing data through navigation controller seems to go against to the single responsibility principle.
Creating strong references in every controller, even if I ensure that the navigationController is properly deinit when flow ends, does not seem a good option, this could lead to memory leaks and retain cycles.
I cannot ensure that, for some hod reason, two controllers try to access the same property at the same time.
This seems the Singleton Design pattern but with a limited “scope”
Resolutions;
Since I am new and I’m working in a company, and, every company has a hierarchy my objective above all is to learn if my conclusions are wrong and have better and more concise explanation about this.
First of all, to address the problem of memory leaks I created a concurrentQueue.
Instead of accessing the model directly to write in it I tried to address it through a method that will use a keyPath instead of the model directly, this is the method I’m using to write in the model;
In holder:
class HolderViewController: UIViewController {
#IBOutlet weak var bottomNavigationContainer: UIView!
private var bottomNavigationController: UINavigationController!
private var someModel: SomeModel!
private let concurrentQueue: DispatchQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
override func viewDidLoad() {
super.viewDidLoad()
setupBottomNavigation()
}
private func setupBottomNavigation() {
rootController = SecondayViewController()
if let root = rootController {
bottomNavigationController = UINavigationController(rootViewController: root)
addChild(bottomNavigationController)
bottomNavigationController.view.frame = bottomNavigationContainer.bounds
bottomNavigationContainer.addSubview(bottomNavigationController.view)
}
}
}
extension HolderViewController {
public func setValueInModel<Value>(_ value: Value, forKey path: WritableKeyPath<SomeModel, Value>) {
concurrentQueue.async(flags: .barrier) { [weak someModelInstance] in
if var obj = someModelInstance {
obj[keyPath: path] = value
}
}
}
public func readFromHolder() -> SomeModel {
concurrentQueue.sync {
return self.someModelInstance
}
}
}
This methods will be called like this;
class SecondayViewController: UIViewController {
var someString: String = "some data"
private func getParent() -> HolderViewController? {
if let parent = navigationController?.parent as? HolderViewController {
return parent
}
return nil
}
private func setValueInHolder(string: String) {
getParent().setValueInModel(string, forKey: \.someModelProperty)
}
private func readFromHolder() -> String {
return getParent().readFromHolder().someModelProperty
}
}
This look like some messy code to do a simple thing, we could use closures, delegates, segues etc… but my team leader does not like the other, simpler and more common solutions. Forgot to mention, every of our controllers has a xib and we do not use storyboards.
I know the basics of how to use the other options, what I’m trying is to understand if this is or it isn’t a good solution and why, and if my way of thinking and my methods make any sense.
Remember, every conclusion I took or every solution I've implemented could be wrong, that’s why I’m sharing with you my thoughts in order to learn from your advices and experience
Thanks in advance. :)
Stay safe!
As a preface: this type of question may be more fitting for the code review community
I can tell just by looking at the first diagram where your concern starts. Seeing the data flow as a graph you notice that there is a cycle. There is a time an a place where this may be use full (for performance more than anything) and memory management is extremely important to keep in mind in that case.
You may notice that after the call to addChild(_:) adds the contained controller to children: [UIViewController] and sets its parent property. But this done for you by the library.
Similar to the concept of the ViewController where views are dumb and the logic is contained in the view controller. I would similarly decouple the children from the parent view controllers moving most of the logic away from the children and coupling using the appropriate mechanism.
With that said I rarely find much value in using KVO with swift there are other mechanism that accomplish the same thing.
It really does depend on what the relationships are between the controllers are and what functions they have. There isn't much there to go off of. I'll leave that up to you to discover what solution you really need and the best guidance I found for this was on NSHipster's blog describing the communication mechanism to use for loose/strong coupling vs one-to-one and one-to-many relationships.
Also should point out that:
if let root = rootController {
bottomNavigationController =
UINavigationController(rootViewController: root)
addChild(bottomNavigationController)
bottomNavigationController.view.frame =
bottomNavigationContainer.bounds
bottomNavigationContainer.addSubview(bottomNavigationController.view)
}
You should be using willMove and didMove and move any set up there.
I am creating a game in which, depending on the number of 'swipes' chosen to do, (let's say 3), 3 different patterns show on the screen, one by one. I am working on developing the first pattern.
So I have this:
if (swipes.no_of_swipes) == 3 {
swipeArray = Array<UInt32>(count: 3, repeatedValue: 0)
for i in 0 ..< 3 {
swipeArray[i] = arc4random_uniform(84)}
}
As far as I am aware, this code creates an array with three UInts which can be accessed by doing swipeArray[0], swipeArray[1], and swipeArray[2]. My first question is how long will this swipeArray stay the same? Until the close the view? Should I have a 'refresh button' when the user loses - and if so, how would I make one?
Then I have a property observer. You will notice the for loop, which I am using to keep code concise. I understand that I could do something like x++ somewhere in here so that it will go through each one.
var playBegin: Bool = false{
didSet {
if playBegin == true {
println("\(playBegin)")
var swipes = Menu()
if (swipes.no_of_swipes) == 3 {
for i in 0 ..< 3 {
patternRoom.image = UIImage(named: "pattern\(swipeArray[x])")
//rest of code
}
}
}
The pattern image comes from a set of 84 images named like pattern7 and pattern56. My second question is, how could I code the for loop to go through each swipeArray[x].
Thank you in advance,
Will
how long will this swipeArray stay the same?
This is a bit too open ended. It’ll stay the same until you assign a new value to it, either from this same bit of code or a different part. Only you can know when that will be, by looking at your code.
Since you express an interest in keeping the code concise, here’s a couple of code tips.
You might think about writing your first snippet’s loop like this:
swipeArray = (0..<swipes.no_of_swipes).map { _ in
arc4random_uniform(84)
}
This combines creating a new array and populating the values. By the way, just in case you don’t realize, there’s no guarantee this array won’t contain the same value twice.
It’s also probably better to make swipeArray of type [Int] rather than [UInt32], and to convert the result of arc4random to an Int straight away:
Int(arc4random_uniform(84))
Otherwise the UInt32s will probably be a pain to work with.
For your second for loop, you can do this:
for i in swipeArray {
patternRoom.image = UIImage(named: "pattern\(i)")
// rest of code
}
When writing Swift, usually (but not always), when you find yourself using array[x] there’s a better more expressive way of doing it.
I have a delete button on a cell and it was crashing my app in iOS7 but not in iOS8. I later found out the crash was caused by by how deep down I was digging into my view hierarchy.
To make things clearer here is the code that solved the issue:
func didTapDeleteButton(sender: UIButton) {
var cell: HoursOfOperationTableViewCell!
if let gotModernAlert: AnyClass = NSClassFromString("UIAlertController") {
println("UIAlertController can be instantiated")
//make and use a UIAlertController
cell = sender.superview?.superview? as HoursOfOperationTableViewCell
}
else {
println("UIAlertController can NOT be instantiated")
cell = sender.superview?.superview?.superview? as HoursOfOperationTableViewCell
}
As you can see I have to go further down into my view hierarchy to get to my custom cell view in iOS 7.
I was wondering if there was a more efficient way of checking the iOS version?
If I don't check then my app crashes in iOS7 if I leave the code like this:
cell = sender.superview?.superview? as HoursOfOperationTableViewCell
Thanks for your time.
To test if a class is loaded in the current runtime, you could call the following:
var classExistsAndIsSafeToUse = countElements(NSStringFromClass(object_getClass(UIAlertController))) > 0
You should always check for the existence of a particular class over checking the iOS version.
That being said, for checking the iOS version, below is an excerpt from my solution on just that
New in iOS 8 is NSProcessInfo that integrates greatly with Swift and
allows for better semantic versioning checks. Thanks to NSHipster
we can see a clean and canonical source from where the OS version can
be derived.
For minimum deployment targets of iOS 8.0 or above, use NSProcessInfo
operatingSystemVersion or isOperatingSystemAtLeastVersion.
This would yield the following:
let minimumVersion = NSOperatingSystemVersion(majorVersion: 3, minorVersion: 1, patchVersion: 3)
if NSProcessInfo().isOperatingSystemAtLeastVersion(minimumVersion) {
//do some fun stuff
}
For minimum deployment targets of iOS 7.1 or below, use compare with
NSStringCompareOptions.NumericSearch on UIDevice systemVersion.
This would yield:
let minimumVersionString = "3.1.3"
let versionComparison: NSComparisonResult = UIDevice.currentDevice().systemVersion.compare(minimumVersionString,
options: NSStringCompareOptions.NumericSearch)
if versionComparison == .OrderedDescending {
//do some fun stuff
}
EDIT
as a side note, if you see something like this:
cell = sender.superview?.superview? as HoursOfOperationTableViewCell
your code has begun to exhibit whats known as code smell, you should consider implementing your view logic inside of your view, the controllers should not have a deep knowledge into the view hierarchy. Remember, the goal is on writing easy to change code. Consider implementing abstraction, delegation and singular responsibility principled design.
Good luck
You can simply check [[UIDevice currentDevice] systemVersion]. This returns an NSString so you will need to convert to a float if you want to compare.
Another way is using NSFoundationVersionNumber. Declare the following constants in a file where you keep global constants.
let iOS7 = floor(NSFoundationVersionNumber) <= floor(NSFoundationVersionNumber_iOS_7_1)
let iOS8 = floor(NSFoundationVersionNumber) > floor(NSFoundationVersionNumber_iOS_7_1)
And check for the version like this.
if iOS8 {
// Use iOS 8 APIs
} else {
// Fallback for older iOS versions
}
I'm learning how to use CALayers and perform animations on their properties. To a beginner Apple's documentation is simply cryptic. I managed to find an example (called: CustomAnimatableProperty) in iOS's documentation which somewhat 'explains' how to do what I want:
// For CALayer subclasses, always support initWithLayer: by copying over custom properties.
-(id)initWithLayer:(id)layer {
if( ( self = [super initWithLayer:layer] ) ) {
if ([layer isKindOfClass:[BulbLayer class]]) {
self.brightness = ((BulbLayer*)layer).brightness;
}
}
return self;
}
Translating the method override to Swift however gives me a few errors:
The errors stem from my lacking understanding of what's going on here. I'm not sure what are we checking for in those nested if statements. Also I am a bit baffled by the usage of "=" in the main if(){} block. Shouldn't we be checking ("==") for equality?
But yeah any general help would mean the world. I've tried reviewing a few blog-posts / tutorials online, however non of them deals with this speciffic issue.
The self = [super init...] idiom is for Objective-C, not Swift. In Swift, init blocks aren't normal functions and don't return anything.
While we're at it, let's use the Swift idiom for downcasting. We also need to guarantee that size is initialized before we call super.init.
override init(layer: AnyObject!) {
if let layer = layer as? SegmentActiveLayer {
size = layer.size
} else {
size = 0
}
super.init(layer: layer)
}