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 theCoordinator
protocol, 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 theMainCoordinator
class. - In
BuyViewController
change the type ofcoordinator
to be an optionalBuyCoordinator
. - In
MainCoordinator
we replaced the cut-out code with a declaration of ourBuyCoordinator
class, passing in thenavigationController
property as argument, then set itsparentCoordinator
toself
, before appending it to the array ofchildCoordinators
and calling thestart
method 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
BuyCoordinator
we added adidFinishBuying()
method which called thechildDidFinish(self)
method on the parent coordinator. - Last thing, we call
viewDidDisappear(_:)
in theBuyViewController
callingcoordinator?.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
BuyCoordinator
comment out thedidFinishBuying
method. - In
BuyViewController
comment out theviewDidDisappear
method. - In
MainCoordinator
, make the class be a subclass ofNSObject
and add conformance to theUINavigationControllerDelegate
protocol. Then, at the beginning of thestart()
method, makeself
be the delegate of the navigation controller. Now implement thenavigationController(_:didShow:animated:)
method. This will check that a bound constant calledfromViewController
will be equal to theviewController(forKey: .from)
return value called on thetransitionCoordinator
or thenavigationController
. Then, if the navigation controller’s view controllers array contains thefromViewController
we 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 thefromViewController
to a constant, conditionally down casting it as aBuyViewController
, then we will call thechildDidFinish
method 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
ViewController
file, calling itproduct
. - Inside
BuyCoordinator
add a propertyvar selectedProduct = 0
. - In
MainCoordinator
modify thebuySubscription
method to accept an integer parameter, then callchild.selectedProduct = productType
(assuming you calledto productType: Int
the parameter!). - In
ViewController
modify the call tobuySubscription
adding 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
UITabBarController
calledMainTabBarController
, give it aMainCoordinator
property calledmain
passing in a new instance ofUINavigationController
as its only argument. Then, inside itsviewDidLoad
method, callstart
onmain
and set theviewControllers
array property to be an array containing main’s navigation controller. - Go to
MainCoordinator
and add a property tostart()
calledvc.tabBarItem
and set it equal toUITabBarItem
with a tab bar system item of.favorites
and 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 calledBuying
and make it conform to theAnyObject
protocol before giving it a method calledbuySubscription
. Repeat the process for another file and protocol calledAccountCreating
and give it a method calledcreateAccount
. - In
MainCoordinator
make the class conform toBuying, AccountCreating
- In
ViewController
change the type ofcoordinator
to be(Buying & AccountCreating)?
. That’s it!
If you want to use closures:
- In
ViewController
scrap the line where the coordinator is created and add two optional empty closures that return nothing tied to thebuyAction
andcreateAccountAction
variable properties. Inside thebuyTapped
andcreateAccountTapped
methods, call them optionally. - In
MainCoordinator
modify thestart
method 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!