Try this with a plain new Single View application template, so in the storyboard there is only 1 view controller
In the ViewController
class ViewController: UIViewController {
override func awakeAfterUsingCoder(aDecoder: NSCoder) -> AnyObject? {
print(self)
return self
}
}
We can see that it is printed 3 times, why is that?
In the spec
Overridden by subclasses to substitute another object in place of the object that was decoded and subsequently received this message.
You can use this method to eliminate redundant objects created by the coder. For example, if after decoding an object you discover that an equivalent object already exists, you can return the existing object. If a replacement is returned, your overriding method is responsible for releasing the receiver.
This method is invoked by NSCoder. NSObject’s implementation simply returns self.
Why is it called many times?
I have tested with your suggest and I see that it call 2 times. And with this I describe my knowledge below:
ViewController will call initWithCoder and after that will call awakeAfterUsingCoder
Continue ViewController will call loadNibNamed, loadNibNamed still call awakeAfterUsingCoder again.
This infomation I have referenced from other source. You can view and if have some info. Please share with me. I am very appreciate :)
From StackOverflow
Cocoanuts
fantageek.com
Related
I'm trying to use the MVC in an app loading a url into a webView. I'm struggling with how to/whether to define the back, forward, reload, etc... functions in the model or the viewController. I'm pretty sure they belong in the model, but then how do I call them in the IBAction for the corresponding button?
In trying to call the class function in the IBAction, first I have to create an instance of WebViewLoadRequest in each IBAction which seems extraneous. I can't (and probably shouldn't) create a global instance of WebViewLoadRequest because self isn't available in the property initializer to reference the UIWebView Outlet
class WebViewLoadRequest {
var outlet: UIWebView
var url : String
private var address: URL
init(outlet: UIWebView, url: String) {
self.outlet = outlet
self.url = url
self.address = URL(string: url)!
}
func load() {
self.outlet.loadRequest(URLRequest(url:address))
}
func back() {
if outlet.canGoBack {
outlet.goBack()
}
}
func forward() {
if outlet.canGoForward {
outlet.goForward()
}
}
func refresh() {
outlet.reload()
}
}
To simplify:
How can I write the following function in the model layer and implement it in the ViewController?
//outlet refers to a UIWebView
func back() {
if outlet.canGoBack {
outlet.goBack()
}
}
Or should this strictly be a function of the view controller? I realize essentially just the view is being changed, but there's a lot going on behind the scenes in the goBack operation that I'm not sure belongs in the ViewController
You are confusing two things here IMO, there is object oriented programming which you described in your question. There is also another concept which is commonly used and also very common in Swift, this concept is MVC. Model, View, Controller. This is basically a seperation of powers. In practice this comes down to:
Model - custom class with all the logic, in your case the WebViewLoadRequest
View - defined in a 'storyboard'
Controller - Will be a subclass of UIViewController, this subclass also has a reference to your model, all the IBActions and IBOutlets to connect to and update your UI.
You can learn more about MVC here:
https://www.raywenderlich.com/132662/mvc-in-ios-a-modern-approach
Also I would strongly recommend watching the C193p course from Standford by Paul Hegarty, this teaches you all there is to know. For example the 4th week is about MVC
https://itunes.apple.com/us/course/developing-ios-10-apps-with-swift/id1198467120
EDIT:
Basically your construction is a construction of delegation. The normal way to solve this issue is by creating a delegate property on your model-class, the delegate implements a certain protocol with for example a 'canIGoBack'-function. The model can request extra data from the delegate by calling that delegate function. Apple has something about delegation in their swift manual (check the 'Delegation' part:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
I believe Paul Hegarty also discussed it.
However, in this case it is also fine to do the canGoBack in the viewcontroller and conditionally call your model. MVC is only a guideline, there are many reasons to sometimes do logic in a viewcontroller, the biggest reason is probably if it makes shit easier. Implementing delegation only for a canGoBack is most of the times just silly.
I was just understanding the method swizzling done in obj c Method Swizzling and dangers of using method swizzling and couldn't help but draw a comparison between doing method swizzling and overwriting method implementation using categories.
They both help override the functionality of the predefined framework methods.
So is there any difference between the two or they can be used interchangeably?
The main difference is that Objective C prevents you from invoking the original implementation from a category override. This is because Objective-C's super invocations start from the super-class, while categories override methods on the same class level.
Method swizzling, on the other hand, lets you keep a reference to the original implementation as well, so that you could call it from inside your implementation. For example, in the article at your first link the author writes this:
- (void) logged_viewDidAppear:(BOOL)animated {
[self logged_viewDidAppear:animated];
NSLog(#"logged view did appear for %#", [self class]);
}
The second line makes a call to logged_viewDidAppear: method, which looks like an unconditional call to itself that should cause infinite recursion. However, this is not what happens: after swizzling, this call gets transformed into a call to the original viewDidAppear: because of the way method swizzling works.
In contrast, overriding a method from a category does not give you access to the logic of the method that you are overriding. It lets you replace the logic, but it does not let you extend it.
extension UIViewController{
public func myViewDidLoad(){
self.viewDidLoad()
//but you need to call this method everywhere replacing
}
//you cant do this
public func viewDidLoad(){
self.viewDidLoad()
//my code
}
}
Categories or extension let you do these:
Add computed properties and computed type properties
Define instance methods and type methods
Provide new initializers
Define subscripts
Define and use new nested types
Make an existing type conform to a protocol
(from Apple)
They don't let you extend original method of the same class that you are extending and if you try like the above code method signature conflict pops up.
You might want to check this website to get the concept diagrammatically. I really loved it.
http://matteogobbi.github.io/blog/2014/12/15/extending-methods-in-a-category-by-method-swizzling/
Make sure to check this awesome article for good implementation detail:
http://nshipster.com/method-swizzling/
I've created simple application using Page-Based Application template. All data stored inside var pageData = NSArray() and in the Apple's example it initialized inside init method of ModelController class. And now I need to update pages with the data received from the server. I made method updateDataFromServer inside ModelController class where put json request to server and updating pageData. If I tried to call my updateDataFromServer from the init of ModelController everything going wrong. From which place I should call updateDataFromServer and how to update pageData correctly? Maybe func updateDataFromServer should be placed in other place?
Source code: http://github.com/stillfinder/UAH-Rate
You can put your mathod inside the RootViewController which may subclass of UIViewController, you can put your method in that class, and call it from viewDidAppear.
As soon your method get response from the server, you can reload data source in rootview itself, and copy that array to the modelViewController so the PaginationViewController - datasource get update.
You can post your code for more help.
May this helps you.
HTH, Enjoy Coding!!
I was using this code to pass data between controllers.
InterfaceController.reloadRootControllersWithNames(["1","2","3"], contexts: ["adf","asd","asd"])
I called this code in willActivate() method, that you can see here.
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
InterfaceController.reloadRootControllersWithNames(["1","2","3"], contexts: ["adf","asd","asd"])
}
I have no idea about whats wrong with this code, in apple watch, the app is just refreshing again and again. Is there any other method to pass data between interface controllers while we use page based navigation.??
Please Find the below way of passing data in paged-based navigation in watch application (Watch os 2 and later).
Note :
An array of strings, each of which contains the name of an interface controller you want to display in the page-based interface. In your storyboard, the name of an interface controller is stored in the object’s Identifier property, which is located in the attributes inspector. The order of the strings in the array is used to set the order of the corresponding interface controllers. This parameter must not be nil or an empty array.
Objectiv C
[self presentControllerWithNames:#[#"FirstViewController",#"SecondViewController",#"ThirdViewController"] contexts:#[#"adf",#"asd",#"asd"]];
Swift
let controller = ["FirstViewController","SecondViewController","ThirdViewController"]
let pageContexts:[AnyObject]? = ["adf","asd","asd"]
presentControllerWithNames(controller, contexts: pageContexts)
If you call reloadRootControllersWithNames(NSArray, contexts: NSArray) onto the view controller, it will do as the function is titled: reload. Therefore, you have a view controller that refreshes all 3 of the items in the first Array you give, and then, since your current view controller calls willActivate() upon loading, it infinitely refreshes.
I don't have enough reputation to make this a comment, but my suggestion here is to find a method that you can call on an instance of the watch interface you want to in order to pass data.
What I mean is, if you have each interface as its own class, then you can make a method in the interface you want to get to that sets a property to whatever data you want to transfer before you present the interface.
I also use Objective-C more than Swift, but here's what my guess is as to what the Obj-C code would come to:
SecondWatchInterface *secondWatchInterface = [SecondWatchInterface new];
[SecondWatchInterface setSomeDataWithAStringValue:#"Foo"];
[self presentWatchInterface:SecondWatchInterface]
In Swift, I'm thinking this would equate to:
SecondWatchInterface *secondWatchInterface = SecondWatchInterface.new()
secondWatchInterface.setSomeDataWithAStringFile:"Foo"
presentWatchInterface(secondWatchInterface)
I'm unfamiliar with WatchKit myself; I typically use SpriteKit. I hope I helped identify what the problem was, at least!
To pass data between interface controllers you need to go for this method:
presentControllerWithName(“Second Watch Controller”, context: [“segue”:“pagebased”,“data”: "Data Passed Successfully”]);
Also, you can refer to this example: http://www.informit.com/articles/article.aspx?p=2272657&seqNum=3
In Objective-C, for example, if Apple adds new method called method1 to UIView, existing apps which are already released to the App Store and use the following code could crash or behave unexpectedly:
// Objective-C
#interface MyView : UIView
- (void)method1;
#end
// Swift
class MyView : UIView {
func method1() {
// do something
}
}
But in Swift, to override a method, you need a override keyword to prevent overriding accidentally.
If you override a method without override keyword, the compiler generates compile-time error.
What will happen if Apple adds new API methods in the next iOS versions, and if my apps or your apps use methods whose name are the same as the new APIs' names.
In Swift, will new API methods be overridden by methods in existing apps like Objective-C?
Or new APIs don't effect existing same-name user-defined methods thanks to Swift's explicit override feature (override keyword)?
If your app is already built and uploaded no you won't have an issue.
But if you try to re-build the app for a new update and you have the bad luck they named a new Api Method the same name of your Object Method, most likely the error you might get is only if the place you use the method you don't use a correct identifier like Not calling self.method1() and just calling method1() and your object inherits from UIViewController, which coincidently has that new Method1.
other than that I wouldn't worry about that type of issue happening, well at least I haven't had an issue like this the 3-4 years I have been programming for iOS.
//Lets assume UIViewController has a new Method in the Api now which
//they updated called Method1
class MyViewController :UIViewController {
init {
//When you try to re-build your app this line of code right here would complain
//because of ambiguity, two methods called the same one from your
//Parent Class and your own Class.
//method1()
//"super.method1()" or code below to solve the ambiguity issue.
self.method1()
}
//Added this just because I happen to use UIViewController.
override viewDidLoad() {
super.viewDidLoad()
}
//Your own method without Override, since you want to use your own method.
func method1() {
//Does something important
}
}
Update Based on your comment:
1) Is there a documentation about it or did you tested it yourself in Swift?
I have tested it myself because I have Apps in the App Store. So what happens is that whatever is already uploaded the code will be working because the App you uploaded prepackages the Frameworks with their current working Apis and your Classes.
And No, I have not seen a Documentation about it, I know about it because I have personally see it.
2) And I think you can't define func method1() without a override keyword if UIViewController already has func method1()
Thats correct! Assuming the APi has a method already you have the write Override keyword to be able to use that function with the same name. But remember according to your scenario you mentioned that the API created that method with that name AFTER you had already your project uploaded in the AppStore. So the problem you would only see it when doing some new coding and trying to Rebuild the app.