How to open a link in Safari
Following this video by Sean Allen.
Import SafariServices.
func showSafariVC(for url: String) {
guard let url = URL(string: url) else {
// show an invalid URL error alert
return
}
let safariVC = SFSafariViewController(url: url)
present(safariVC, animated: true)
}
Inside an action of a button call this function with a proper string URL.
Work with DateComponents
Following this video by Sean Allen.
The video already contains a finished file, which I do not want to override. You can find it in the description.
Advanced Coordinators in iOS
Following this video by Paul Hudson
Six questions:
- Using child coordinators
- Navigating backwards
- Passing data between controllers
- Coordinated tab bar controllers
- Handling segues
- Protocols and closures
1. Child Coordinators
Crazy … I had to use YouTube functionality of turning down the speed to 0.75 to be able to follow anything at all. Really crazy…
Anyway, not that I could understand a lot of this, it is a very complicated topic to me. In short:
- We created a new class,
BuyCoordinator, that conforms to theCoordinatorprotocol, gave it a weak reference to an optionalMainCoordinator, initialised the navigation controller to the required variable, then, in thestart()method, we cut-pasted the code that was previously in thebuySubscription()method of theMainCoordinatorclass. - In
BuyViewControllerchange the type ofcoordinatorto be an optionalBuyCoordinator. - In
MainCoordinatorwe replaced the cut-out code with a declaration of ourBuyCoordinatorclass, passing in thenavigationControllerproperty as argument, then set itsparentCoordinatortoself, before appending it to the array ofchildCoordinatorsand calling thestartmethod on thechild. Then we added achildDidFinish(_:)method which loops over the enumerated version of the child coordinators array and if the coordinator is equal to the child (using the===operator), then it would remove at the index before breaking from the loop. - Back in
BuyCoordinatorwe added adidFinishBuying()method which called thechildDidFinish(self)method on the parent coordinator. - Last thing, we call
viewDidDisappear(_:)in theBuyViewControllercallingcoordinator?.didFinishBuying().
Now let’s see if I can reproduce it alone for the other view controller we added.
I DID IT! YES!
2. NAVIGATING BACK
To avoid issues with the coordinator stack once our apps get bigger and bigger we need to perform some changes:
- In
BuyCoordinatorcomment out thedidFinishBuyingmethod. - In
BuyViewControllercomment out theviewDidDisappearmethod. - In
MainCoordinator, make the class be a subclass ofNSObjectand add conformance to theUINavigationControllerDelegateprotocol. Then, at the beginning of thestart()method, makeselfbe the delegate of the navigation controller. Now implement thenavigationController(_:didShow:animated:)method. This will check that a bound constant calledfromViewControllerwill be equal to theviewController(forKey: .from)return value called on thetransitionCoordinatoror thenavigationController. Then, if the navigation controller’s view controllers array contains thefromViewControllerwe will return from the method, meaning that we should not get out of this screen. At this point, if it is possible to conditionally bind thefromViewControllerto a constant, conditionally down casting it as aBuyViewController, then we will call thechildDidFinishmethod passing in thebuyViewController.coordinator.
It’s a bit obscure to me how this is popping the view controller but … well … who knows? Anyway, I now try to repeat the same process for the other coordinator.
DONE! YES!
3. PASSING DATA
This part had to be slightly modified to work with our new version of the file, but don’t worry, it’s easy.
- Drag a segmented control onto the Storyboard and create an outlet for it into the
ViewControllerfile, calling itproduct. - Inside
BuyCoordinatoradd a propertyvar selectedProduct = 0. - In
MainCoordinatormodify thebuySubscriptionmethod to accept an integer parameter, then callchild.selectedProduct = productType(assuming you calledto productType: Intthe parameter!). - In
ViewControllermodify the call tobuySubscriptionadding theto:argument name and passing inproduct.selectedSegmentIndex.
As before, I will try now and replicate it with the other coordinator to see if I can do it!
DONE! Also corrected a stupid error I had made!
4. COORDINATED TAB BARS
This was neat! Just … will I be able to use it in my apps in the future? Well … I don’t know, I need to make some test in the huge amount of time available!
- Create a new subclass of
UITabBarControllercalledMainTabBarController, give it aMainCoordinatorproperty calledmainpassing in a new instance ofUINavigationControlleras its only argument. Then, inside itsviewDidLoadmethod, callstartonmainand set theviewControllersarray property to be an array containing main’s navigation controller. - Go to
MainCoordinatorand add a property tostart()calledvc.tabBarItemand set it equal toUITabBarItemwith a tab bar system item of.favoritesand a tag of 0. - Now, in the
SceneDelegate, comment out the previous coordinator code and just change the window’s root view controller to be a new instance ofMainTabBarController.
5. USING SEGUES
Lol … just throw them away!
6. PROTOCOLS AND CLOSURES
Coordinators are just protocols! Maybe a little specialised!
- Add a new Swift file called
Buying, create a protocol calledBuyingand make it conform to theAnyObjectprotocol before giving it a method calledbuySubscription. Repeat the process for another file and protocol calledAccountCreatingand give it a method calledcreateAccount. - In
MainCoordinatormake the class conform toBuying, AccountCreating - In
ViewControllerchange the type ofcoordinatorto be(Buying & AccountCreating)?. That’s it!
If you want to use closures:
- In
ViewControllerscrap the line where the coordinator is created and add two optional empty closures that return nothing tied to thebuyActionandcreateAccountActionvariable properties. Inside thebuyTappedandcreateAccountTappedmethods, call them optionally. - In
MainCoordinatormodify thestartmethod so that it doesn’t set the vc’s coordinator to self but instead sets each action to the corresponding method, making sure it captures self weakly before.
Wow, this was a journey!
I can’t wait to try this in my future apps! Well … yeah … one day I will!
Some Xcode in 20 Seconds videos by Paul Hudson (& similar)
- Project filtering: at the bottom of the project navigator section we have two icons, one which looks like a clock and shows the files we have worked on recently and the other that looks like the source control navigator icon. This last one shows files that are under source control, have some changes but have not been committed yet.
- Renaming code: select a property then go to Editor > Refactor > Rename
- Fix all issues: go to Editor > Fix all issues, for example when changing Swift versions.
- Integer multiples in Swift:
- Finding callers: once the cursor is inside a method, we can click in the top left of the editor area on that icon that looks like four squares with little arrow between them, called “Navigate to related items”. There, a list opens and we can check which files are calling or are called by our method.
- Counting items in Swift:
This is new in Swift 5.0 - Transforming and unwrapping dictionary values with
compactMapValues(): - Quick Help: hold Option and then click on the name of the variable/method you want to explore.
- Toggling Boolean:
- Find in project: press Shift-Command-O, then type to search for a file before opening it (one can even press Option-Shift-Enter to decide where to open it in the editor). Once this is open, pressing Shift-Cmd-J will select it in the project.
- Adding Documentation: press Option-Command-/. For the Italian keyboard add the Shift key to access the forward-slash character.
- Locking views: in the storyboard, in the Identity Inspector, change the Lock property to “All Properties”.
- Vector artwork: using vector artwork is a very good choice to preserve scaling and image qualities. Select all your artworks in the assets, then go to the Attributes Inspector, select “Preserve Vector Data” and then change the Scales property to “Single Scale”
- Jump to a line: from tip 10 of this list you can add a colon
:followed by a line number to jump straight to that line. - Code coverage: Hold Option then go to Product > Test. Then select Code Coverage > Gather coverage. Then Editor > Show Code Coverage.
- To detect issues with some code that throws invisible errors (such as networking code), we can create a breakpoint in the gutter and then edit it so that it has an action playing a sound. Remember to check the “Automatically continue after evaluating actions” option.
- When we get a crash or something that interrupts execution of our code, in the lower left portion of the Debug Area there is Quick Look button which lets us preview the value of variables at the moment the code execution was interrupted.
That’s it for today!
Thank you for reading!
If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).
If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s books from this Affiliate Link.
Anyways, thank you so much for reading!
Till the next one!
