Skip to content

Instantly share code, notes, and snippets.

@cleonps
Last active July 15, 2022 14:47
Show Gist options
  • Save cleonps/89595a8b36295955234240dfc373b59a to your computer and use it in GitHub Desktop.
Save cleonps/89595a8b36295955234240dfc373b59a to your computer and use it in GitHub Desktop.
Swift Snippets

Swift-Snippets

Snippets for my iOS Projects :D

  1. File Location for:
    1. CoreData
    2. SQL
    3. User Defaults
    4. Info Plists
  2. Enums with Identifiers/Names/Keys for:
    1. User Defaults
    2. TableViewCell Names and Nib Names
    3. CollectionViewCell Names and Nib Names
    4. Segues, Storyboards and Views
    5. Color Names
    6. Image Names
    7. SystemSounds
  3. Semaphore:
    1. Semaphore
  4. Environment Variables:
    1. Secrets
  5. Configuration/AppDelegate/SceneDelegate:
    1. NavigationBar Configuration (Still Under development)
    2. Project without Storyboard: Scene Delegate
    3. .gitignore
  6. Persistence:
    1. CoreData
    2. Custom Property List
  7. SwiftUI:
    1. Preview UIKit ViewControllers
  8. MISC:
    1. NSPredicate
// Prints Core Data DB Location
print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask))
// When opening from terminal add to path: ../Library/Application Support/
// When opening from finder go up one folder then: Library/application Support/
// Prints SQL DB Location
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
print(paths[0])
// Prints UserDefaults File Location
print(NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last! as String)
// When opening from terminal add to path: ../Library/Preferences/
// When opening from finder go up one folder then: Library/Preferences/
// Prints Custom PList File Location
let dataFilePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("<#name#>.plist")
print(dataFilePath)
// Creates enum Keys for UserDefaults.
extension UserDefaults{
public enum Keys: String, CaseIterable{
// Example Key
case user
}
public func setValue(_ value: Any?, forKey key: Keys) {
setValue(value, forKey: key.rawValue)
}
public func value(forKey key: Keys) -> Any? {
return value(forKey: key.rawValue)
}
public func string(forKey key: Keys) -> String? {
return string(forKey: key.rawValue)
}
public func integer(forKey key: Keys) -> Int {
return integer(forKey: key.rawValue)
}
public func float(forKey key: Keys) -> Float {
return float(forKey: key.rawValue)
}
public func double(forKey key: Keys) -> Double {
return double(forKey: key.rawValue)
}
public func removeObject(forKey key: Keys) {
removeObject(forKey: key.rawValue)
}
/// Removes all objects created on User Defaults
public func removeAll() {
for key in Keys.allCases {
removeObject(forKey: key)
}
}
}
// Usage:
// let userDefaults = UserDefaults.standard
// userDefaults.setValue("ExampleUserName", .user)
// let _ = userDefaults.string(forKey: .user)!
// userDefaults.removeAll()
// Creates enums CellNames and NibNames for UITableViewCells.
import UIKit
extension UITableView {
public enum CellNames: String {
// Example for Cell Name
case userData = "UserData"
}
public enum NibNames: String {
// Example for Nib Name
case userData = "UserDataTableViewCell"
}
public func dequeueReusableCell(withIdentifier name: CellNames, for indexPath: IndexPath) -> UITableViewCell {
return self.dequeueReusableCell(withIdentifier: name.rawValue, for: indexPath) as UITableViewCell
}
public func register(nibName: NibNames, cellName: CellNames) {
self.register(UINib(nibName: nibName.rawValue, bundle: nil), forCellReuseIdentifier: cellName.rawValue)
}
}
// Usage:
// When registering a cell
// usersTableView.register(nibName: .userData, cellName: .userData)
// When using a cell
// let cell = tableView.dequeueReusableCell(withIdentifier: .userData, for: indexPath) as! UserDataTableViewCell
// Creates enums CellNames and NibNames for UICollectionViewCells.
import UIKit
extension UICollectionView {
public enum CellNames: String {
// Example for Cell Name
case userData = "UserData"
}
public enum NibNames: String {
// Example for Nib Name
case userData = "UserDataCollectionViewCell"
}
public func dequeueReusableCell(withReuseIdentifier name: CellNames, for indexPath: IndexPath) -> UICollectionViewCell {
return self.dequeueReusableCell(withReuseIdentifier: name.rawValue, for: indexPath) as UICollectionViewCell
}
public func register(nibName: NibNames, cellName: CellNames) {
self.register(UINib(nibName: nibName.rawValue, bundle: nil), forCellWithReuseIdentifier: cellName.rawValue)
}
}
// Usage:
// When registering a cell
// usersCollectionView.register(nibName: .userData, cellName: .userData)
// When using a cell
// let cell = tableView.dequeueReusableCell(withReuseIdentifier: .userData, for: indexPath) as! UserDataCollectionViewCell
// Creates enums Segues, Storyboards and ViewIdentifiers for segues and instantiations.
extension UIViewController {
public enum Segues: String {
case loginHome = "loginHomeSegue"
}
public enum Storyboards: String {
case main = "Main"
case profile = "Profile"
}
public enum ViewIdentifiers: String {
case login = "Login"
case home = "Home"
}
public func performSegue(withIdentifier identifier: Segues) {
performSegue(withIdentifier: identifier.rawValue, sender: nil)
}
/// Returns an instance of UIViewController subclass
///
/// - Parameters:
/// - storyboard: The *name* of the storyboard in which the view is located.
/// - identifier: The *storyboard id* property of the view.
/// - controller: The *UIViewController subclass* associated with the view.
/// - presentation: The *UIModalPresentationStyle*, default value is .fullScreen
public func instantiateVC<T: UIViewController>(storyboard: Storyboards, identifier: ViewIdentifiers, controller: T.Type, presentation: UIModalPresentationStyle? = .fullScreen) -> T {
let storyboard = UIStoryboard(name: storyboard.rawValue, bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: identifier.rawValue) as! T
viewController.modalPresentationStyle = presentation!
return viewController
}
}
// Usage:
// When preparing for segue
// override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// switch segue.identifier {
// case Segues.loginHome.rawValue:
// let destinationVC = segue.destination as! HomeViewController
// destinationVC.<#someVariable#> = <#someValue#>
// default:
// return
// }
// }
// When performing a segue
// performSegue(withIdentifier: .loginHome)
// When instantiating a view
// let vc = instantiateVC(storyboard: .main, identifier: .login, controller: LoginViewController.self, presentation: .fullScreen)
// vc.<#someVariable#> = <#someValue#>
// present(vc, animated: false)
// Creates constants for named colors
// First add the colors inside Assets.xcassets
// Named colors require iOS11+
extension UIColor {
static let completed = UIColor(named: "Completed")!
static let deleted = UIColor(named: "Deleted")!
static let loader = UIColor(named: "Loader")!
static let primary = UIColor(named: "Primary")!
static let accent = UIColor(named: "Accent")!
}
// Usage:
// UIColor.primary
// Creates constants for named images
// First add the images inside Assets.xcassets
extension UIImage {
public enum ImageName: String {
case questionMark = "QuestionMark"
case like = "Like"
case logo = "Logo"
}
convenience init?(named: ImageName) {
self.init(named: named.rawValue)
}
}
// Usage:
// UIImage(named: .questionMark)!
// Creates enum Keys for AudioToolbox.
import AudioToolbox
public enum SystemSounds: UInt32 {
case peek = 1519 // weak boom
case pop = 1520 // pop boom
case cancelled = 1521 // three weak booms
case tryAgain = 1102 // weak boom then strong boom
case vibrateTwice = 1311 // two vibrations
case vibrate1 = 1350 // vibration
case vibrate2 = 1351 // vibration
case vibrate3 = 4095 // vibration
}
public func AudioServicesPlaySystemSound(_ inSystemSoundName: SystemSounds) {
AudioToolbox.AudioServicesPlaySystemSound(inSystemSoundName.rawValue)
}
// Usage example:
// AudioServicesPlaySystemSound(.peek)
let semaphore = DispatchSemaphore(value: 0)
asyncFunc(){
// Closure body
// When async code is complete execute a signal
semaphore.signal()
}
// Continue when signal is executed
let _ = semaphore.wait(timeout: .distantFuture)
// Creates file to handle ids and secrets saved as environment variables
// Replace "Service" and "SERVICE_..." for the service name or your own naming conventions
enum Secrets {
enum Service {
static let clientId = Secrets.environmentVariable(named: "SERVICE_CLIENT_ID")
static let clientSecret = Secrets.environmentVariable(named: "SERVICE_CLIENT_SECRET")
}
fileprivate static func environmentVariable(named: String) -> String? {
let processInfo = ProcessInfo.processInfo
guard let value = processInfo.environment[named] else {
print("Missing Environment Variable: '\(named)'")
return nil
}
return value
}
}
// Setup:
// Fill the environment variable for the current target
// Product>Scheme>Edit Scheme (Make sure the correct target is selected)
// Select Run, then Arguments tab and add the environtment variables
// Usage:
// Call variables when needed and replace "Service" for the service name or your own naming conventions
// let clientId = Secrets.Service.clientId
// Example of configuration of NavBar on method didFinishLaunchingWithOptions of AppDelegate
// (This isn't the final version; currently under development. This is a special case for backItems without text)
// Safely unwrap fonts
if let titleFont = UIFont(name: "Avenir-heavy", size: 18),
let backFont = UIFont(name: "Avenir-heavy", size: 0),
let barFont = UIFont(name: "Avenir-book", size: 14) {
// Configure color and appearance
let backColor: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.clear]
let backAppearance = UIBarButtonItem.appearance()
// Each button property must be configured for at least .normal and .selected
backAppearance.setTitleTextAttributes(backColor, for: .normal)
backAppearance.setTitleTextAttributes(backColor, for: .highlighted)
backAppearance.setTitleTextAttributes(backColor, for: .selected)
backAppearance.setTitleTextAttributes([.font: backFont], for: .normal)
backAppearance.setTitleTextAttributes([.font: backFont], for: .highlighted)
backAppearance.setTitleTextAttributes([.font: backFont], for: .selected)
// Large Titles
UINavigationBar.appearance().largeTitleTextAttributes = [.font: titleFont]
UITabBarItem.appearance().setTitleTextAttributes([.font: barFont], for: .normal)
}
/*
Project without Storyboard, initial configuration:
- Remove Storyboard
- Erase project configuration pointing to main Storyboard
- Search and erase main from info.plist
SceneDelegate.swift:
- Name the scene
- Init window = UIWindow(windowScene: ...) or
- Init window = UIWindow(frame: ...)
- Then asign window.windowScene = scene
- Asign a the window?rootViewController = someViewControllerInstance
- Make key and visible window?.makeKeyAndVisible()
*/
// SceneDelegate configuration with a NavigationController as RootViewController
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let scene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: scene)
let nav = UINavigationController(rootViewController: LoginController())
window?.rootViewController = nav
window?.makeKeyAndVisible()
}
## This is my personal and simple .gitignore file for swift projects:
## Various settings
xcuserdata/
## Mac files
.DS_Store
## ---------------------------------------------------------------------
## Below is an extended version of a .gitignore for bigger projects
## This is JB's .gitignore, more info at: https://github.com/joanby/ios-12/blob/master/.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
.DS_Store
// First create a Data Model from Core Data
// Next add this code at the end of app delegate and remember to import CoreData
// Entities created from Data Model Inspector must have the value "Module: Current Product Module"
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "<#DataModelName#>")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
// Finally invoke self.saveContext() inside applicationWillTerminate to persist information in DataBase when app terminates
// Custom PList
let encoder = PropertyListEncoder()
do {
let data = try encoder.encode(self.<#objectModelName#>)
try data.write(to: self.<#dataFilePath#>)
} catch {
print("Error encoding and saving the array: \(error)")
}
// It's possible to preview UIKit ViewControllers with SwiftUI
// #1 import SwiftUI
// #2 Make your "SomeViewController" class final
// #3 Add the following and Replace the name "SomeViewController"
struct <#SomeViewController#>_Preview: PreviewProvider {
static var previews: some View {
Group {
<#SomeViewController#>()
.previewDevice(PreviewDevice(rawValue: "iPhone 11 Pro Max"))
.previewDisplayName("iPhone 11 Pro Max")
<#SomeViewController#>()
.previewDevice(PreviewDevice(rawValue: "iPhone 8"))
.previewDisplayName("iPhone 8")
}
}
}
extension <#SomeViewController#>: UIViewControllerRepresentable {
// If you have a Storyboard you may instead make an instance here using UIStoryboard and the View identifier instead of calling directly the class
func makeUIViewController(context: Context) -> UIViewController {
return <#SomeViewController#>()
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}
// Create NSPredicate
// Change field name. Example "title"
// Change findThis. Example searchBar.text!
// Remove [cd] if you need a case and diacritic sensitive predicate
let predicate = NSPredicate(format: "<#field#> CONTAINS[cd] %@", <#findThis#>)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment