How to get IPhone's Public IP Address in swift3 - swift3

For my project, i need to get the IPhone's Public IP address, there are so many examples available, which show public IP address by using external / third party URL. I just want to know how to extract IPhone's IP Address without help of using another URL.
I have following code but it gives local IP.
func getWiFiAddress() -> String? {
var address : String?
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// Check interface name:
let name = String(cString: interface.ifa_name)
if name == "en0" {
// Convert interface address to a human readable string:
var addr = interface.ifa_addr.pointee
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(&addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}

Related

BillingClient.querySkuDetails(SkuDetailsParams.builder()) not working?

Getting an empty list when fetching with querySkuDetails()?
So in case you've been having this issue lately, where you want to fetch your Google Play Console list of SkuDetail, maybe to show the price of one of the SkuDetail and show it to the user has it was in my case or to display some other information about a SkuDetail from your Google Play Console merchant account. Anyways, here's what's worked for me:
First you need to add this to your build.gradle app file:
implementation "com.android.billingclient:billing-ktx:4.0.0"
Then Inside of my Fragment's ViewModel, I did the following:
class MainViewModel(application: Application) : AndroidViewModel(application) {
private val billingClient by lazy {
BillingClient.newBuilder(application.applicationContext)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build()
}
/**
#param result Returns true if connection was successful, false if otherwise
*/
private inline fun billingStartConnection(crossinline result: (Boolean) -> Unit) {
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
result(true)
}
}
override fun onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
result(false)
}
})
}
sealed class BillingClientObserver {
object Loading : BillingClientObserver()
object ClientDisconnected : BillingClientObserver()
object HasNoPurchases : BillingClientObserver()
object HasNoAdsPrivilege : BillingClientObserver()
object UserCancelledPurchase : BillingClientObserver()
data class UnexpectedError(val debugMessage: String = "") : BillingClientObserver()
}
private val _billingClientObserver: MutableStateFlow<BillingClientObserver> =
MutableStateFlow(BillingClientObserver.Loading)
val billingClientObserver: StateFlow<BillingClientObserver> = _billingClientObserver
suspend fun checkSkuDetailById(productId: String) =
billingStartConnection { billingClientReady ->
if (billingClientReady) {
val skuList = ArrayList<String>()
skuList.add(productId)
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
viewModelScope.launch(Dispatchers.Main) {
val skuDetailList = withContext(Dispatchers.IO) {
billingClient.querySkuDetails(params.build())
}
skuDetailList.skuDetailsList?.let {
Timber.d("Timber> List<SkuDetails>: $it")
if (it.isNotEmpty()) {
val skuDetails: SkuDetails = it[0]
_goAdsFreePricing.value = skuDetails.price
} else {
_billingClientObserver.value =
BillingClientObserver.UnexpectedError(context.getString(R.string.unable_to_get_price_msg))
}
} ?: run {
_billingClientObserver.value =
BillingClientObserver.UnexpectedError(context.getString(R.string.unable_to_get_price_msg))
}
}
} else {
_billingClientObserver.value =
BillingClientObserver.UnexpectedError(context.getString(R.string.unable_connect_to_play_store))
}
}
}
The most important thing to do for the
BillingClient.querySkuDetails(SkuDetailsParams.builder())
to be successful, is to first establish a successful connection through the
BillingClient.startConnection( listener: BillingClientStateListener)
then do the query on the background thread, very important that the
querySkuDetails()
happens in the background thread as it is made to fail if done on the
#MainThread
then listen for its result on the
#MainThread
like in the above example.

Cannot fetch multiple CKReference records from public Database in a for loop

I have a contact CKRecord with many location CKRecords ( 1 to many relationship)
Both contact CKRecord and Location CKRecord are created in public Database. I add CKReference fro contact to locaiotn via a field named owningContact on location.
ckRecord["owningContact"] = CKReference(record: contactRecord!, action: .deleteSelf)
I go to cloudKit dashboard and verify both the records exist. The location CKRecord has field owningContact that has the recordName of the contact CKRecord. I defined a function to get locations like this:
private func iCloudFetchLocations(withContactCKRecord: CKRecord, completionHandler: #escaping ([CKRecord]?, Error?) -> Void) {
var records = [CKRecord]()
let recordToMatch = CKReference(recordID: withContactCKRecord.recordID, action: .deleteSelf)
let predicate = NSPredicate(format: "owningContact == %#", recordToMatch)
// Create the query object.
let query = CKQuery(recordType: "location", predicate: predicate)
let queryOp = CKQueryOperation(query: query)
queryOp.resultsLimit = 1
queryOp.qualityOfService = .userInteractive
queryOp.recordFetchedBlock = {
records.append($0)
print($0)
}
queryOp.queryCompletionBlock = { (cursor, error) in
guard error == nil else {
if let ckerror = error as? CKError {
self.aErrorHandler.handleCkError(ckerror: ckerror)
}
return
}
if (cursor != nil) {
let newOperation = CKQueryOperation(cursor: cursor!)
newOperation.resultsLimit = queryOp.resultsLimit
newOperation.recordFetchedBlock = queryOp.recordFetchedBlock
newOperation.queryCompletionBlock = queryOp.queryCompletionBlock
self.publicDB?.add(newOperation)
}
completionHandler(records, error)
}
self.publicDB?.add(queryOp)
}
Then I call the code to fetch location CKRecord based on contact CKRecord like this:
let predicate = NSPredicate(format: "TRUEPREDICATE")
let query = CKQuery(recordType: Cloud.Entity.Contact, predicate: predicate)
publicDB?.perform(query, inZoneWith: nil, completionHandler: { (records, error) in
guard error == nil else {
if let ckerror = error as? CKError {
self.aErrorHandler.handleCkError(ckerror: ckerror)
}
return
completion(false)
}
if let contactRecords = records {
for aContactRecord in contactRecords {
// fetch Location Data
self.iCloudFetchLocations(withContactCKRecord: aContactRecord, completionHandler: { records, error in
guard error == nil else {
if let ckerror = error as? CKError {
self.aErrorHandler.handleCkError(ckerror: ckerror)
}
return
completion(false)
}
if let locationRecords = records {
}
})
}
}
})
I have two contacts the first one has been CKReferenc'ed to the location, where as the second contact is still not yet CKReferenc'ed to the location.
I think here is the problem: First time in the loop contact CKRecord information is sent by calling iCloudFetchLocations which returns immediately without waiting for cloud response, and the for loop sends the second contact and calls iCloudFetchLocations again. Since the second contact has no CKReference to the location, the call fails and I can never get to the first contact's location since it hasn't returned yet.
How to fix this?
I found that I had not set the CKReference field: owningContact as Queryable. The way I found out is printing error like this: 
if let ckerror = error as? CKError {
print(ckerror.userInfo)
print(ckerror.errorUserInfo)
self.aErrorHandler.handleCkError(ckerror: ckerror)
}
As soon as I did that it started working, Since I was in a for loop it was timing out on previous fetch I think.

how to collect sitecore information for anonymous

I need to get sitecore information that collected from anonymous users to give him availability to export it or opt out - [GDPR]
any idea about contact ID for anonymous !
The way of doing it is dependent on the sitecore version.
Sitcore 9 you can use right to be forgotten
Sitecore 8+ you have to implement the feature from scratch.
Regarding Anonymous user - If the user is really anonymous, then you done need to worry about the GDPR (my view). But sometimes we map user email and sensitive personal info to anonymous user by using forms or WFFM. You can use email address of that user to query xDB (Contact Identifiers) to get the contact and contactID. Then reset informations.
Also: please note that based on WFFFM save action config, anonymous user will store in Core DB and Contact List.
To forget a user, you can use the following code. It will execute the ExecuteRightToBeForgotten function on the contact and scrub their data.
Forget User
public bool ForgetUser()
{
var id = _contactIdentificationRepository.GetContactId();
if (id == null)
{
return false;
}
var contactReference = new IdentifiedContactReference(id.Source, id.Identifier);
using (var client = _contactIdentificationRepository.CreateContext())
{
var contact = client.Get(contactReference, new ContactExpandOptions());
if (contact != null)
{
client.ExecuteRightToBeForgotten(contact);
client.Submit();
}
}
return false;
}
Fake up some data
public void FakeUserInfo()
{
var contactReference = _contactIdentificationRepository.GetContactReference();
using (var client = SitecoreXConnectClientConfiguration.GetClient())
{
// we can have 1 to many facets
// PersonalInformation.DefaultFacetKey
// EmailAddressList.DefaultFacetKey
// Avatar.DefaultFacetKey
// PhoneNumberList.DefaultFacetKey
// AddressList.DefaultFacetKey
// plus custom ones
var facets = new List<string> { PersonalInformation.DefaultFacetKey };
// get the contact
var contact = client.Get(contactReference, new ContactExpandOptions(facets.ToArray()));
// pull the facet from the contact (if it exists)
var facet = contact.GetFacet<PersonalInformation>(PersonalInformation.DefaultFacetKey);
// if it exists, change it, else make a new one
if (facet != null)
{
facet.FirstName = $"Myrtle-{DateTime.Now.Date.ToString(CultureInfo.InvariantCulture)}";
facet.LastName = $"McSitecore-{DateTime.Now.Date.ToString(CultureInfo.InvariantCulture)}";
// set the facet on the client connection
client.SetFacet(contact, PersonalInformation.DefaultFacetKey, facet);
}
else
{
// make a new one
var personalInfoFacet = new PersonalInformation()
{
FirstName = "Myrtle",
LastName = "McSitecore"
};
// set the facet on the client connection
client.SetFacet(contact, PersonalInformation.DefaultFacetKey, personalInfoFacet);
}
if (contact != null)
{
// submit the changes to xConnect
client.Submit();
// reset the contact
_contactIdentificationRepository.Manager.RemoveFromSession(Analytics.Tracker.Current.Contact.ContactId);
Analytics.Tracker.Current.Session.Contact = _contactIdentificationRepository.Manager.LoadContact(Analytics.Tracker.Current.Contact.ContactId);
}
}
}
ContactIdentificationRepository
using System.Linq;
using Sitecore.Analytics;
using Sitecore.Analytics.Model;
using Sitecore.Analytics.Tracking;
using Sitecore.Configuration;
using Sitecore.XConnect;
using Sitecore.XConnect.Client.Configuration;
namespace Sitecore.Foundation.Accounts.Repositories
{
public class ContactIdentificationRepository
{
private readonly ContactManager contactManager;
public ContactManager Manager => contactManager;
public ContactIdentificationRepository()
{
contactManager = Factory.CreateObject("tracking/contactManager", true) as ContactManager;
}
public IdentifiedContactReference GetContactReference()
{
// get the contact id from the current contact
var id = GetContactId();
// if the contact is new or has no identifiers
var anon = Tracker.Current.Contact.IsNew || Tracker.Current.Contact.Identifiers.Count == 0;
// if the user is anon, get the xD.Tracker identifier, else get the one we found
return anon
? new IdentifiedContactReference(Sitecore.Analytics.XConnect.DataAccess.Constants.IdentifierSource, Tracker.Current.Contact.ContactId.ToString("N"))
: new IdentifiedContactReference(id.Source, id.Identifier);
}
public Analytics.Model.Entities.ContactIdentifier GetContactId()
{
if (Tracker.Current?.Contact == null)
{
return null;
}
if (Tracker.Current.Contact.IsNew)
{
// write the contact to xConnect so we can work with it
this.SaveContact();
}
return Tracker.Current.Contact.Identifiers.FirstOrDefault();
}
public void SaveContact()
{
// we need the contract to be saved to xConnect. It is only in session now
Tracker.Current.Contact.ContactSaveMode = ContactSaveMode.AlwaysSave;
this.contactManager.SaveContactToCollectionDb(Tracker.Current.Contact);
}
public IXdbContext CreateContext()
{
return SitecoreXConnectClientConfiguration.GetClient();
}
}
}

Registering and resolving dependencies in a loop

I have the following hardcoded setup in an application using MVVM:
var ips = configFile.Read();
for (string ip in ips)
{
var tcpClient = new TcpClient(ip);
var stream = (Stream) tcpClient.GetStream();
var service = new Service(stream);
var connectionViewModel = new ConnectionViewModel(service);
var extendedViewModel = new ExtendedViewModel(connectionViewModel);
}
A number of IP addresses are read from a file, and each address results in a ViewModel being created that displays stuff from the IP.
What is the best approach if I want to let DryIoc handle this? All new objects are unique foreach loop.
Possible option is using Func to pass the ip:
var c = new Container();
c.Register<ExtendedViewModel>();
c.Register<ConnectionViewModel>();
c.Register<Service>();
c.Register<TcpClient>();
foreach (var ip in ips) {
var getVM = c.Resolve<Func<string, ExtendedViewModel>>();
var vm = getVM(ip);
// use vm
}
Update:
For Stream, add the following registration without changing the resolution part:
c.Register<Stream>(Made.Of(
_ => ServiceInfo.Of<TcpClient>(),
tcpClient => (Stream)tcpClient.GetStream()));
Made.Of plays nicely with Func and other wrappers resolutions, and can be in the middle of object graph. That's why it is preferable over RegisterDelegate.

How to get last location on Glass reliably?

Method getLastLocation() from LocationManager often return null and it's quite tricky to select best provider. The documentation says:
Warning: Do not use the LocationManager.getBestProvider() method or the constants GPS_PROVIDER or NETWORK_PROVIDER to listen for location updates. Glass uses a dynamic set of providers and listening only to a single provider may cause your application to miss location updates.
How to get best last location?
Because Glass uses dynamic set of providers, you need to get location from all of them and select the location with best accuracy:
public static Location getLastLocation(Context context) {
LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.NO_REQUIREMENT);
List<String> providers = manager.getProviders(criteria, true);
List<Location> locations = new ArrayList<Location>();
for (String provider : providers) {
Location location = manager.getLastKnownLocation(provider);
if (location != null && location.getAccuracy()!=0.0) {
locations.add(location);
}
}
Collections.sort(locations, new Comparator<Location>() {
#Override
public int compare(Location location, Location location2) {
return (int) (location.getAccuracy() - location2.getAccuracy());
}
});
if (locations.size() > 0) {
return locations.get(0);
}
return null;
}
Destil's answer above correctly handles the case where at least one provider returns a valid location for getLastKnownLocation().
However, I've also seen Glass return null for getLastKnownLocation() for all providers (in XE16 in particular).
In this case, your only option is to register a LocationListener and wait for a new location update.
For example, in context of getting a location when creating a new Activity, it would look like the following:
public class MyActivity extends Activity implements LocationListener {
...
LocationManager mLocationManager;
Location mLastKnownLocation;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Activity setup
...
// Use Destil's answer to get last known location, using all providers
mLastKnownLocation = getLastLocation(this);
if (mLastKnownLocation != null) {
// Do something with location
doSomethingWithLocation(mLastKnownLocation);
} else {
// All providers returned null - start a LocationListener to force a refresh of location
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
List<String> providers = mLocationManager.getProviders(true);
for (Iterator<String> i = providers.iterator(); i.hasNext(); ) {
mLocationManager.requestLocationUpdates(i.next(), 0, 0, this);
}
}
...
}
...
}
You'll then need to handle the LocationListener callbacks:
#Override
public void onLocationChanged(Location location) {
if (mLastKnownLocation == null) {
// At least one location should be available now
// Use Destil's answer to get last known location again, using all providers
mLastKnownLocation = getLastLocation(this);
if (mLastKnownLocation == null) {
// This shouldn't happen if LocationManager is saving locations correctly, but if it does, use the location that was just passed in
mLastKnownLocation = location;
}
// Stop listening for updates
mLocationManager.removeUpdates(this);
// Do something with location
doSomethingWithLocation(mLastKnownLocation);
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
#Override
public void onProviderEnabled(String provider) {}
#Override
public void onProviderDisabled(String provider) {}
It can be a little tricky to change to the asynchronous model to avoid blocking the UI thread while waiting for the update, and it may require moving some of your app logic around.
The code in the answer should be adjusted to handle an accuracy of "0.0" which represents "NO Accuracy" known!
Here is an alternative that includes this adjustment
public static Location getLastLocation(Context context) {
Location result = null;
LocationManager locationManager;
Criteria locationCriteria;
List<String> providers;
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
locationCriteria = new Criteria();
locationCriteria.setAccuracy(Criteria.NO_REQUIREMENT);
providers = locationManager.getProviders(locationCriteria, true);
// Note that providers = locatoinManager.getAllProviders(); is not used because the
// list might contain disabled providers or providers that are not allowed to be called.
//Note that getAccuracy can return 0, indicating that there is no known accuracy.
for (String provider : providers) {
Location location = locationManager.getLastKnownLocation(provider);
if (result == null) {
result = location;
}
else if (result.getAccuracy() == 0.0) {
if (location.getAccuracy() != 0.0) {
result = location;
break;
} else {
if (result.getAccuracy() > location.getAccuracy()) {
result = location;
}
}
}
}
return result;
}