Tips, tricks and favorites
@attheodo
After almost 10 years on the iOS platform
App Delegate
🤪
You can have more than one!
Single responsibility principle
public protocol ApplicationDelegateResponding: UIApplicationDelegate {}
final class LoggerApplicationService: NSObject, ApplicationService {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: ...) -> Bool
{
log("App started")
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
log("Entered background")
}
}
Manual Window stack setup
Cause YOU WILL test at some point
func application(:didFinishLaunching) -> Bool {
if !testing() {
setupAppCoordinator()
}
}
fileprivate func setupAppCoordinator() {
if window != nil { return }
window = UIWindow(frame: UIScreen.main.bounds)
appCoordinator = AppCoordinator(window: window!, services: services)
let appCoordinatorRootVC: UIViewController? = appCoordinator.start()
guard let initialVC = appCoordinatorRootVC else {
fatalError("AppCoordinator didn't return a view controller")
}
window?.rootViewController = initialVC
window?.makeKeyAndVisible()
}
Kill switch
Cause backwards compatibility is not forever
https://github.com/ArtSabintsev/Siren
let siren = Siren.sharedInstance
siren.majorUpdateAlertType = .force
siren.minorUpdateAlertType = .option
siren.patchUpdateAlertType = .skip
siren.revisionUpdateAlertType = .none
siren.checkVersion(checkType: .immediately)
Project Structure
- Group by feature (usually by tab)
- Inside features, group by type
File "structure"
// MARK: - Static
static let cellHeight: CGFloat = 80.0
// MARK: - IBOutlets
@IBOutlet weak var myLabel: UILabel!
// MARK: - IBActions
@IBAction func didTapDoneButton(sender: UIControl)
// MARK: - Public Properties
weak var delegate: MyDelegate?
// MARK: - Private Properties
fileprivate var someProperty: Bool = false
// MARK: - Overrides
var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
// MARK: - Lifecycle
func viewDidLoad() {}
deinit {}
// MARK: - Public Methods
open func doSomething()
// MARK: - Private Methods
fileprivate func doSomethingElse()
Targets
-
As many as you see fit:
- ​Usually Dev, Staging, Production
-
Make sure your go with different `Info.plist` files and not preprocessor macros:
- ​Info-{dev/staging/prod/...}.plist
- Different icons
- Different API endpoints
- Different 3rd party services keys
Type Safety
-
Too many APIs rely on Strings
- ​Storyboard Ids
- Image names
- Fonts
- Segues
- Nibs
- Storyboard names
- Strings on APIs = begging for crashes
https://github.com/mac-cain13/R.swift
Type Safety
myImage.image = R.swift.deleteIcon()
label.font = R.font.muli(size: 15.0)
let main = R.storyboards.main.mainViewController()
let cell = table.dequeReusableCell(withIdentifier: R.reuseIdentitifers.myCell)
Type Safety
-
There's another beast...
- ​NSNotifications
-
userInfo 🤮
https://github.com/artman/Signals
class NetworkLoader {
// Creates a number of signals that can be subscribed to
let onData = Signal<(data:NSData, error:NSError)>()
let onProgress = Signal<Float>()
...
func receivedData(receivedData:NSData, receivedError:NSError) {
// Whenever appropriate, fire off any of the signals
self.onProgress.fire(1.0)
self.onData.fire((data:receivedData, error:receivedError))
}
}
networkLoader.onProgress.subscribe(with: self) { (progress) in
print("Loading progress: \(progress*100)%")
}
Safety in general
extension Collection where Indices.Iterator.Element == Index {
subscript (unsafe index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
"Base" view controllers
var appDelegate: AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
func handleReachabilityChanges() {
NotificationCenter.default.addObserver(self,
selector: #selector(reachabilityChanged),
name: ReachabilityChangedNotification,
object: nil)
}
// meant to be overriden
@objc func didInternetBecomeReachable() {}
@objc func didInternetBecomeUnreachable() {}
Copy of DI-MVVM-C
By Thanos Theodoridis
Copy of DI-MVVM-C
- 236