Profile Photo Rabbit Hole

13 Nov 2022

down the rabbit hole, children’s book illustration - stable diffusion

I’m on day 60 of #100Days , and have just wasted most of an evening’s coding time going down a rabit hole I didn’t need to. The app for this challenge is called “FriendFace” and is pretty straightforward: download a heap of JSON which is an array of users. Show it in a list that can be clicked through to see the details of that user.

I did that, and instead of moving onto the next project, decided I’d like to show a profile picture of each person. There’s no data for that, so I’ll use a fake photo. I use the Stable Diffusion AI for many of the pictures in this blog, so I assumed there would be an API for grabbing a fake profile pic from somewhere. It turns out there are several, but they are paid services.

Never mind, I know unsplash is a great source of free images, and they definitely have a portrait category. I’ll get them from there. Instead of using a REST api, I just wanted to grab them from a URL. After a bit of googling around, it turns out you can just use the url https://source.unsplash.com/collection/9948714?372 and change the number after the question mark to get a different portrait.

Now all I need is a way to generate a number 1-1000 such that it’s always the same for each individual user. That’s basically a hash, and Swift has a hashValue property on it’s string. I tried user.name.hashValue % 1_000, but kept getting different pictures. Pulled up a playground and tried some print() debugging, and the hashValue was different each run. It would be the same if I hashed the same string twice in a row in code, but not between runs. A few googles later, I’ve learned this is deliberate.

So I need to write my own hash. This is not cryptography - I can just sum all the ascii values of the characters in the string and modulo them. I’m a bit hazy on how to get every character in a Swift string because of the unicode thing, but rmaddy has this answer :

extension String {
    var fourBitHash: Int {
        return self.utf8.reduce(0) { $0 + Int($1) } % 16
    }
}

let colorIndex = "John R Smith".fourBitHash
print(colorIndex)

Perfect. I adapt this into my code as:

func simpleHash(_ string: String) -> Int {
    string.utf8.reduce(0) { $0 + Int($1) } % 1000
}

And we’re in business, but hang on, I’m still getting a different picture by repeatedly going into the view detail for the same user. I try pasting in the url a few times, and sure enough, unsplash are serving up random portraits for the same URL….

Okay, so I need a different source for pics. More googling, and I discover RandomUser.me They only have 100 profiles for men, and another 100 for women - but this app is only for me, and I’ll probably get bored after I’ve clicked on three or four so that will be fine. I throw that into my AsyncImage and we’re in business.

AsyncImage(
    url: URL(string: "https://randomuser.me/api/portraits/women/\(simpleHash(user.name)).jpg"),
    scale: 3
) { image in image
        .resizable()
        .scaledToFit()
} placeholder: {
    ProgressView()
}

I’d prefer to show the correct gender (I’m not being deliberately binary, I’d just like to double the number of photos) , and there is no gender field in my data. So, I guess I’ll need an API that guesses gender based on a name input based on a giant lookup table or an AI model.

More googling , and I find stomgren’s genderize api. An input of:

[https://api.genderize.io/?name=ian](https://api.genderize.io/?name=ian)

returns:

{
  "count": 306685,
  "gender": "male",
  "name": "ian",
  "probability": 1
}

or

https://api.genderize.io/?name=kim

{
  "count": 83361,
  "gender": "female",
  "name": "kim",
  "probability": 0.7
}

Perfect. So, I can just add this to my code to pull up the picture, or, you know what, I’m procrastinating and it’s time for bed.