Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Designing Swift APIs

Designing Swift APIs

It’s very common to think of APIs as something that is only relevant for packaged code, like SDKs or Frameworks, Back-End services even. But, most definitely, we are all API designers whether we like it or not - even if your code is bundled.
However, designing great APIs while balancing between ease-of-use, capabilities and the fact that different developers have varying levels of knowledge, can be very tricky.

Let’s look at some best practices and techniques that are good to keep in mind when designing various APIs in Swift, and also, how we can create APIs that are both easy to use, non-redundant, and powerful all together.

Avatar for Erez Hod

Erez Hod

August 14, 2019

Other Decks in Programming

Transcript

  1. !2

  2. • Even greater content • Reach more iOS developers from

    beginners to advanced • More meetups Where are we headed? !4
  3. How can I help? WE WANT YOU To talk about

    iOS/Swift in our upcoming meetups !5
  4. • Name: Erez Hod • Age: 31 • Weight: Noticeable

    • Personal Motto:
 “If it doesn’t support Dark Mode, then don’t use it…” • Occupations: • Casual Gamer • Sports Cars Lover • Co-Founder of Hero Apps, Ltd !6 About Me
  5. !7

  6. !8

  7. Noticeable Code Pain Points • Unclear context • Doesn’t feel

    intuitive and natural to use • Prone to other developers mistakes 10
  8. How does that help you? • Saves time & headache:

    • For developers on your team • For your successors • Improves your team’s scaling level • Improve yourself professionally 12
  9. Real World API Example struct ChatManager { func sendMessageToUser(message: Message,

    user: User, otherUser: User, messageOptions: MessageOptions) { // ... } } !15
  10. First Things First… • What does “API” mean? • Context

    & Call Sites • Nested Types • Functions Overloading • Strong Typing • Default & Optional function parameters !17
  11. First Things First… • What does “API” mean? • Context

    & Call Sites • Nested Types • Functions Overloading • Strong Typing • Default & Optional function parameters !18
  12. What does “API” mean? What if I told you, every

    time you write a non-private property or function, you are actually writing an API? !20
  13. Great API - Key Characteristics • Balancing ease-of-use & capabilities

    • The right amount of context • Feels intuitive & natural to use !21
  14. Agenda • What does “API” mean? • Context & Call

    Sites • Nested Types • Functions Overloading • Strong Typing • Default & Optional function parameters !22
  15. struct VideoLibrary { mutating func add(video: Video) { // ...

    } } Context & Call Sites !25 library.add(video: video)
  16. extension VideoLibrary { func calculateDiskSpace(_ service: CloudService) -> DiskSpace {

    // ... } } Context & Call Sites let diskSpace = chatManager.calculateDiskSpace(user.cloudService) !29
  17. func calculateDiskSpace(_ service: CloudService) -> DiskSpace { // ... }

    Context & Call Sites !30 func calculateDiskSpace(leftIn service: CloudService) -> DiskSpace { // ... }
  18. Context & Call Sites !31 extension VideoLibrary { func calculateDiskSpace(leftIn

    service: CloudService) -> DiskSpace } chatManager.calculateDiskSpace(leftIn: user.cloudService)
  19. Context & Call Sites
 Summary • Context is important •

    Add/omit function labels wisely • The call site is what really matters !32
  20. Agenda • What does “API” mean? • Context & Call

    Sites • Nested Types • Functions Overloading • Strong Typing • Default & Optional function parameters !33
  21. Nested Types !35 struct Album { let name: String let

    videos: [Video] } struct Video { let name: String let author: Author }
  22. Nested Types !37 extension Video { struct Album { let

    name: String let videos: [Video] } }
  23. Nested Types !38 extension Video { struct Album { let

    name: String let videos: [Video] } } let album = Video.Album()
  24. Agenda • What does “API” mean? • Context & Call

    Sites • Nested Types • Functions Overloading • Strong Typing • Default & Optional function parameters !39
  25. Functions Overloading !41 struct VideoLibrary { mutating func add(_ video:

    Video) { // ... } mutating func add(_ album: Video.Album) { album.videos.forEach { add($0) } } }
  26. Functions Overloading !42 struct VideoLibrary { mutating func add(_ video:

    Video) { // ... } mutating func add(_ album: Video.Album) { album.videos.forEach { add($0) } } }
  27. Functions Overloading !43 struct VideoLibrary { mutating func add(_ video:

    Video) { // ... } mutating func add(_ album: Video.Album) { album.videos.forEach { add($0) } } }
  28. Functions Overloading !44 struct VideoLibrary { mutating func add(_ video:

    Video) { // ... } mutating func add(_ album: Video.Album) { album.videos.forEach { add($0) } } } library.add(video) library.add(album)
  29. Agenda • What does “API” mean? • Context & Call

    Sites • Nested Types • Functions Overloading • Strong Typing • Default & Optional function parameters !45
  30. Strong Typing extension VideoLibrary { mutating func update(author: String, for

    video: Video) { // ... } } library.update(author: user.name, for: video) !47
  31. Strong Typing extension VideoLibrary { mutating func update(author: String, for

    video: Video) { // ... } } library.update(author: user.name, for: video) !48 struct Author { let name: String }
  32. Strong Typing extension VideoLibrary { mutating func update(author: Author, for

    video: Video) { // ... } } library.update(author: user.name, for: video) !49 struct Author { let name: String }
  33. Strong Typing extension VideoLibrary { mutating func update(_ author: Author,

    for video: Video) { // ... } } library.update(author: user.name, for: video) !50 struct Author { let name: String }
  34. Strong Typing extension VideoLibrary { mutating func update(_ author: Author,

    for video: Video) { // ... } } library.update(Author(name: "Steven Spielberg"), for: video) !51 struct Author { let name: String }
  35. Agenda • What does “API” mean? • Context & Call

    Sites • Nested Types • Functions Overloading • Strong Typing • Default & Optional function parameters !53
  36. struct Networking { func request(url: URL, method: HTTPMethod, headers: Dictionary<String,

    String>, parameters: Array<URLQueryItem>, body: Dictionary<String, Any>) { // ... } } !55 Default & Optional Parameters
  37. struct HTTPHeader { let key: String let value: String }

    let url = URL(string: "https://my-back-end.com/api/v1/login")! let headers = [ HTTPHeader(key: "Content-Type", value: "application/json"), HTTPHeader(key: "Accept-Language", value: user.language) ] let body = [ HTTPBodyItem(key: "email", value: "[email protected]"), HTTPBodyItem(key: "password", value: "awesomePassword") ] !56 struct HTTPBodyItem { let key: String let value: Any } Default & Optional Parameters
  38. Default & Optional Parameters struct Networking { func request(url: URL,

    method: HTTPMethod, headers: Dictionary<String, String>, parameters: Array<URLQueryItem>, body: Dictionary<String, Any>) { // ... } } !57
  39. Default & Optional Parameters struct Networking { func request(url: URL,

    method: HTTPMethod, headers: Dictionary<String, String>, parameters: Array<URLQueryItem>, body: Dictionary<String, Any>) { // ... } } !58 struct Networking { func request(_ url: URL, method: HTTPMethod = .get, headers: Array<HTTPHeader>? = nil, parameters: Array<URLQueryItem>? = nil, body: Array<HTTPBodyItem>? = nil) { // ... } }
  40. Default & Optional Parameters !59 struct Networking { func request(_

    url: URL, method: HTTPMethod = .get, headers: Array<HTTPHeader>? = nil, parameters: Array<URLQueryItem>? = nil, body: Array<HTTPBodyItem>? = nil) { // ... } }
  41. Default & Optional Parameters !60 struct Networking { func request(_

    url: URL, method: HTTPMethod = .get, headers: Array<HTTPHeader>? = nil, parameters: Array<URLQueryItem>? = nil, body: Array<HTTPBodyItem>? = nil) { // ... } }
  42. Default & Optional Parameters struct Networking { func request(_ url:

    URL, method: HTTPMethod = .get, headers: Array<HTTPHeader>? = nil, parameters: Array<URLQueryItem>? = nil, body: Array<HTTPBodyItem>? = nil) { // ... } } networking.request(url) // Default GET request networking.request(url, headers: headers) // GET request with headers networking.request(url, method: .post, headers: headers, body: body) // POST request with headers & body !61
  43. Agenda • What does “API” mean? • Context & Call

    Sites • Nested Types • Functions Overloading • Strong Typing • Default & Optional function parameters !62
  44. Remember This? struct ChatManager { func sendMessageToUser(message: Message, user: User,

    otherUser: User, messageOptions: MessageOptions) { // ... } } chatManager.sendMessageToUser(message: message, user: user1, otherUser: user2, messageOptions: options) !64
  45. Remember This? struct ChatManager { func sendMessageToUser(message: Message, user: User,

    otherUser: User, messageOptions: MessageOptions) { // ... } } !65 struct ChatManager { func send(_ message: Message, from currentUser: User, to destinationUser: User, with options: Message.Options? = nil) { // ... } }
  46. Remember This? !66 struct ChatManager { func send(_ message: Message,

    from currentUser: User, to destinationUser: User, with options: Message.Options? = nil) { // ... } }
  47. Remember This? !67 struct ChatManager { func send(_ message: Message,

    from currentUser: User, to destinationUser: User, with options: Message.Options? = nil) { // ... } }
  48. Remember This? !68 struct ChatManager { func send(_ message: Message,

    from currentUser: User, to destinationUser: User, with options: Message.Options? = nil) { // ... } }
  49. Remember This? !69 struct ChatManager { func send(_ message: Message,

    from currentUser: User, to destinationUser: User, with options: Message.Options? = nil) { // ... } }
  50. Making it better… chatManager.sendMessageToUser(message: message, user: user1, otherUser: user2, messageOptions:

    options) !70 chatManager.send(message, from: currentUser, to: destinationUser, with: options) // We can also omit the ‘with options' parameter if we don't need it chatManager.send(message, from: currentUser, to: destinationUser)
  51. So, what have we learned? • API design is… •

    All over your code • Not an absolute science • Some great Swift tips & tricks • Making our APIs clearer and easier to use !71
  52. The $1M Question… “Have I done everything I possibly can

    to make this API as clear and easy-to-use as possible?” !73