Git Product home page Git Product logo

stone's Introduction

Stone

Carthage compatible

A Swift framework for connecting to Phoenix Channels in your iOS app.

Why Build This?

There are already a few options available for working with Phoenix Channels, such as ObjCPhoenixClient and SwiftPhoenixClient. Both of these are great, and in fact, Stone took a lot of inspiration from the design of ObjCPhoenixClient. But I felt both of these could be made to take advantage of the additional type safety that Swift provides. Because of this, Stone attempts to provide overloads for using custom enums in place of Strings for things like Channel topics and event handlers. It also makes use of Result<T> enums for things like callbacks for events. Additionally, Stone has built in support for Presence tracking (added in Phoenix 1.2).

Why Call it Stone?

Well, as we all know, Swift and Phoenix are both named after birds. You know, two birds, one Stone?...

Why Should I Use Stone?

You should use Stone if you've looking to connect to Channels on your Phoenix server, and are okay with using a fairly opinionated framework that itself has a few dependencies. Stone depends on SwiftWebSocket for Web Socket communication, as well as Wrap and Unbox for JSON serialization/deserialization. I built this framework for a project I'm working on myself, but if you find it helpful then by all means use it!

Working With Sockets

To create a Socket, simply initialize an instance of the provided Socket class.

guard let url = NSURL(string: "ws://localhost:4000/socket"),
  socket = Socket(url: url, heartbeatInterval: 15.0) else {
    return
}

Sockets were build to require minimal customization, but you can change the following settings if desired.

socket.shouldReconnectOnError = false // Default is true
socket.shouldAutoJoinChannels = false // Default is true
socket.reconnectInterval = 10.0 // Default is 5.0 (units are seconds)

To receive callbacks for any Socket level events, you can utilize the following hooks.

socket.onOpen = {
  print("socket open")
}

socket.onMessage = { (result: Result<Message>) in
  // Will print every single message event that comes over the socket.
  // print("received message: \(result)")
}

socket.onError = { (error: NSError) in
  print("error received: \(error)")
}

socket.onClose = { (code: Int, reason: String, clean: Bool) in
  print("socket closed - code: \(code), reason: \(reason), clean: \(clean)")
}

After your Socket is set up, you can optionally provide its connect method with parameters to be included in the URL's query when a connection is made. The below example uses Array<NSURLQueryItem>, but there is another overload available that takes Dictionary<QueryStringConvertible, QueryStringConvertible> to force all parameters to provide an implementation of QueryStringConvertible to escape themselves for a query string.

Since stone provides a default implementation of this protocol for String, you can make use of the toQueryItems() instance method attached to all Dictionary<String, String>s to convert them into Array<NSURLQueryItem>.

let params = [NSURLQueryItem(name: "user_id", value: "iPhone")]
socket.connect(params)

When you're done with a socket, you can call socket.disconnect(), to disconnect from the server.

If you want to reconnect, using the parameters supplied during the first connection, simply call socket.reconnect().

Working With Channels

Channels are defined on a Socket by Socket basis, and are considered to be unique by their topic. To create a Channel, all you have to do is initialize one, passing the topic as input. This topic can either be a String, or an Enum whose RawType is String.

let channel = Channel(topic: MyTopics.Lobby)

Once you've created a channel, you can use its onEvent method to get callbacks when different events occur on the given Channel.

channel.onEvent(Event.Custom("new:msg")) { (result: Result<Message>) in
  do {
    let message: Message = try result.value()
  } catch {
    print(error)
  }
}

To stop receiving callbacks for a given Event, use offEvent().

channel.offEvent(Event.Custom("new:msg"))

If desired, Stone is capable of tracking Presence information in Channels. By default, this is disabled, but can be enabled as easily as setting the shouldTrackPresence instance variable.

channel.shouldTrackPresence = true

Tracking Presence changes can be done by setting the Presence related callbacks on your Channel.

channel.onPresenceDiff { (result: Result<PresenceDiff> in
  do {
    let diff: PresenceDiff = try result.value()
    let leaves: [PresenceChange] = diff.leaves
    let joins: [PresenceChange] = diff.joins
  } catch {
    print(error)
  }
}

channel.onPresenceState { (result: Result<Array<PresenceChange>>) in
  do {
    let connections: [PresenceChange] = try result.value()
  } catch {
    print(error)
  }
}

When you're done configuring your Channel, just add it to your Socket. If you've left socket.shouldAutoJoinChannels enabled, then you've done. If you've disabled it, you'll need to explicitly call channel.join() when you've ready to join the Channel's topic.

socket.addChannel(channel)

PresenceChange

PresenceChange objects are a simple implementation detail of Stone. They're nothing more than structs containing the name of the change, and its associated metas Dictionary. When a Presence state change occurs, you'll be given an Array of these, and when a diff occurs, you'll get a PresenceDiff object, which contains two Arrays of PresenceChanges representing the joins, and leaves that happened during this diff.

Sending Messages

Once your Socket is connected, and you've joined a Channel, creating and sending Messages is quite simple. Here's an example.

let payload = [
  "body": someTextField.text ?? "",
  "user": UIDevice.currentDevice().name
]

let message = Message(
  topic: MyTopics.Lobby,
  event: Event.Custom("new:msg"),
  payload: payload
)

channel.sendMessage(message) { (result: Result<Message>) in
  do {
    let callbackMessage = try result.value()
  } catch {
    print(error)
  }
}

Working With Events

In an attempt to keep all event handling as type safe as possible, Stone provides an Event enum to try to wrap both default and custom events. Some examples of the difference Event types that can be created are as follows.

let phoenixEvent = Event.Phoenix(.Join)
let customEvent = Event.Custom("new:msg")
let presenceEvent = Event.Presence(.Diff)

Installation (currently iOS only)

Carthage

Add the following line to your Cartfile:

github "0x7fs/Stone" ~> 0.2.2

Check Carthage's README for up to date installation instructions.

Contributing

As I stated earlier, I made this framework to support my own use, but would love for it to support yours as well. In the spirit of this, if I borked something, or you can think of a cool feature that Stone is missing, please raise an issue and I'll try to incorporate it. If you'd like to help out, pull requests are more than welcome. All I ask is that you try to keep your code style the same as is used in the rest of Stone.

License

The MIT License (MIT). See LICENSE.md for more information. SwiftWebSocket, Wrap and Unbox are all MIT licensed as well.

stone's People

Contributors

0x7fffffff avatar mickmaccallum avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

carabina

stone's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.