Skip to content

pepasflo/swift

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 

Repository files navigation

Swift snippets

Some examples of Swift code.

Note: type annotations have been added for clarity, but are not required (unless noted).

typealias

typealias APIToken = String

Enumerating the keys and values in a Dictionary

for (k, v) in somedict {
    ...
}

List of Dictionary keys as Array

It turns out that Dictionary.keys does not return an Array of the keys (it returns an object of type Dictionary.Keys).

Array(somedict.keys)

Dictionary key membership

To check if a key exists in a dictionary:

if dict.keys.contains(key) {
    ...
}

Deprecation

Marking a function as deprecated:

@available(*, deprecated)
func myFunc() {}

More: https://stackoverflow.com/a/25406285/7543271

String to Data

let s: String = "hello"
let d: Data = s.data(using: .utf8)!

Data to String

let d: Data = Data(bytes: [104, 101, 108, 108, 111, 0]) // "hello"
let s: String = String(data: d, encoding: .utf8)!

Resources:

Codable (Encodable & Decodable)

Resources:

Encoding primitives:

You can't do this (yet):

let s: String = "hello"
let d: Data = try! JSONEncoder().encode(s)

Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Swift.EncodingError.invalidValue("hello", Swift.EncodingError.Context(codingPath: [], debugDescription: "Top-level String encoded as string JSON fragment.", underlyingError: nil))

The top-level object of the resulting JSON output must be a dictionary or an array (not a string, number, boolean, or nil).

However, this may be supported in the future, see:

Encoding / Decoding a basic struct:

struct Movie: Codable {
    let id: Int
    let title: String
}

Encoding:

let m: Movie = Movie(id: 1, title: "Goonies")
let d: Data = try! JSONEncoder().encode(m)

Decoding from Data:

let m2: Movie = try! JSONDecoder().decode(Movie.self, from: d)

Decoding from String:

Convert the String to Data first, then decode as above:

let s: String = """
{
    "id": 1,
    "title": "Goonies"
}
"""
let d3: Data = s.data(using: .utf8)!
let m3: Movie = try! JSONDecoder().decode(Movie.self, from: d3)

Encoding / Decoding a nested struct:

As long as the structs are all Codable, they can be nested:

struct Author: Codable {
    let name: String
}

struct Book: Codable {
    let title: String
    let author: Author
}

Encoding:

let b: Book = Book(title: "Not Always So", author: Author(name: "Shunryu Suzuki"))
let d: Data = try! JSONEncoder().encode(b)

Decoding:

let b2: Book = try! JSONDecoder().decode(Book.self, from: d)

Decoding ISO8601 dates

struct Movie: Codable {
    let title: String
    let releaseDate: Date

    enum CodingKeys: String, CodingKey {
        case title
        case releaseDate = "release_date"
    }
}

This doesn't work out of the box:

let s: String = """
{
    "title": "Groundhog Day",
    "release_date": "1993-02-12T22:47:51+0000"
}
"""

let d: Data = s.data(using: .utf8)!
let m: Movie = try! JSONDecoder().decode(Movie.self, from: d)

Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "release_date", intValue: nil)], debugDescription: "Expected to decode Double but found a string/data instead.", underlyingError: nil))

You need to set the dateDecodingStrategy on the JSONDecoder:

let d: Data = s.data(using: .utf8)!
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let m: Movie = try! decoder.decode(Movie.self, from: d)

Resources:

Codable extensions

We can write a pair of extensions to make this a bit more succinct, for the common case where we simply want an optional returned (and aren't interested in examining the exception).

struct Author: Codable {
    let name: String
}

Encoding:

extension Encodable {
    public var encoded: Data? {
        return try? JSONEncoder().encode(self)
    }
}
let a: Author = Author(name: "Mark Twain")
let d: Data = a.encoded!

Decoding:

let a2: Author = d.decoded()!

Note: the type annotation on a2 is required.

Custom key mapping

Define a CodingKeys enum which adheres to String, CodingKey:

struct Author: Codable {
    let firstName: String
    let age: Int

    enum CodingKeys: String, CodingKey {
        case firstName = "first_name"
        case age
    }
}

Encoding:

let a: Author = Author(firstName: "Mark", age: 45)
let d: Data = try! JSONEncoder().encode(a)

Check the result:

let s: String = String(data: d, encoding: .utf8)!
print(s)
{"age":45,"first_name":"Mark"}

Decoding:

let s2: String = """
{
    "first_name": "Mark",
    "age": 45
}
"""
let d2: Data = s2.data(using: .utf8)!
let a2: Author = try! JSONDecoder().decode(Author.self, from: d2)

Using a local JSON file in unit tests

struct Movie: Codable {
    let title: String
}

movie.json (make sure this file is added to your unit test target, not your main target):

{
    "title": "Groundhog Day"
}
import XCTest
@testable import CodableDemo

class MovieTests: XCTestCase {
    func testMovieDecode() {
        let b: Bundle = Bundle(for: type(of: self))
        let u: URL = b.url(forResource: "movie", withExtension: "json")!
        let d: Data = try! Data(contentsOf: u)
        let m: Movie = try! JSONDecoder().decode(Movie.self, from: d)

        XCTAssertEqual(m.title, "Groundhog Day")
    }
}

Dynamic UITableViewCell height

self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = UITableViewAutomaticDimension
  • Remove any related function definitions (e.g. heightForRowAtIndexPath).
  • Ensure your cells have a continuous chain of vertical constraints from top to bottom.

Type declaration with protocol conformance

var playableCell: UITableViewCell & Playable = ...

updateConstraints

Call to super

super.updateConstraints should be called last. Use defer:

public override func updateConstraints() {
    defer {
        super.updateConstraints()
    }

    ...
}

Performance considerations

WWDC 2018 #220 is a great resource for building an intuition around the performance impact of calling updateConstraints. TL;DR: keep this method as optimized as possible.

Programatically creating UIWindow

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
    ) -> Bool {

        let window = UIWindow(frame: UIScreen.main.bounds)
        window.makeKeyAndVisible()
        window.rootViewController = UIViewController()
        self.window = window

        return true
    }
}

Also remember to empty out the "Main Interface" field under the "General" tab of your target's settings.

Switching on the type of something:

without casting:

switch item {
case is Int:
    ...
case is Double:
    ...

with casting:

switch item {
case let i as Int:
    ...
case let d as Double:
    ...

Switching on a tuple:

Sometimes you need to switch on the cases of two enums:

enum Content {
    case article(URL)
    case video(URL)
}

let a: Content = .article(URL(string: "http://google.com")!)
let b: Content = .video(URL(string: "http://yahoo.com")!)

switch (a, b) {
case (.article(let url1), .article(let url2)):
    ...
case (.video(let url1), .video(let url2)):
    ...
default:
    ...
}

Adding a custom extension to Observable (RxSwift):

extension Observable where E == Int {
    public func foo() -> Observable<Int> {
        ...
    }
}

Importing a specific member of a framework

import struct FloSportsCore.Event

Limitations

You can't use a tuple as a dictionary key

Turns out you can't implement Hashable on a tuple. https://stackoverflow.com/a/24152255/7543271

Gotcha's

Call removeFromSuperview in addition to removeArrangedSubview

https://stackoverflow.com/a/52718219/7543271

About

Some examples of Swift code.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published