Active Filters: Networking

Topics

#76: Background Fetch 🔄

Topics

It's very common for an app to need to regularly check for new data. Background Fetch allows us to do just that, while running in the background.

The system will periodically wake our app up and ask it to fetch new data. Once awoken, we'll be able to run whatever code we need to check for or fetch new data.

Lastly, we'll need to tell the system what the result of our fetch was. The system will use our response to decide how frequently our app needs to fetch.

Let's start by going to the capabilities tab of our project, and turning on Background Modes. Then we'll check the box next to the Background fetch mode.

Next, the code. We'll set a minimum background fetch interval in our didFinishLaunching function.

The performFetchWithCompletionHandler function is the last piece of the puzzle. We fetch any new data that might exist, and call the provided completionHandler with an enum describing the result.

func application(application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
  application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
  return true
}

func application(application: UIApplication, 
  performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)) {

  JEDIGossipAPI.fetchStories() { (stories: [JEDIGossipStory], error: NSError?) in
    guard stories.count > 0 else { completionHandler(.NoData); return }
    guard error == nil else { completionHandler(.Failed); return }

    completionHandler(.NewData)
  }
}

Calling the completion handler will also trigger the system to take a new snapshot of our app for the multitasking interface, so we should make sure we've updated any views that need it beforehand.

To test everything, we can use the Debug > Simulate Background Fetch command in the Simulator, or create a background fetch-specific scheme like we covered in Bite #65.

Topics

#63: Multipeer Connectivity Basics 👪

Topics

The Multipeer Connectivity framework allows devices to communicate directly with one another without the need for a central server. It uses all sorts of technology under the hood: Peer-to-peer WiFi, "regular" WiFi, as well as Bluetooth. It can handle all kinds of data: streams, files, or just good 'ol NSData. Let's try it out. First we need a service type, here's ours (just a global variable):

let CrewChatServiceType = "crew-chat"

When a user wants to start a new chat session, we need to advertise it to other nearby devices, we can do that with an MCNearbyServiceAdvertiser:

let serviceAdvertiser = MCNearbyServiceAdvertiser(
  peer: localPeerID,
  discoveryInfo: nil,
  serviceType: CrewChatServiceType
)

serviceAdvertiser.delegate = self
serviceAdvertiser.startAdvertisingPeer()

Next, we need to properly respond to invitations from other peers. To do this we'll need to implement just one (very verbosely named) function. It is
advertiser(advertiser:didRecieveInvitationFromPeer:withContent:invitationHandler:).

We'll create a session, then pass it to the provided handler.

func advertiser(advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: NSData?, invitationHandler: (Bool, MCSession) -> Void) {
  let session = MCSession(
    peer: localPeerID,
    securityIdentity: nil,
    encryptionPreference: .None
  )

  session.delegate = self

  invitationHandler(true, session)
}

Lastly, we'll test things out by sending new peers a welcome message after they connect, using the session delegate's session(session:peer:didChangeState:) function:

func session(session: MCSession, peer peerID: MCPeerID, didChangeState state: MCSessionState) {
  if state == MCSessionState.Connected {
    let message = "Hello \(peerID.displayName), welcome to the chat!"
    let messageData = message.dataUsingEncoding(NSUTF8StringEncoding)!

    try! session.sendData(messageData, toPeers: [peerID], withMode: .Reliable)
  }
}
Page 2 of 2