Git Product home page Git Product logo

Comments (1)

aufflick avatar aufflick commented on May 7, 2024

Hi, thanks for the question :)

A quick look at the state machine logs you pasted suggests that connect is being called more than once on the same instance.

[ERROR] No transition found for (bluetoothConnected, didConnect(<JacquardSDK.PeripheralImplementation: 0x282988940>))

What this is saying is that the current state of the tag state machine is bluetoothConnected, and yet it is being asked to handle the message didConnect -- which absent a bug in either the state machine or CoreBluetooth should only be possible if connect is called more than once.

You're callingconnect in a SwiftUI View struct init - these are simple value objects which may be (re)created more than once so it's likely that's what's happening here.

In fact when I run your code connected to a single tag I see three instances of tag connect request in the console.

There are two ways you can solve this - one is to write a SwiftUI State Object wrapper for your connection/tag logic (ie. something that conforms to ObservableObject. I've done this before and it works but feels like a bunch of boilerplate code.

Since you're targeting iOS 16 I'd suggest using the (new in iOS 15) .task SwiftUI modifier for calling async/await code for a view (SwiftUI connects very nicely with the new structured concurrency features). This means you'll need to convert the Combine publisher to an AsyncStream somehow. It's pretty easy to do that by hand (see below) but it should also be possible to write a generic bridging function.

Since you're using the tag ID connection method, something like this should work:

Create an async/await wrapper for connect

extension JacquardManager {
  func asyncConnect(_ identifier: UUID) -> AsyncThrowingStream<TagConnectionState, Error> {
    AsyncThrowingStream { continuation in
      let cancellable = self.connect(identifier)
        .sink { completion in
          switch completion {
          case .finished:
            continuation.finish()
          case .failure(let error):
            continuation.finish(throwing: error)
          }
        } receiveValue: { connectionState in
          continuation.yield(connectionState)
        }
      
      continuation.onTermination = { @Sendable _ in
        cancellable.cancel()
      }
    }
  }
}

Triggering connection and looping over the AsyncThrowingSequence

struct ConnectingView: View {
  @State private var connectedTag: ConnectedTag? = nil
  @State private var lastConnectionStateDescription = "Preparing to connect..."
  
  private var tagIdentifier: UUID
  private var jqManager: JacquardManager
  
  init(manager: JacquardManager, tagId: UUID) {
    self.tagIdentifier = tagId
    self.jqManager = manager
  }
  
  var body: some View {
    VStack {
      
      if let connectedTag = connectedTag {
        Text("Connected to \(connectedTag.displayName)")
      } else {
        Text(lastConnectionStateDescription)
      }
    }
    .task {
      do {
        let connectionStream = jqManager.asyncConnect(tagIdentifier)
        for try await connectionState in connectionStream {
          print("\(connectionState)")
          switch connectionState {
          case .connected(let tag):
            connectedTag = tag
          default:
            connectedTag = nil
            lastConnectionStateDescription = "\(connectionState)"
          }
        }
      } catch {
        print("error during connection: \(error)")
      }
    }
  }
}

Dropping this into your repository seems to work with an adhoc test, including tag reconnection (you'll want to retain and pass around the connectionStream instance not the connectedTag instance so that you handle reconnections like the Combine examples do. If you search the usual places you'll find some people have written helper functions for AsyncSequence that provide Combine/Rx like operators if that's helpful for your design.

from jacquardsdkios.

Related Issues (2)

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.