Core Bluetooth CBAdvertisementDataServiceDataKey could have some custom data? - swift

im trying to send some data between and android and iOS device with BLE, but I need to send some custom information on the side of iOS to android , I already try almost all the keys that the CBCentralManager provides , but I try to send some string with the key CBAdvertisementDataServiceDataKey but nothing happens,anyone to know how to send information in that key?
in advance I try to have that information without connection.
this is the function that I use to try to make that part that I explain before.
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn{
let data = CBMutableCharacteristic(type: hardCodeUUID, properties: [.read], value: userName.data(using: .utf8), permissions: [.readable])
peripherialManager.startAdvertising([ CBAdvertisementDataLocalNameKey:"Jael2522",CBAdvertisementDataServiceDataKey:userName.data(using: .utf8),CBAdvertisementDataServiceDataKey:[CBUUID(string: "Jose")]])
let serialService = CBMutableService(type: hardCodeUUID, primary: true)
serialService.characteristics = [data]
peripherialManager.add(serialService)
}
}

CBAdvertisementDataServiceDataKey is readonly in iOS. The only things you can directly advertise are the local name and the list of service UUIDs (neither of which are guaranteed to be advertised, though they will be when your app is in the foreground if they can fit in the packet).
In order to reliably send data between the devices, you need one side to actually connect and read or write to characteristics.

Related

Why Firebase doesn't send an updated value for remote config flag until its time interval has been passed in an iOS project?

In an iOS project, we decided to turn on a feature as a demo purposing scenario to only some white listed users to implement this, after user logs in to the app in LoginController we send user id to Firebase by using this
Analytics.setUserProperty(id, forName: "ID")
and we have setup a remote config flag with multiple conditions which each condition is checking a particular user id if that's equal with the user id sending after login process to Firebase, it returns true otherwise false.
Exactly after we send user id in LoginController we try to fetch the remote config flag
RCUtility.fetchAVFlagValue { result in
UserDefaults.standard.set(result, forKey: DefaultKeys.remoteConfigFlag)
}
and here is the RCUtility class
class RCUtility {
static func fetchAVFlagValue(completion: #escaping (_ flag: Bool) -> Void) {
#if DEBUG
let fetchDuration: TimeInterval = 0
activateDebugMode()
#else
let fetchDuration: TimeInterval = 3600
#endif
let remoteConfig = RemoteConfig.remoteConfig()
remoteConfig.fetch(withExpirationDuration: fetchDuration) { _, error in
if let error = error {
print("Error fetching remote values: \(error)")
return
}
remoteConfig.activateFetched()
completion(remoteConfig
.configValue(forKey: "auto_vision_flag")
.boolValue)
}
}
static func activateDebugMode() {
let debugSettings = RemoteConfigSettings(developerModeEnabled: true)
RemoteConfig.remoteConfig().configSettings = debugSettings
}
}
the problem is as long as I test it in debug mode which time interval is 0 it works properly when for example I login as a whitelisted user I see that specific feature and when I logout and login as another none whitelisted users I won't see the feature but when we publish the app for QAs this isn't happening if they login as a good user firstly they see the feature but if they logout and login again as a bad user they can still see the feature which they suppose not! unless they wait for 1 hr (the specified time interval to fetch a new value for the flag) or if they delete the app and re-install it and login again! It seems FB doesn't return updated value if these two conditions aren't met.
I am sure the flag has been setup in Firebase correctly.
I have tried different ways such as activating the flag after fetching from FB or try to reset the value from UserDefaults after user logs out of the app or save the value in a constant instead of using UserDefaults but none of them worked I am not sure is it the way FB works with remote config flag or I am missing anything?
The Android version doesn't have this issue with same implementation and same time interval and regardless of the debug or production mode or type of user they get the correct value each time from Firebase without having to delete and reinstall the app.
Remote Config's fetch mechanism probably doesn't know anything about the user, which means that it simple waits until the cache expires, which defaults to 12 hours. Also see the Firebase documentation on caching.
I'm inclined to consider this a bug, although I'm not sure if it's working as intended. Either way, it might be worth it to file a bug report.
As a workaround, you could tell fetch to request new values after you detect a fresh login by calling fetchWithExpirationDuration:completionHandler: with a very low expiration duration. Just be sure to only call this variant after a new user signs in, as too frequent calls may result in a server-side throttle being applied.
I've done some tests and I believe it's a Firebase Bug. In the first request I get the old result, and after two requests I got the update.

client-server approach using realm with swift xcode

I'm new to xcode, swift, and realm. And i have to build an IOS application for my graduation project. I have a problem on how to handle multiple clients request. my application is suppose to get requests from multiple users and i have to handle these requests in a server (start counters, a timer, or add, delete, update, etc), and my server is using the realm database. my question is how to communicate between a client and a server locally ? and can i implement the server with swift not javascript ?
If you're using the Realm Mobile Platform for your client to server interactions, you should be able to use the event handling features of the Realm Object Server to detect and respond to requests triggered by users. You can download a trial of the Professional Edition (Which should be enough for your needs as a private project.)
The code for registering an event handler looks like this (Taken from the Realm docs page)
var Realm = require('realm');
// Insert the Realm admin token here
// Linux: cat /etc/realm/admin_token.base64
// macOS: cat realm-object-server/admin_token.base64
var ADMIN_TOKEN = 'ADMIN_TOKEN';
// the URL to the Realm Object Server
var SERVER_URL = 'realm://127.0.0.1:9080';
// The regular expression you provide restricts the observed Realm files to only the subset you
// are actually interested in. This is done in a separate step to avoid the cost
// of computing the fine-grained change set if it's not necessary.
var NOTIFIER_PATH = '/^\/([0-9a-f]+)\/private$/';
// The handleChange callback is called for every observed Realm file whenever it
// has changes. It is called with a change event which contains the path, the Realm,
// a version of the Realm from before the change, and indexes indication all objects
// which were added, deleted, or modified in this change
function handleChange(changeEvent) {
// Extract the user ID from the virtual path, assuming that we're using
// a filter which only subscribes us to updates of user-scoped Realms.
var matches = changeEvent.path.match(/^\/([0-9a-f]+)\/private$/);
var userId = matches[1];
var realm = changeEvent.realm;
var coupons = realm.objects('Coupon');
var couponIndexes = changeEvent.changes.Coupon.insertions;
for (var couponIndex in couponIndexes) {
var coupon = coupons[couponIndex];
if (coupon.isValid !== undefined) {
var isValid = verifyCouponForUser(coupon, userId);
// Attention: Writes here will trigger a subsequent notification.
// Take care that this doesn't cause infinite changes!
realm.write(function() {
coupon.isValid = isValid;
});
}
}
}
// create the admin user
var adminUser = Realm.Sync.User.adminUser(adminToken);
// register the event handler callback
Realm.Sync.addListener(SERVER_URL, adminUser, NOTIFIER_PATH, 'change', handleChange);
console.log('Listening for Realm changes');
Unfortunately, there's no support for Realm and Swift on the server at this point (Unless it's a Mac server) since Realm Swift needs the Objective-C runtime to work, and this isn't available on non-Mac platforms. Node.js is the way to go. :)

Triggers in Parse Server using Swift

Recently, I was tasked to do a simple chat app for iOS, using Swift.. So, I have a parse server ready and running! All I want to know, is how to use triggers..
Let's say I have opened a conversation and I just received a new message. How can I get it, without constantly checking for new messages? I saw that cloud code is probably the way to go, but if it is so, is it practical? I mean, if I have 5000 users and they are constantly chatting, will it perform well?
Thanks in advance!
You want to use Parse LiveQuery component.
Add Live Query to your server's config:
let api = new ParseServer({
...,
liveQuery: {
classNames: ['Test', 'TestAgain']
}
});
// Initialize a LiveQuery server instance, app is the express app of your Parse Server
let httpServer = require('http').createServer(app);
httpServer.listen(port);
var parseLiveQueryServer = ParseServer.createLiveQueryServer(httpServer);
Install Parse LiveQuery library as a pod to your project (pod 'ParseLiveQuery').
Subscribe for events:
let myQuery = Message.query()!.where("user", equalTo: PFUser.currentUser()!)
let subscription: Subscription<Message> = myQuery.subscribe()
Handle events:
subscription.handleEvent { query, event in
// Handle event
// This callback gets called every time an object is created, updated, deleted etc.
}

Building user database model in Firebase

so I already finished all of the actual app for this. I just need to setup the backend. I figured Firebase was the best solution since Parse is no longer a thing. What I wanted was:
Users with profiles - These profiles can be viewed by added friends but only edited (written) to by the actual profile owner.
So I read through the Firebase Docs and still cannot really figure out how to do this. They only have 1 Swift application example that does not do anything similar and the one Obj C twitter one, will not even build. All of their docs still have println for Swift which just makes me think it is not updated frequently.
Does anyone have any good examples / tutorials of this? I keep trying to search for things but nothing is as similar enough to what I want. I am more looking on how to setup the db for each user and access it rather actually using Firebase in Swift.
As I wrote in my comment to your question, this answer is based on what we do in a real social app Impether using Swift + Firebase.
Data structure
Let's assume that you want to store the following information for a single user:
email
username
name
followers - number of people who follow a particular user
following - number of people who a particular user follows
avatar_url - url of their avatar
bio - some additional text
Since in Firebase everything is stored a JSON objects, you can store the above structure under node with path like users/$userId, where $userId is Firebase User UID which is created for each registered user if you use simple email/password Firebase authorization.
Firebase email/password authorization is described in their docs:
https://www.firebase.com/docs/ios/guide/user-auth.html
https://www.firebase.com/docs/ios/guide/login/password.html
Notice that there are both Obj-C and Swift snippets. I find Firebase documentation really great as it helped me a lot when I was building our app.
For the purpose of this answer let's assume that we have user with username jack and Firebase User UID equal to jack_uid (in reality this will be a string generated by Firebase).
Then an example data for this user will be store under a path users/jack_uid and can look like this:
{
"email" : "jack#example.com",
"username" : "jack",
"name" : "Jack",
"followers" : 8,
"following" : 11,
"avatar_url" : "http://yourstoragesystem.com/avatars/jack.jpg",
"bio" : "Blogger, YouTuber",
}
Firebase email/password authorization works really well, but let's be honest, if user wants to sign in into the app, it's a lot better for him to use his username than his email he gave while he registering his account.
In order to do that, we decided to store a mapping from usernames to user ids. The idea is that if user inputs his username and password in a login form, we use that mapping to retrieve his user id and then we try to sign him in using his user id and provided password.
The mapping can be stored for example under a path username_to_uid and looks like this:
{
"sample_username_1": "firebase_generated_userid_1",
"sample_username_2": "firebase_generated_userid_2",
...
"jack": "jack_uid",
"sample_username_123": "firebase_generated_userid_123"
}
Then creating a profile may looks like this and it's done as soon as registration of a new account was successful (this snippet is very close to the exact code we use in the production):
func createProfile(uid: String, email: String,
username: String, avatarUrl: String,
successBlock: () -> Void, errorBlock: () -> Void) {
//path to user data node
let userDataPath = "/users/\(uid)"
//path to user's username to uid mapping
let usernameToUidDataPath = "/username_to_uid/\(username)"
//you want to have JSON object representing user data
//and we do use our User Swift structures to do that
//but you can just create a raw JSON object here.
//name, avatarUrl, bio, followers and following are
//initialized with default values
let user = User(uid: uid, username: username, name: "",
avatarUrl: avatarUrl, bio: "",
followers: 0, following: 0)
//this produces a JSON object from User instance
var userData = user.serialize()
//we add email to JSON data, because we don't store
//it directly in our objects
userData["email"] = email
//we use fanoutObject to update both user data
//and username to uid mapping at the same time
//this is very convinient, because either both
//write are successful or in case of any error,
//nothing is written, so you avoid inconsistencies
//in you database. You can read more about that technique
//here: https://www.firebase.com/blog/2015-10-07-how-to-keep-your-data-consistent.html
var fanoutObject = [String:AnyObject]()
fanoutObject[userDataPath] = userData
fanoutObject[usernameToUidDataPath] = uid
let ref = Firebase(url: "https://YOUR-FIREBASE-URL.firebaseio.com/images")
ref.updateChildValues(fanoutObject, withCompletionBlock: {
err, snap in
if err == nil {
//call success call back if there were no errors
successBlock()
} else {
//handle error here
errorBlock()
}
})
}
In addition to this you possibly want to store for each user a list of his followers and a separate list of users he follows. This can be done just by storing user ids at a path like followers/jack_uid, for example it can look like this:
{
"firebase_generated_userid_4": true,
"firebase_generated_userid_14": true
}
This is the way we store sets of values in our app. It very convenient, because it is really user to update it and check if some value is there.
In order to count the number of followers, we put this counter into user's data directly. This makes reading the counter very efficient. However, updating this counter requires using transactional writes and the idea is almost exactly the same as in my answer here: Upvote/Downvote system within Swift via Firebase
Read/write permissions
A part of your question is how to handle permissions to data you store. The good news is that Firebase is exceptionally good here. If you go to your Firebase dashboard there is a tab named Security&Rules and this is the place where you control permissions to your data.
What's great about Firebase rules is that they are declarative, which makes them very easy to use and maintain. However, writing rules in pure JSON is not the best idea since it's quite hard to control them when you want to combine some atomic rules into a bigger rule or your app simple grows and there are more and more different data you store in your Firebase database. Fortunately, Firebase team wrote Bolt, which is a language in which you can write all rules you need very easily.
First of all I recommend to read Firebase docs about Security, especially how does permission to a node influences permission for its children. Then, you can take a look at Bolt here:
https://www.firebase.com/docs/security/bolt/guide.html
https://www.firebase.com/blog/2015-11-09-introducing-the-bolt-compiler.html
https://github.com/firebase/bolt/blob/master/docs/guide.md
For example, we use rules for managing users data similar to this:
//global helpers
isCurrentUser(userId) {
auth != null && auth.uid == userId;
}
isLogged() {
auth != null;
}
//custom types, you can extend them
//if you want to
type UserId extends String;
type Username extends String;
type AvatarUrl extends String;
type Email extends String;
type User {
avatar_url: AvatarUrl,
bio: String,
email: Email,
followers: Number,
following: Number,
name: String,
username: Username,
}
//user data rules
path /users/{$userId} is User {
write() { isCurrentUser($userId) }
read() { isLogged() }
}
//user's followers rules
//rules for users a particular
//user follows are similar
path /followers/{$userId} {
read() { isLogged() }
}
path /followers/{$userId}/{$followerId} is Boolean {
create() { isCurrentUser($followerId) && this == true }
delete() { isCurrentUser($followerId) }
}
//username to uid rules
path /username_to_uid {
read() { true }
}
path /username_to_uid/{$username} is UserId {
create() { isCurrentUser(this) }
}
The bottom line is that you write rules you want using Bolt, then you compile them into JSON using Bolt compiler and then you deploy them into your Firebase, using command line tools or by pasting them into dashboard, but command line is way more efficient. A nice additional feature is that you can test your rules by using tools in Simulator tab in your dashboard.
Summary
For me Firebase is a great tool for implementing a system you want. However, I recommend to start with simple features and learn how to use Firebase in the first place. Implementing social app with functionality like for example Instagram is quite a big challenge, especially if you want to do it right :) It's very tempting to put all functionality there very quickly and Firebase makes it relatively easy to do, but I recommend to be patient here.
In addition, take your time and invest in writing tools. For example, we have two separated Firebase databases, one for production and second for testing, which is really important if you want to write unit and UI tests efficiently.
Also, I recommend building permission rules from the beginning. Adding them later may be tempting, but also quite overwhelming.
Last but not least, follow Firebase blog. They post regularly and you can be up to date with their latest features and updates - this is how I learnt how to use concurrent writes using fanout technique.

iTunes api, lookup by bundle ID?

Is there any way to use the iTunes API to lookup information based on the unique app bundleId? I have an iphone app, I want to use the API to check to see if the user has the newest version. Using the search sucks, cuz its fuzzy (can return lots of results). I'd prefer not to have to iterate over the result set looking for my bundleId..
I'm not looking for a way to do this on the device side (not objective c). I'd like to make server side code on my server that hides when/if apple makes API change.
Apple has changed their API, and removed the language code from the URL, so you should only the bundleId for the app you are looking for.
For example:
http://itunes.apple.com/lookup?bundleId=com.yelp.yelpiphone
In addition, you can add the country parameter to the query, to get results for a specific country App Store.
For example:
http://itunes.apple.com/lookup?bundleId=com.yelp.yelpiphone&country=de
The description, user rating and other fields might change between different App Store countries.
Turns out you can use the 'Apple ID' instead of the bundle ID as it is also unique per app. The 'Apple ID' maps to 'trackId' in http://itunes.apple.com/lookup service.
You can use a bundle id with Search API. Just replace id with bundleId, like following:
http://itunes.apple.com/lookup?bundleId=com.facebook.Facebook
EDIT:
As #Roman Blachman stated, the country code logic has changed. If you want to limit your results to a specific country, use the following example.
http://itunes.apple.com/lookup?bundleId=com.facebook.Facebook&country=us
You can use the library, iVersion, to see if the user has the latest version of the app from within the app.
iVersion
Library for dynamically checking for updates to Mac/iPhone App Store
apps from within the application and notifying users about the new
release. Can also notify users about new features in the app the first
time they launch after an upgrade.
https://github.com/nicklockwood/iVersion
Thanks to all above answers. Here is code in swift 4.2
guard let info = Bundle.main.infoDictionary,
let identifier = info["CFBundleIdentifier"] as? String,
let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else { return }
do {
let data = try Data(contentsOf: url)
guard let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] else {
return
}
if let result = (json["results"] as? [Any])?.first as? [String: Any],
let version = result["version"] as? String {
print("version in app store", version)
}
} catch let erro as NSError {
print(erro.description)
}

Categories