Firebase iOS SDK - Count connected users - ios

I'm using the Firebase iOS SDK to build a chat system that will let my users to connect to some random "rooms" where they can chat together. Inside the room I want to display to them the total number of people connected at the moment. The problem is that I don't know how to do that count. The number of users connected should be updated on connects and disconnects of particular users. I don't know where to start and what to do.

This is simple :)
Whenever a user authenticates/joins a room save them to the list of active users.
Swift
let ref = Firebase(url: "<your-firebase-db>")
ref.observeAuthEventWithBlock { authData in
if authData != nil {
// 1 - Get the ref
let activeUsersRef = Firebase(url: '<your-firebase-db>/activeUsers')
// 2 - Create a unique ref
let singleUserRef = activeUsersRef.childByAutoId()
// 3 - Add them to the list of online users
singleUserRef.setValue(authData.providerData["email"])
// 4 - When they drop their connection, remove them
singleUserRef.onDisconnectRemoveValue()
}
}
Objective-C
Firebase *ref = [[Firebase alloc] initWithUrl: #"<your-firebase-db>"];
[ref observeAuthEventWithBlock: ^(FAuthData *authData) {
Firebase *activeUsersRef = [[Firebase alloc] initWithUrl: #"<your-firebase-db>/activeUsers"];
Firebase *singleUserRef = [activeUsersRef childByAutoId];
[singleUserRef setValue: #"Whatever-the-key-is"];
[singleUserRef onDisconnectRemoveValue];
}];
The snippet above will maintain a list of active users.
All you need to do now is display the count.
Swift
// Listen to the same ref as above
let activeUsersRef = Firebase(url: 'firebase-db.firebaseio.com/activeUsers')
activeUsersRef.observeEventType(.Value, withBlock: { (snapshot: FDataSnapshot!) in
var count = 0
// if the snapshot exists, get the children
if snapshot.exists() {
count = snapshot.childrenCount
}
})
Objective-C
Firebase *activeUsersRef = [[Firebase alloc] initWithUrl: #"<your-firebase-db>/activeUsers"];
[activeUsersRef observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
NSUInteger count = 0;
if ([snapshot exists]) {
count = snapshot.childrenCount;
}
}];

Related

From Airwatch SDK, how to I get the current logged in username/certificate

Airwatch documentation is gawdawful. So bear with me.
From Airwatch SDK, how to I get the currently logged in username/user certificate?
It's in the API
//setup authorization profile
var client = new RestClient("https://enterprise.telstra.com/api/mdm");
client.Authenticator=new HttpBasicAuthenticator("#####", "#######");
var request = new RestRequest("devices/search", DataFormat.Json);
request.AddHeader("aw-tenant-code", "##########");
//get the data as json
var response = client.Get(request);
if (response.IsSuccessful)
{
//serialize the data into the Devices[] array RootObject
RootObject ro = new RootObject();
ro = JsonConvert.DeserializeObject<RootObject>(response.Content);
//create a new DataTable with columns specifically required
DataTable dt = new DataTable("tblDevices");
dt.Columns.Add("AssetNumber");
dt.Columns.Add("DeviceFriendlyName");
dt.Columns.Add("IMEI");
dt.Columns.Add("MacAddress");
dt.Columns.Add("Model");
dt.Columns.Add("DeviceType");
dt.Columns.Add("OEMinfo");
dt.Columns.Add("Platform");
dt.Columns.Add("SerialNumber");
dt.Columns.Add("UserEmailAddress");
dt.Columns.Add("UserName");
//iterate through each device data and add it into a DataRow
foreach(Device d in ro.Devices)
{
DataRow dr = dt.NewRow();
dr["UserName"] = d.UserName.ToUpper(); //uppercase the userid
//add the row to the table
dt.Rows.Add(dr);
}
With godly help from the client
AWController.clientInstance().retrieveStoredPublicCertificates { payload, error in
if let _ = error {
return
}
guard let payload = payload as? [String : [PKCS12Certificate]] else {
return
}
processCertificates(payload)
NotificationCenter.default.post(name: .awSDKCeritificatesReceived,
object: payload)
}

Import big set in Core data with relations in batches

I'm trying to import a large data set of around 80k objects. I'm trying to follow Apple example
I have two issues:
In the code example there is a comment:
// taskContext.performAndWait runs on the URLSession's delegate queue
// so it won’t block the main thread.
But in my case I'm not using URLSession to fetch the JSON. The file is bundled with the app. In this case how to make sure the import won’t block the main thread. Should I create a custom queue ? Any example ?
In the example it's just importing an array of entities. But in my case I need to import just one entity that has 70k object in a relation to many.
So what I want to achieve is:
If there is a ContactBook don't import anything because we have already imported the JSON.
If there is no ContactBook create one and import all the 70k Contact object to the contacts relation of the ContactBook. This should happen in batches like in the example and should not block the UI.
What I have tried:
private func insertContactbookIfNeeded() {
let fetch: NSFetchRequest<Contactbook> = ContactBook.fetchRequest()
let contactBookCount = (try? context.count(for: fetch)) ?? 0
if contactBookCount > 0 {
return
}
let contacts = Bundle.main.decode([ContactJSON].self, from: "contacts.json")
// Process records in batches to avoid a high memory footprint.
let batchSize = 256
let count = contacts.count
// Determine the total number of batches.
var numBatches = count / batchSize
numBatches += count % batchSize > 0 ? 1 : 0
for batchNumber in 0 ..< numBatches {
// Determine the range for this batch.
let batchStart = batchNumber * batchSize
let batchEnd = batchStart + min(batchSize, count - batchNumber * batchSize)
let range = batchStart..<batchEnd
// Create a batch for this range from the decoded JSON.
let contactsBatch = Array(contacts[range])
// Stop the entire import if any batch is unsuccessful.
if !importOneBatch(contactsBatch) {
assertionFailure("Could not import batch number \(batchNumber) range \(range)")
return
}
}
}
private func importOneBatch(_ contactsBatch: [ContactJSON]) -> Bool {
var success = false
// Create a private queue context.
let taskContext = container.newBackgroundContext()
taskContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
// NOT TRUE IN MY CASE: (Any suggestion ??)
// taskContext.performAndWait runs on the URLSession's delegate queue
// so it won’t block the main thread.
print("isMainThread: \(Thread.isMainThread)") // prints true
taskContext.performAndWait {
let fetchRequest: NSFetchRequest<ContactBook> = ContactBook.fetchRequest()
fetchRequest.returnsObjectsAsFaults = true
fetchRequest.includesSubentities = false
let contactBookCount = (try? taskContext.count(for: fetchRequest)) ?? 0
var contactBook: ContactBook?
if contactBookCount > 0 {
do {
contactBook = try taskContext.fetch(fetchRequest).first
} catch let error as NSError {
assertionFailure("can't fetch the contactBook \(error)")
}
} else {
contactBook = ContactBook(context: taskContext)
}
guard let book = contactBook else {
assertionFailure("Could not fetch the contactBook")
return
}
// Create a new record for each contact in the batch.
for contactJSON in contactsBatch {
// Create a Contact managed object on the private queue context.
let contact = Contact(context: taskContext)
// Populate the Contact's properties using the raw data.
contact.name = contactJSON.name
contact.subContacts = NSSet(array: contactJSON.subContacts { subC -> Contact in
let contact = Contact(context: taskContext)
contact.name = subC.name
})
book.addToContacts(contact)
}
// Save all insertions and deletions from the context to the store.
if taskContext.hasChanges {
do {
try taskContext.save()
} catch {
print("Error: \(error)\nCould not save Core Data context.")
return
}
// Reset the taskContext to free the cache and lower the memory footprint.
taskContext.reset()
}
success = true
}
return success
}
The problem is that this is very slow because in each batch I fetch the workbook (which is getting bigger in each iteration) to be able to insert new batch of contacts in the contact book. Is there a efficient way to avoid fetching the workbook in each batch ? also any suggestion to make this is faster ? increase the batch size ? create a background queue ?
Update:
I have tried to create a ContactBook once in insertWordbookIfNeeded and pass it to importOneBatch with each iteration but I get:
Thread 1: Exception: "Illegal attempt to establish a relationship
'contactBook' between objects in different contexts

Retrieving data in firebase

I am a beginner in firebase and in iOS. I am trying to change a project from parse to firebase.I successfully created and updated the firebase database. But while trying to retrieve data from firebase its just not working for me.
Here's the code I used.
self.ref = [[FIRDatabase database] reference];
NSString *userID = [FIRAuth auth].currentUser.uid;
[[[ref child:#"users"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSLog(#"%#",snapshot.value);
// Get user value
}withCancelBlock:^(NSError * _Nonnull error) {
NSLog(#"%#", error.localizedDescription);
}];
The control just skips the whole block. I can't figure out the problem.
The firebase guide is very hard to understand. Please tell me how to get values to a dictionary from a firebase child.
DatabaseReference database =
FirebaseDatabase.getInstance().getReference();
database.child("notes").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List notes = new ArrayList<>();
for (DataSnapshot noteDataSnapshot : dataSnapshot.getChildren()) {
Note note = noteDataSnapshot.getValue(Note.class);
notes.add(note);
}
adapter.updateList(notes);
}
...
});

Authorize.net TransactionResponse error in iOS

I have created the testing transaction request using purchaseWithRequest, always i am getting error
Transactions of this market type cannot be processed on this system
My Code:
- (void) createTransaction {
AuthNet *an = [AuthNet getInstance];
[an setDelegate:self];
CreditCardType *creditCardType = [CreditCardType creditCardType];
creditCardType.cardNumber = #"4007000000027";
creditCardType.cardCode = #"100";
creditCardType.expirationDate = #"0215";
PaymentType *paymentType = [PaymentType paymentType];
paymentType.creditCard = creditCardType;
ExtendedAmountType *extendedAmountTypeTax = [ExtendedAmountType extendedAmountType];
extendedAmountTypeTax.amount = #"0";
extendedAmountTypeTax.name = #"Tax";
ExtendedAmountType *extendedAmountTypeShipping = [ExtendedAmountType extendedAmountType];
extendedAmountTypeShipping.amount = #"0";
extendedAmountTypeShipping.name = #"Shipping";
LineItemType *lineItem = [LineItemType lineItem];
lineItem.itemName = #"Soda";
lineItem.itemDescription = #"Soda";
lineItem.itemQuantity = #"1";
lineItem.itemPrice = #"1.00";
lineItem.itemID = #"1";
TransactionRequestType *requestType = [TransactionRequestType transactionRequest];
requestType.lineItems = [NSMutableArray arrayWithObject:lineItem];
requestType.amount = #"1.00";
requestType.payment = paymentType;
requestType.tax = extendedAmountTypeTax;
requestType.shipping = extendedAmountTypeShipping;
CreateTransactionRequest *request = [CreateTransactionRequest createTransactionRequest];
request.transactionRequest = requestType;
request.transactionType = AUTH_ONLY;
request.anetApiRequest.merchantAuthentication.mobileDeviceId =
[[Utility getDeviceID]
stringByReplacingOccurrencesOfString:#"-" withString:#"_"];
request.anetApiRequest.merchantAuthentication.sessionToken = sessionToken;
[an purchaseWithRequest:request];
}
Which give call back to this delegate method,
- (void) requestFailed:(AuthNetResponse *)response {
// Handle a failed request
// getting callback to this method
}
- (void) connectionFailed:(AuthNetResponse *)response {
// Handle a failed connection
}
- (void) paymentSucceeded:(CreateTransactionResponse *) response {
// Handle payment success
}
Note: my mobileDeviceRegistrationSucceeded and mobileDeviceLoginSucceeded, only purchaseWithRequestFailed
TransactionResponse.errors = (
"Error.errorCode = 87\nError.errorText = Transactions of this market type cannot be processed on this system.\n"
)
A Market Type Error (Error 87)
Error Text: (87) Transactions of this market type cannot be processed on this system.
What It Means
This error indicates that the account that you are using was created for Card Present (retail) transactions, but you are trying to integrate to our Card Not Present (e-commerce) APIs or vice versa. The only way to resolve this is to open a new test account with the correct market type. If the error is occurring with a live account, the merchant will need to call Customer Support to see how to get the correct account.
In the coming months, Authorize.Net will be supporting blended accounts that support both Card Present and Card Not Present transactions, which will eliminate this error. When these accounts are available, we’ll post an announcement in the News and Announcements board.

How does Square's CardCase app automatically populate the user's details from the address book?

Square's new card case iOS app has a "Create Account" feature. Tap it and it shows a form PREPOPULATED with the user's entry from the Address book.
How is this possible? Anyone know? I thought this was unpossible, to get the user's info this way. It's not an iOS 5.0 thing, afaict.
the only solution I could come up with was using the device name and then searching the addressbook for a match. this assumes someone would use a particular naming convention. I for example use 'Nik's iPhone' as my device name. I am also the only Nik in my addressbook, so for my scenario is works well to use the text prior to 's as the owner name.
It makes use of the very handy wrapper for ABAddressBook by Erica Sadun, ABContactHelper.
I've left the enumeration code in instead of using array index at 0 as likely a small number of matches will be returned so you could expand to give the user an option to choose 'their' details. Which whilst not exactly matching the square case solution works well. imho.
NSString *firstname;
NSString *lastname;
NSString *ownerName = [[UIDevice currentDevice] name];
NSRange t = [ownerName rangeOfString:#"'s"];
if (t.location != NSNotFound) {
ownerName = [ownerName substringToIndex:t.location];
}
NSArray *matches = [ABContactsHelper contactsMatchingName:ownerName];
if(matches.count == 1){
for (ABContact *contact in matches){
firstname = [contact firstname];
lastname = [contact lastname];
}
}
Since the changes that require apps to ask for permissions to access the address book this method no longer works. Nik Burns' answer worked great, but it needs access to the address book.
I downloaded the Square Wallet (which is the successor to Square Card Case references in the original question) and it no longer pre-populates the sign up form. Path also used to do the device name address book trick but it also no longer pre-populates the sign up form.
Using the above method, you could still pre-populate the sign up form after requesting Contacts access but it loses the desired "magic" experience.
Since I was not happy with having to use the device name I chose to use the first record of the address book which corresponds to "ME"
ABAddressBookRef addressBook = ABAddressBookCreate( );
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
ABRecordRef ref = CFArrayGetValueAtIndex( allPeople, 0 );
ABContact * contact = [ABContact contactWithRecord:ref];
No you can
http://developer.apple.com/library/ios/#documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Introduction.html#//apple_ref/doc/uid/TP40007744
I know it was available in 4.X; not just a 5.0 feature... Not sure if it was available earlier.
I had the same issue, but in Swift, and used Nik Burns suggestion and wrote a blog post about it: http://paulpeelen.com/2016/01/20/prefill-an-signup-form-in-ios-using-swift-and-cncontact/
This is basically the end result in Swift:
import Contacts
...
#IBOutlet weak var nameFirst: UILabel!
#IBOutlet weak var nameLast: UILabel!
#IBOutlet weak var email: UILabel!
#IBOutlet weak var phoneNo: UILabel!
#IBOutlet weak var address: UILabel!
/**
Search the addressbook for the contact
*/
private func getMe() {
let ownerName = getOwnerName()
let store = CNContactStore()
do {
// Ask permission from the addressbook and search for the user using the owners name
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName(ownerName), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactPostalAddressesKey, CNContactEmailAddressesKey])
//assuming contain at least one contact
if let contact = contacts.first {
// Checking if phone number is available for the given contact.
if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) {
// Populate the fields
populateUsingContact(contact)
} else {
//Refetch the keys
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactPostalAddressesKey, CNContactEmailAddressesKey]
let refetchedContact = try store.unifiedContactWithIdentifier(contact.identifier, keysToFetch: keysToFetch)
// Populate the fields
populateUsingContact(refetchedContact)
}
}
} catch let e as NSError {
print(e.localizedDescription)
}
}
/**
Get the device owners name
- returns: The owners name
*/
private func getOwnerName() -> String {
// Get the device owners name
var ownerName = UIDevice.currentDevice().name.trimmedString.stringByReplacingOccurrencesOfString("'", withString: "")
// Get the model of the device
let model = UIDevice.currentDevice().model
// Remove the device name from the owners name
if let t = ownerName.rangeOfString("s \(model)") {
ownerName = ownerName.substringToIndex(t.startIndex)
}
return ownerName.trimmedString
}
/**
Populate the fields using a contact
- parameter contact: The contact to use
*/
private func populateUsingContact(contact: CNContact) {
// Set the name fields
nameFirst.text = contact.givenName
nameLast.text = contact.familyName
// Check if there is an address available, it might be empty
if (contact.isKeyAvailable(CNContactPostalAddressesKey)) {
if let
addrv = contact.postalAddresses.first,
addr = addrv.value as? CNPostalAddress where addrv.value is CNPostalAddress
{
let address = "\(addr.street)\n\(addr.postalCode) \(addr.city)\n\(addr.country)"
self.address.text = address
}
}
// Check if there is a phonenumber available, it might be empty
if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) {
if let
phonenumberValue = contact.phoneNumbers.first,
pn = phonenumberValue.value as? CNPhoneNumber where phonenumberValue.value is CNPhoneNumber
{
phoneNo.text = pn.stringValue
}
}
}
And the extension:
extension String {
/// Trim a string
var trimmedString: String {
return stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
}
}

Resources